libroken: Build on windows
[openafs.git] / src / WINNT / afsclass / c_svr.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <winsock2.h>
11 #include <ws2tcpip.h>
12
13 extern "C" {
14 #include <afsconfig.h>
15 #include <afs/param.h>
16 #include <afs/stds.h>
17 #include <roken.h>
18 }
19
20 #include <WINNT/afsclass.h>
21 #include "internal.h"
22
23
24 /*
25  * DEFINITIONS ________________________________________________________________
26  *
27  */
28
29          // Each SERVER object maintains a list of aggregates and a list
30          // of services; those lists have hashtables placed across their
31          // names (and, for aggregates, their IDs) for faster lookup.
32          // The default table size in a HASHLIST is 1000 elements--that's
33          // too large for a list of aggregates or services on a server,
34          // as it's enough to handle up to 30,000 objects before the table
35          // would need to resize iteself (see the documentation in
36          // hashlist.cpp for info). Instead, we choose a more reasonable
37          // default table size.
38          //
39 #define cKEYAGGREGATENAME_TABLESIZE  100
40 #define cKEYAGGREGATEID_TABLESIZE    100
41
42 #define cKEYSERVICENAME_TABLESIZE    50
43
44
45 /*
46  * VARIABLES __________________________________________________________________
47  *
48  */
49
50
51 /*
52  * PROTOTYPES _________________________________________________________________
53  *
54  */
55
56
57 /*
58  * ROUTINES ___________________________________________________________________
59  *
60  */
61
62
63 SERVER::SERVER (LPCELL lpCellParent, LPTSTR pszName)
64 {
65    m_lpiCell = lpCellParent->GetIdentifier();
66
67    lstrcpy (m_szName, pszName);
68
69    m_lpCellBOS = NULL;
70    m_hCellBOS = NULL;
71    m_hBOS = NULL;
72    m_cReqBOS = 0;
73
74    m_hCellVOS = NULL;
75    m_hVOS = NULL;
76    m_cReqVOS = 0;
77
78    m_fCanGetAggregates = TRUE;
79    m_fAggregatesOutOfDate = TRUE;
80    m_lAggregates = New (HASHLIST);
81    m_lAggregates->SetCriticalSection (AfsClass_GetCriticalSection());
82    m_lkAggregateName = m_lAggregates->CreateKey ("Aggregate Name", SERVER::KeyAggregateName_Compare, SERVER::KeyAggregateName_HashObject, SERVER::KeyAggregateName_HashData, cKEYAGGREGATENAME_TABLESIZE);
83    m_lkAggregateID = m_lAggregates->CreateKey ("Aggregate ID", SERVER::KeyAggregateID_Compare, SERVER::KeyAggregateID_HashObject, SERVER::KeyAggregateID_HashData, cKEYAGGREGATEID_TABLESIZE);
84
85    m_fCanGetServices = TRUE;
86    m_fServicesOutOfDate = TRUE;
87    m_lServices = New (HASHLIST);
88    m_lServices->SetCriticalSection (AfsClass_GetCriticalSection());
89    m_lkServiceName = m_lServices->CreateKey ("Service Name", SERVER::KeyServiceName_Compare, SERVER::KeyServiceName_HashObject, SERVER::KeyServiceName_HashData, cKEYSERVICENAME_TABLESIZE);
90
91    m_wGhost = 0;
92    m_lpiThis = NULL;
93    m_fMonitor = TRUE;
94    m_fDelete = FALSE;
95    m_lastStatus = 0;
96
97    m_fVLDBOutOfDate = FALSE; /* FIXME: added because it was missing */
98    m_fStatusOutOfDate = TRUE;
99    memset (&m_ss, 0x00, sizeof(SERVERSTATUS));
100 }
101
102
103 SERVER::~SERVER (void)
104 {
105    if (!m_fMonitor)
106       {
107       LPCELL lpCell;
108       if ((lpCell = m_lpiCell->OpenCell()) != NULL)
109          {
110          (lpCell->m_nServersUnmonitored)--;
111          lpCell->Close();
112          }
113       }
114
115    if (m_lpiThis)
116       m_lpiThis->m_cRef --;
117
118    FreeAll();
119    Delete (m_lAggregates);
120    Delete (m_lServices);
121 }
122
123
124 void SERVER::FreeAll (void)
125 {
126    if (m_cReqBOS)
127       {
128       m_cReqBOS = 1;
129       CloseBosObject();
130       }
131    if (m_cReqVOS)
132       {
133       m_cReqVOS = 1;
134       CloseVosObject();
135       }
136
137    FreeAggregates();
138    FreeServices();
139 }
140
141
142 void SERVER::FreeAggregates (void)
143 {
144    for (LPENUM pEnum = m_lAggregates->FindLast(); pEnum; pEnum = pEnum->FindPrevious())
145       {
146       LPAGGREGATE lpAggregate = (LPAGGREGATE)(pEnum->GetObject());
147       m_lAggregates->Remove (lpAggregate);
148       Delete (lpAggregate);
149       }
150 }
151
152
153 void SERVER::FreeServices (void)
154 {
155    for (LPENUM pEnum = m_lServices->FindLast(); pEnum; pEnum = pEnum->FindPrevious())
156       {
157       LPSERVICE lpService = (LPSERVICE)(pEnum->GetObject());
158       m_lServices->Remove (lpService);
159       Delete (lpService);
160       }
161 }
162
163
164 void SERVER::SendDeleteNotifications (void)
165 {
166    LPENUM pEnum;
167    for (pEnum = m_lAggregates->FindFirst(); pEnum; pEnum = pEnum->FindNext())
168       {
169       LPAGGREGATE lpAggregate = (LPAGGREGATE)(pEnum->GetObject());
170       lpAggregate->SendDeleteNotifications ();
171       }
172
173    for (pEnum = m_lServices->FindFirst(); pEnum; pEnum = pEnum->FindNext())
174       {
175       LPSERVICE lpService = (LPSERVICE)(pEnum->GetObject());
176       lpService->SendDeleteNotifications();
177       }
178
179    NOTIFYCALLBACK::SendNotificationToAll (evtDestroy, GetIdentifier());
180 }
181
182
183 void SERVER::Close (void)
184 {
185    AfsClass_Leave();
186 }
187
188
189 LPIDENT SERVER::GetIdentifier (void)
190 {
191    if (m_lpiThis == NULL)
192       {
193       if ((m_lpiThis = IDENT::FindIdent (this)) == NULL)
194          m_lpiThis = New2 (IDENT,(this));
195       m_lpiThis->m_cRef ++;
196       }
197
198    return m_lpiThis;
199 }
200
201
202 PVOID SERVER::OpenBosObject (PVOID *phCell, ULONG *pStatus)
203 {
204    if (!m_hBOS)
205       {
206       if ((m_lpCellBOS = m_lpiCell->OpenCell (pStatus)) != NULL)
207          {
208          if ((m_hCellBOS = m_lpCellBOS->GetCellObject (pStatus)) != NULL)
209             {
210             TCHAR szCell[ cchNAME ];
211             m_lpiCell->GetCellName (szCell);
212
213             WORKERPACKET wp;
214             wp.wpBosServerOpen.hCell = m_hCellBOS;
215             wp.wpBosServerOpen.pszServer = m_szName;
216             if (Worker_DoTask (wtaskBosServerOpen, &wp, pStatus))
217                m_hBOS = wp.wpBosServerOpen.hServer;
218             }
219
220          if (!m_hBOS)
221             {
222             m_lpCellBOS->Close();
223             m_lpCellBOS = NULL;
224             m_hCellBOS = NULL;
225             }
226          }
227       }
228
229    if (m_hBOS)
230       {
231       if (phCell)
232          *phCell = m_hCellBOS;
233       ++m_cReqBOS;
234       }
235    return m_hBOS;
236 }
237
238
239 BOOL SERVER::CloseBosObject (ULONG *pStatus)
240 {
241    BOOL rc = TRUE;
242
243    if ((m_cReqBOS > 0) && ((--m_cReqBOS) == 0))
244       {
245       if (m_hBOS != NULL)
246          {
247          WORKERPACKET wp;
248          wp.wpBosServerClose.hServer = m_hBOS;
249          if (!Worker_DoTask (wtaskBosServerClose, &wp, pStatus))
250             rc = FALSE;
251          m_hBOS = NULL;
252          }
253       if (m_lpCellBOS != NULL)
254          {
255          m_lpCellBOS->Close();
256          m_lpCellBOS = NULL;
257          }
258       }
259
260    return rc;
261 }
262
263
264 PVOID SERVER::OpenVosObject (PVOID *phCell, ULONG *pStatus)
265 {
266    if (!m_hCellVOS)
267       {
268       LPCELL lpCell;
269       if ((lpCell = m_lpiCell->OpenCell (pStatus)) != NULL)
270          {
271          m_hCellVOS = lpCell->GetCellObject (pStatus);
272          lpCell->Close();
273          }
274       }
275
276    if (m_hCellVOS && !m_hVOS)
277       {
278       TCHAR szCell[ cchNAME ];
279       m_lpiCell->GetCellName (szCell);
280
281       WORKERPACKET wp;
282       wp.wpVosServerOpen.hCell = m_hCellVOS;
283       wp.wpVosServerOpen.pszServer = m_szName;
284       if (Worker_DoTask (wtaskVosServerOpen, &wp, pStatus))
285          m_hVOS = wp.wpVosServerOpen.hServer;
286       }
287
288    if (m_hVOS)
289       {
290       if (phCell)
291          *phCell = m_hCellVOS;
292       ++m_cReqVOS;
293       }
294    return m_hVOS;
295 }
296
297
298 BOOL SERVER::CloseVosObject (ULONG *pStatus)
299 {
300    BOOL rc = TRUE;
301
302    if ((m_cReqVOS > 0) && ((--m_cReqVOS) == 0))
303       {
304       if (m_hVOS != NULL)
305          {
306          WORKERPACKET wp;
307          wp.wpVosServerClose.hServer = m_hVOS;
308          if (!Worker_DoTask (wtaskVosServerClose, &wp, pStatus))
309             rc = FALSE;
310          }
311
312       m_hVOS = NULL;
313       m_hCellVOS = NULL;
314       }
315
316    return rc;
317 }
318
319
320 void SERVER::Invalidate (void)
321 {
322    if (!m_fAggregatesOutOfDate || !m_fServicesOutOfDate || !m_fStatusOutOfDate)
323       {
324       if (m_wGhost & GHOST_HAS_SERVER_ENTRY)
325          {
326          m_fAggregatesOutOfDate = TRUE;
327          m_fServicesOutOfDate = TRUE;
328          m_fStatusOutOfDate = TRUE;
329          }
330
331       NOTIFYCALLBACK::SendNotificationToAll (evtInvalidate, GetIdentifier());
332       }
333 }
334
335
336 void SERVER::InvalidateStatus (void)
337 {
338    if (!m_fStatusOutOfDate)
339       {
340       if (m_wGhost & GHOST_HAS_SERVER_ENTRY)
341          {
342          m_fStatusOutOfDate = TRUE;
343          }
344
345       NOTIFYCALLBACK::SendNotificationToAll (evtInvalidate, GetIdentifier());
346       }
347 }
348
349
350 void SERVER::InvalidateServices (void)
351 {
352    if (!m_fServicesOutOfDate)
353       {
354       if (m_wGhost & GHOST_HAS_SERVER_ENTRY)
355          {
356          m_fServicesOutOfDate = TRUE;
357          }
358
359       NOTIFYCALLBACK::SendNotificationToAll (evtInvalidate, GetIdentifier());
360       }
361 }
362
363
364 BOOL SERVER::RefreshAggregates (BOOL fNotify, ULONG *pStatus)
365 {
366    BOOL rc = TRUE;
367    DWORD status = 0;
368
369    if (m_fAggregatesOutOfDate)
370       {
371       m_fAggregatesOutOfDate = FALSE;
372
373       if (fIsMonitored())
374          {
375          if (fNotify)
376             NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAggregatesBegin, GetIdentifier());
377
378          // First thing is to forget about what aggregates we think we have
379          // now.
380          //
381          LPENUM pEnum;
382          for (pEnum = m_lAggregates->FindLast(); pEnum; pEnum = pEnum->FindPrevious())
383             {
384             LPAGGREGATE lpAggregate = (LPAGGREGATE)(pEnum->GetObject());
385             lpAggregate->SendDeleteNotifications();
386             m_lAggregates->Remove (lpAggregate);
387             Delete (lpAggregate);
388             }
389
390          // Next, the harder part: look through the server to find a list
391          // of aggregates.
392          //
393          PVOID hCell;
394          PVOID hVOS;
395          if ((hVOS = OpenVosObject (&hCell, &status)) == NULL)
396             rc = FALSE;
397          else
398             {
399             WORKERPACKET wpBegin;
400             wpBegin.wpVosPartitionGetBegin.hCell = hCell;
401             wpBegin.wpVosPartitionGetBegin.hServer = hVOS;
402
403             if (!Worker_DoTask (wtaskVosPartitionGetBegin, &wpBegin, &status))
404                rc = FALSE;
405             else
406                {
407                for (;;)
408                   {
409                   WORKERPACKET wpNext;
410                   wpNext.wpVosPartitionGetNext.hEnum = wpBegin.wpVosPartitionGetBegin.hEnum;
411                   if (!Worker_DoTask (wtaskVosPartitionGetNext, &wpNext, &status))
412                      {
413                      if (status == ADMITERATORDONE)
414                         status = 0;
415                      else
416                         rc = FALSE;
417                      break;
418                      }
419
420                   vos_partitionEntry_p pData = &wpNext.wpVosPartitionGetNext.Data;
421
422                   LPTSTR pszName = AnsiToString (pData->name);
423                   LPTSTR pszDevice = AnsiToString (pData->deviceName);
424
425                   LPAGGREGATE lpAggregate = New2 (AGGREGATE,(this, pszName, pszDevice));
426
427                   lpAggregate->m_as.dwID = lpAggregate->GetID();
428
429                   FreeString (pszDevice, pData->deviceName);
430                   FreeString (pszName,   pData->name);
431
432                   lpAggregate->m_wGhost |= GHOST_HAS_SERVER_ENTRY;
433                   lpAggregate->m_as.ckStorageTotal = pData->totalSpace;
434                   lpAggregate->m_as.ckStorageFree = pData->totalFreeSpace;
435                   m_lAggregates->Add (lpAggregate);
436
437                   NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpAggregate->GetIdentifier());
438                   }
439
440                WORKERPACKET wpDone;
441                wpDone.wpVosPartitionGetDone.hEnum = wpBegin.wpVosPartitionGetBegin.hEnum;
442                Worker_DoTask (wtaskVosPartitionGetDone, &wpDone);
443                }
444
445             CloseVosObject();
446             }
447
448          if (fNotify)
449             NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAggregatesEnd, GetIdentifier(), ((rc) ? 0 : status));
450          }
451       }
452
453    if (pStatus && !rc)
454       *pStatus = status;
455    return TRUE;
456 }
457
458
459 BOOL SERVER::RefreshServices (BOOL fNotify, ULONG *pStatus)
460 {
461    BOOL rc = TRUE;
462    DWORD status = 0;
463
464    if (m_fServicesOutOfDate)
465       {
466       m_fServicesOutOfDate = FALSE;
467
468       if (fIsMonitored())
469          {
470          if (fNotify)
471             NOTIFYCALLBACK::SendNotificationToAll (evtRefreshServicesBegin, GetIdentifier());
472
473          // First thing is to forget about what services we think we have now.
474          //
475          LPENUM pEnum;
476          for (pEnum = m_lServices->FindLast(); pEnum; pEnum = pEnum->FindPrevious())
477             {
478             LPSERVICE lpService = (LPSERVICE)(pEnum->GetObject());
479             lpService->SendDeleteNotifications();
480             m_lServices->Remove (lpService);
481             Delete (lpService);
482             }
483
484          // Next, the harder part: look through the server to find a list
485          // of services.
486          //
487          PVOID hCell;
488          PVOID hBOS;
489          if ((hBOS = OpenBosObject (&hCell, &status)) == NULL)
490             rc = FALSE;
491          else
492             {
493             WORKERPACKET wpBegin;
494             wpBegin.wpBosProcessNameGetBegin.hServer = hBOS;
495             if (!Worker_DoTask (wtaskBosProcessNameGetBegin, &wpBegin, &status))
496                rc = FALSE;
497             else
498                {
499                LPSERVICE lpService = New2 (SERVICE,(this, TEXT("BOS")));
500                m_lServices->Add (lpService);
501                NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpService->GetIdentifier());
502
503                for (;;)
504                   {
505                   TCHAR szServiceName[ cchNAME ];
506
507                   WORKERPACKET wpNext;
508                   wpNext.wpBosProcessNameGetNext.hEnum = wpBegin.wpBosProcessNameGetBegin.hEnum;
509                   wpNext.wpBosProcessNameGetNext.pszService = szServiceName;
510
511                   if (!Worker_DoTask (wtaskBosProcessNameGetNext, &wpNext, &status))
512                      {
513                      if (status == ADMITERATORDONE)
514                         status = 0;
515                      else
516                         rc = FALSE;
517                      break;
518                      }
519
520                   lpService = New2 (SERVICE,(this, wpNext.wpBosProcessNameGetNext.pszService));
521                   m_lServices->Add (lpService);
522                   NOTIFYCALLBACK::SendNotificationToAll (evtCreate, lpService->GetIdentifier());
523                   }
524
525                WORKERPACKET wpDone;
526                wpDone.wpBosProcessNameGetDone.hEnum = wpBegin.wpBosProcessNameGetBegin.hEnum;
527                Worker_DoTask (wtaskBosProcessNameGetDone, &wpDone);
528                }
529
530             CloseBosObject();
531             }
532
533          if (fNotify)
534             NOTIFYCALLBACK::SendNotificationToAll (evtRefreshServicesEnd, GetIdentifier(), ((rc) ? 0 : status));
535          }
536       }
537
538    if (pStatus && !rc)
539       *pStatus = status;
540    return TRUE;
541 }
542
543
544 BOOL SERVER::RefreshStatus (BOOL fNotify, ULONG *pStatus)
545 {
546    BOOL rc = TRUE;
547    DWORD status = 0;
548
549    if (m_fStatusOutOfDate)
550       {
551       m_fStatusOutOfDate = FALSE;
552
553       if (fNotify)
554          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusBegin, GetIdentifier());
555
556       LPCELL lpCell;
557       if ((lpCell = OpenCell (&status)) == NULL)
558          rc = FALSE;
559       else
560          {
561          PVOID hCell;
562          if ((hCell = lpCell->GetCellObject (&status)) == NULL)
563             rc = FALSE;
564          else
565             {
566             WORKERPACKET wp;
567             wp.wpClientAFSServerGet.hCell = hCell;
568             wp.wpClientAFSServerGet.pszServer = m_szName;
569
570             if (!Worker_DoTask (wtaskClientAFSServerGet, &wp, &status))
571                rc = FALSE;
572             else
573                {
574                m_ss.nAddresses = 0;
575
576                for (size_t iAddr = 0; iAddr < AFS_MAX_SERVER_ADDRESS; ++iAddr)
577                   {
578                   if (wp.wpClientAFSServerGet.Entry.serverAddress[ iAddr ] == 0)
579                      continue;
580                   AfsClass_IntToAddress (&m_ss.aAddresses[ m_ss.nAddresses++ ], wp.wpClientAFSServerGet.Entry.serverAddress[ iAddr ]);
581                   }
582
583                lpCell->m_lServers->Update (this); // That update affected a hashlistkey
584                }
585             }
586          lpCell->Close();
587          }
588
589       if (fNotify)
590          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusEnd, GetIdentifier(), ((rc) ? 0 : status));
591       }
592
593    if (pStatus && !rc)
594       *pStatus = status;
595    return TRUE;
596 }
597
598
599 void SERVER::GetName (LPTSTR pszName)
600 {
601    SERVER::ShortenName (pszName, m_szName);
602 }
603
604
605 void SERVER::GetLongName (LPTSTR pszName)
606 {
607    lstrcpy (pszName, m_szName);
608 }
609
610
611 LPCELL SERVER::OpenCell (ULONG *pStatus)
612 {
613    return m_lpiCell->OpenCell (pStatus);
614 }
615
616 BOOL SERVER::GetStatus (LPSERVERSTATUS lpss, BOOL fNotify, ULONG *pStatus)
617 {
618    BOOL rc = TRUE;
619
620    if (m_fMonitor)
621       rc = RefreshStatus (fNotify, pStatus);
622
623    memcpy (lpss, &m_ss, sizeof(SERVERSTATUS));
624    return rc;
625 }
626
627
628 short SERVER::GetGhostStatus (void)
629 {
630    return m_wGhost;
631 }
632
633
634 PVOID SERVER::GetUserParam (void)
635 {
636    return GetIdentifier()->GetUserParam();
637 }
638
639
640 void SERVER::SetUserParam (PVOID pUserNew)
641 {
642    GetIdentifier()->SetUserParam (pUserNew);
643 }
644
645
646 void SERVER::ShortenName (LPTSTR pszTarget, LPTSTR pszSource, BOOL fForce)
647 {
648    lstrcpy (pszTarget, pszSource);
649
650    if (fForce || !fLongServerNames)
651       {
652       // If the name is really an IP address, don't shorten it.
653       //
654       BOOL fIsIPAddress = TRUE;
655       for (LPTSTR pch = pszTarget; *pch && fIsIPAddress; ++pch)
656          {
657          if (!isdigit(*pch) && !(*pch == TEXT('.')))
658             fIsIPAddress = FALSE;
659          }
660
661       if (!fIsIPAddress)
662          {
663          if ((pszTarget = (LPTSTR)lstrchr (pszTarget, TEXT('.'))) != NULL)
664             *pszTarget = TEXT('\0');
665          }
666       }
667 }
668
669
670 BOOL SERVER::fIsMonitored (void)
671 {
672    return m_fMonitor;
673 }
674
675
676 BOOL SERVER::SetMonitor (BOOL fShouldMonitor, ULONG *pStatus)
677 {
678    BOOL rc = TRUE;
679    ULONG status = 0;
680
681    if (m_fMonitor != fShouldMonitor)
682       {
683       LPCELL lpCell;
684       if ((lpCell = m_lpiCell->OpenCell (&status)) == NULL)
685          rc = FALSE;
686       else
687          {
688          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusBegin, GetIdentifier());
689
690          if ((m_fMonitor = fShouldMonitor) == FALSE)
691             {
692             FreeAll();
693             (lpCell->m_nServersUnmonitored)++;
694             }
695          else // (fMonitor == TRUE)
696             {
697             (lpCell->m_nServersUnmonitored)--;
698             Invalidate();
699             rc = RefreshAll (&status);
700             }
701
702          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusEnd, GetIdentifier(), m_lastStatus);
703          lpCell->Close();
704          }
705       }
706
707    if (!rc && pStatus)
708       *pStatus = status;
709    return rc;
710 }
711
712
713 /*
714  * REFRESHALL __________________________________________________________________
715  *
716  * If a server is down, or doesn't have an VOS process running, it could
717  * take some time before we time out trying to talk to the server.  During
718  * the course of a refresh, the first timeout-and-fail that we will hit is
719  * our call to wtaskVosPartitionGetBegin; since this call is very quick if
720  * it's going to be successful, we can safely perform this call once up-front
721  * to test to see if the server is listening at all.  That test is performed
722  * on a separate thread, so that in the event the request times out, we can
723  * simply discard the thread and let it terminate on its own.
724  *
725  */
726
727 typedef struct
728    {
729    BOOL fInUse;
730    BOOL fCanceled;
731    LPSERVER lpServer;
732    PVOID hCell;
733    } REFRESHSECTION, *LPREFRESHSECTION;
734
735 static REFRESHSECTION    *aRefSec = NULL;
736 static size_t             cRefSec = 0;
737 static LPCRITICAL_SECTION pcsRefSec = NULL;
738
739 void AfsClass_InitRefreshSections (void)
740 {
741    if (pcsRefSec == NULL)
742       {
743       pcsRefSec = New (CRITICAL_SECTION);
744       InitializeCriticalSection (pcsRefSec);
745       }
746 }
747
748
749 void AfsClass_SkipRefresh (int idSection)
750 {
751    AfsClass_InitRefreshSections();
752    EnterCriticalSection (pcsRefSec);
753
754    if (aRefSec && (idSection < (int)cRefSec))
755       {
756       if (aRefSec[ idSection ].fInUse)
757          {
758          aRefSec[ idSection ].fCanceled = TRUE;
759          }
760       }
761
762    LeaveCriticalSection (pcsRefSec);
763 }
764
765
766 DWORD WINAPI SERVER::CanTalkToServer_ThreadProc (PVOID lp)
767 {
768    int idSection = PtrToInt(lp);
769
770    // Until we post a notification saying that we've entered
771    // a section, we don't need to worry about the aRefSec[] entry
772    // being invalid. Once that post is made, the user can skip
773    // the section at any time--so we'll have to check frequently,
774    // always under the pcsRefSec critical section.
775    //
776    NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllSectionStart, NULL, NULL, NULL, NULL, idSection, 0);
777
778    BOOL fAggregatesOK = FALSE;
779    BOOL fServicesOK = FALSE;
780    BOOL fContinue = TRUE;
781
782    // Try to get the BOS object for this server.  Remember, there's
783    // a catch here: we can only assume that the aRefSec[idSection].lpServer
784    // pointer is valid so long as we're within the pcsRefSec critical
785    // section! (if we're in that critsec, we can verify that no one
786    // has canceled the operation--and if no one has, there is a thread
787    // hanging around which holds the library's critsec, which ensures
788    // the lpServer pointer won't have been freed.)
789    // 
790    PVOID hCell;
791    PVOID hBOS = NULL;
792    PVOID hVOS = NULL;
793
794    TCHAR szServer[ cchNAME ];
795
796    EnterCriticalSection (pcsRefSec);
797    if ( ((!aRefSec[ idSection ].fInUse) || (aRefSec[ idSection ].fCanceled)) )
798       fContinue = FALSE;
799    else
800       {
801       hCell = aRefSec[ idSection ].hCell;
802       aRefSec[ idSection ].lpServer->GetLongName (szServer);
803       }
804    LeaveCriticalSection (pcsRefSec);
805
806    if (fContinue)
807       {
808       WORKERPACKET wp;
809       wp.wpBosServerOpen.hCell = hCell;
810       wp.wpBosServerOpen.pszServer = szServer;
811
812       ULONG status;
813       fContinue = Worker_DoTask (wtaskBosServerOpen, &wp, &status);
814
815       EnterCriticalSection (pcsRefSec);
816       if ( ((!aRefSec[ idSection ].fInUse) || (aRefSec[ idSection ].fCanceled)) )
817          fContinue = FALSE;
818       else if (!fContinue)
819          aRefSec[ idSection ].lpServer->m_lastStatus = status;
820       else
821          hBOS = wp.wpBosServerOpen.hServer;
822       LeaveCriticalSection (pcsRefSec);
823       }
824
825    if (fContinue)
826       {
827       WORKERPACKET wpBegin;
828       wpBegin.wpBosProcessNameGetBegin.hServer = hBOS;
829
830       ULONG status;
831       fContinue = Worker_DoTask (wtaskBosProcessNameGetBegin, &wpBegin, &status);
832
833       EnterCriticalSection (pcsRefSec);
834       if ( ((!aRefSec[ idSection ].fInUse) || (aRefSec[ idSection ].fCanceled)) )
835          fContinue = FALSE;
836       else if (fContinue)
837          aRefSec[ idSection ].lpServer->m_lastStatus = status;
838       LeaveCriticalSection (pcsRefSec);
839
840       if (fContinue)
841          {
842          WORKERPACKET wpDone;
843          wpDone.wpBosProcessNameGetDone.hEnum = wpBegin.wpBosProcessNameGetBegin.hEnum;
844          Worker_DoTask (wtaskBosProcessNameGetDone, &wpDone);
845
846          // We can talk to BOS!
847          fServicesOK = TRUE;
848          }
849       }
850
851    // If we couldn't talk to BOS, it's a sure bet the server is down--
852    // and regardless, if BOS isn't around, VOS isn't either.  So
853    // we may not even have to test that.
854    //
855    if (fContinue)
856       {
857       WORKERPACKET wp;
858       wp.wpVosServerOpen.hCell = hCell;
859       wp.wpVosServerOpen.pszServer = szServer;
860
861       ULONG status;
862       fContinue = Worker_DoTask (wtaskVosServerOpen, &wp, &status);
863
864       EnterCriticalSection (pcsRefSec);
865       if ( ((!aRefSec[ idSection ].fInUse) || (aRefSec[ idSection ].fCanceled)) )
866          fContinue = FALSE;
867       else if (!fContinue)
868          aRefSec[ idSection ].lpServer->m_lastStatus = status;
869       else
870          hVOS = wp.wpVosServerOpen.hServer;
871       LeaveCriticalSection (pcsRefSec);
872       }
873
874    if (fContinue)
875       {
876       WORKERPACKET wpBegin;
877       wpBegin.wpVosPartitionGetBegin.hCell = hCell;
878       wpBegin.wpVosPartitionGetBegin.hServer = hVOS;
879
880       ULONG status;
881       fContinue = Worker_DoTask (wtaskVosPartitionGetBegin, &wpBegin, &status);
882
883       EnterCriticalSection (pcsRefSec);
884       if ( ((!aRefSec[ idSection ].fInUse) || (aRefSec[ idSection ].fCanceled)) )
885          fContinue = FALSE;
886       else if (fContinue)
887          aRefSec[ idSection ].lpServer->m_lastStatus = status;
888       LeaveCriticalSection (pcsRefSec);
889
890       if (fContinue)
891          {
892          WORKERPACKET wpDone;
893          wpDone.wpVosPartitionGetDone.hEnum = wpBegin.wpVosPartitionGetBegin.hEnum;
894          Worker_DoTask (wtaskVosPartitionGetDone, &wpDone);
895
896          // We can talk to VOS!
897          fAggregatesOK = TRUE;
898          }
899       }
900
901    // Close the VOS and BOS objects we obtained.
902    //
903    if (hBOS)
904       {
905       WORKERPACKET wp;
906       wp.wpBosServerClose.hServer = hBOS;
907       Worker_DoTask (wtaskBosServerClose, &wp);
908       }
909    if (hVOS)
910       {
911       WORKERPACKET wp;
912       wp.wpVosServerClose.hServer = hVOS;
913       Worker_DoTask (wtaskVosServerClose, &wp);
914       }
915
916    // Return our entry in the RefSec array back to the pool.
917    // If the request was never canceled, there is another
918    // thread waiting to hear our results--update the server
919    // entry specified by RefSec before leaving.
920    //
921    NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllSectionEnd, NULL, NULL, NULL, NULL, idSection, 0);
922
923    EnterCriticalSection (pcsRefSec);
924    if ( (aRefSec[ idSection ].fInUse) && (!aRefSec[ idSection ].fCanceled) )
925       {
926       aRefSec[ idSection ].lpServer->m_fCanGetAggregates = fAggregatesOK;
927       aRefSec[ idSection ].lpServer->m_fCanGetServices = fServicesOK;
928       }
929    aRefSec[ idSection ].fInUse = FALSE;
930    LeaveCriticalSection (pcsRefSec);
931    return 1;
932 }
933
934
935 BOOL SERVER::CanTalkToServer (ULONG *pStatus)
936 {
937    // Ensure the server exists in the cell at all--
938    // this call just updates the server's IP addresses
939    // etc (information it gets from the database servers)
940    // and doesn't require talking to the server itself.
941    //
942    if (!RefreshStatus (FALSE, pStatus))
943       return FALSE;
944
945    // Find a new refsec array element to use...
946    //
947    AfsClass_InitRefreshSections();
948    EnterCriticalSection (pcsRefSec);
949
950    int idSection;
951    for (idSection = 0; idSection < (int)cRefSec; ++idSection)
952       {
953       if (!aRefSec[ idSection ].fInUse)
954          break;
955       }
956    if (idSection == (int)cRefSec)
957       {
958       if (!REALLOC (aRefSec, cRefSec, 1+idSection, 4))
959          {
960          if (pStatus)
961             *pStatus = GetLastError();
962          LeaveCriticalSection (pcsRefSec);
963          return FALSE;
964          }
965       }
966    aRefSec[ idSection ].fInUse = TRUE;
967    aRefSec[ idSection ].fCanceled = FALSE;
968    aRefSec[ idSection ].lpServer = this;
969    aRefSec[ idSection ].hCell = NULL;
970
971    LPCELL lpCell;
972    if ((lpCell = OpenCell()) != NULL)
973       {
974       aRefSec[ idSection ].hCell = lpCell->GetCellObject();
975       lpCell->Close();
976       }
977
978    LeaveCriticalSection (pcsRefSec);
979
980    // Until we find out differently, assume that we won't be
981    // able to query VOS or BOS on this server.
982    //
983    m_fCanGetAggregates = FALSE;
984    m_fCanGetServices = FALSE;
985    m_lastStatus = 0;
986
987    // Fork a separate thread, on which to quickly try to talk
988    // to the server.
989    //
990    DWORD dwThreadID;
991    HANDLE hThread;
992    if ((hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)CanTalkToServer_ThreadProc, IntToPtr(idSection), 0, &dwThreadID)) == NULL)
993       {
994       EnterCriticalSection (pcsRefSec);
995       aRefSec[ idSection ].fInUse = FALSE;
996       LeaveCriticalSection (pcsRefSec);
997       if (pStatus)
998          *pStatus = GetLastError();
999       return FALSE;
1000       }
1001    SetThreadPriority (hThread, THREAD_PRIORITY_BELOW_NORMAL);
1002
1003    // Wait for that thread to terminate, or for our
1004    // newly-allocated RefSec entry to be marked Canceled.
1005    //
1006    DWORD dw;
1007    for (dw = STILL_ACTIVE; dw == STILL_ACTIVE; )
1008       {
1009       EnterCriticalSection (pcsRefSec);
1010
1011       GetExitCodeThread (hThread, &dw);
1012       if (dw == STILL_ACTIVE)
1013          {
1014          if ( (aRefSec[ idSection ].fInUse) &&
1015               (aRefSec[ idSection ].lpServer == this) &&
1016               (aRefSec[ idSection ].fCanceled) )
1017             {
1018             if (m_lastStatus == 0)
1019                m_lastStatus = ERROR_CANCELLED;
1020             dw = 0;
1021             }
1022          }
1023
1024       LeaveCriticalSection (pcsRefSec);
1025
1026       if (dw == STILL_ACTIVE)
1027          Sleep(100);    // wait another brief instant
1028       }
1029
1030    // dw == 0 : user canceled operation (thread is still running!)
1031    // dw == 1 : thread completed successfully, and set fCanTalkTo* flags.
1032    //
1033    // Note that the thread will clear aRefSec[idSection].fInUse when it
1034    // terminates (so, if dw!=-1, it has already done so).
1035    //
1036    if (pStatus)
1037       *pStatus = m_lastStatus;
1038    return (dw == 0) ? FALSE : TRUE;
1039 }
1040
1041
1042 BOOL SERVER::RefreshAll (ULONG *pStatus, double dInit, double dFactor)
1043 {
1044    BOOL rc = TRUE;
1045    ULONG status = 0;
1046
1047    if (m_fAggregatesOutOfDate || m_fServicesOutOfDate)
1048       {
1049       if ((++cRefreshAllReq) == 1)
1050          {
1051          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllBegin, GetIdentifier(), 0);
1052          }
1053
1054       double perAGGREGATES = 65.0; // % of time spent finding aggs & sets
1055       double perSERVICES   = 25.0; // % of time spent finding services
1056       double perVLDB       = 10.0; // % of time spent finding VLDB info
1057
1058       if (cRefreshAllReq >= 2) // being called as part of a cell-wide op?
1059          {
1060          perAGGREGATES = 80.0; // % of time spent finding aggs & sets
1061          perSERVICES   = 20.0; // % of time spent finding services
1062          perVLDB       = 0.0;  // we won't query VLDB stuff ourself.
1063          }
1064
1065       NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusBegin, GetIdentifier());
1066
1067       if (!CanTalkToServer (&status))  // Determines fCanGetAggregates, fCanGetServices
1068          {
1069          if (m_fMonitor)
1070             SetMonitor (FALSE);
1071          rc = FALSE;
1072          }
1073       else
1074          {
1075          if (!m_fCanGetAggregates)
1076             {
1077             FreeAggregates();
1078             m_fAggregatesOutOfDate = FALSE;
1079             }
1080          else
1081             {
1082             size_t nAggregates = 0;
1083             size_t iAggregate = 0;
1084             HENUM hEnum;
1085             LPAGGREGATE lpAggregate;
1086             for (lpAggregate = AggregateFindFirst (&hEnum); lpAggregate; lpAggregate = AggregateFindNext (&hEnum))
1087                {
1088                ++nAggregates;
1089                lpAggregate->Close();
1090                }
1091                
1092             if (nAggregates)
1093                {
1094                for (lpAggregate = AggregateFindFirst (&hEnum); lpAggregate; lpAggregate = AggregateFindNext (&hEnum))
1095                   {
1096                   ULONG perComplete = (ULONG)( ((double)perAGGREGATES / 100.0) * ((double)iAggregate * 100.0 / nAggregates) );
1097                   NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllUpdate, lpAggregate->GetIdentifier(), NULL, NULL, NULL, (ULONG)( 100.0 * dInit + dFactor * (double)perComplete ), 0);
1098
1099                   lpAggregate->RefreshFilesets (TRUE);
1100                   lpAggregate->Close();
1101
1102                   ++iAggregate;
1103                   }
1104                }
1105             }
1106
1107          if (!m_fCanGetServices)
1108             {
1109             FreeServices();
1110             m_fServicesOutOfDate = FALSE;
1111             }
1112          else
1113             {
1114             size_t nServices = 0;
1115             size_t iService = 0;
1116             HENUM hEnum;
1117             LPSERVICE lpService;
1118             for (lpService = ServiceFindFirst (&hEnum); lpService; lpService = ServiceFindNext (&hEnum))
1119                {
1120                ++nServices;
1121                lpService->Close();
1122                }
1123
1124             if (nServices)
1125                {
1126                for (lpService = ServiceFindFirst (&hEnum); lpService; lpService = ServiceFindNext (&hEnum))
1127                   {
1128                   ULONG perComplete = (ULONG)( (double)perAGGREGATES + ((double)perSERVICES / 100.0) * ((double)iService * 100.0 / nServices) );
1129                   NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllUpdate, lpService->GetIdentifier(), NULL, NULL, NULL, (ULONG)( 100.0 * dInit + dFactor * (double)perComplete ), 0);
1130
1131                   lpService->RefreshStatus (TRUE);
1132                   lpService->Close();
1133
1134                   ++iService;
1135                   }
1136                }
1137             }
1138
1139          if (cRefreshAllReq == 1) // not being called as part of a cell-wide op?
1140             {
1141             LPCELL lpCell;
1142             if ((lpCell = OpenCell()) != NULL)
1143                {
1144                lpCell->RefreshVLDB (GetIdentifier(), TRUE, NULL);
1145                lpCell->Close();
1146                }
1147             }
1148          }
1149
1150       NOTIFYCALLBACK::SendNotificationToAll (evtRefreshStatusEnd, GetIdentifier(), m_lastStatus);
1151
1152       if ((--cRefreshAllReq) == 0)
1153          {
1154          NOTIFYCALLBACK::SendNotificationToAll (evtRefreshAllEnd, GetIdentifier(), NULL, NULL, NULL, 100, m_lastStatus);
1155          }
1156       }
1157
1158    if (rc && m_lastStatus)
1159       rc = FALSE;
1160    if (!rc && pStatus)
1161       *pStatus = status;
1162    return rc;
1163 }
1164
1165
1166 /*
1167  * AGGREGATES _________________________________________________________________
1168  *
1169  */
1170
1171 LPAGGREGATE SERVER::OpenAggregate (LPTSTR pszName, ULONG *pStatus)
1172 {
1173    if (!RefreshAggregates (TRUE, pStatus))
1174       return NULL;
1175
1176    LPAGGREGATE lpAggregate;
1177    if ((lpAggregate = (LPAGGREGATE)(m_lkAggregateName->GetFirstObject (pszName))) != NULL)
1178       AfsClass_Enter();
1179
1180    return lpAggregate;
1181 }
1182
1183
1184 LPAGGREGATE SERVER::OpenAggregate (ULONG dwID, ULONG *pStatus)
1185 {
1186    if (!RefreshAggregates (TRUE, pStatus))
1187       return NULL;
1188
1189    LPAGGREGATE lpAggregate;
1190    if ((lpAggregate = (LPAGGREGATE)(m_lkAggregateID->GetFirstObject (&dwID))) != NULL)
1191       AfsClass_Enter();
1192
1193    return lpAggregate;
1194 }
1195
1196
1197 LPAGGREGATE SERVER::AggregateFindFirst (HENUM *phEnum, BOOL fNotify, ULONG *pStatus)
1198 {
1199    return AggregateFindFirst (phEnum, NULL, fNotify, pStatus);
1200 }
1201
1202
1203 LPAGGREGATE SERVER::AggregateFindFirst (HENUM *phEnum, LPIDENT lpiFind, BOOL fNotify, ULONG *pStatus)
1204 {
1205    LPAGGREGATE lpAggregate = NULL;
1206
1207    if (!RefreshAggregates (fNotify, pStatus))
1208       return NULL;
1209
1210    if (lpiFind != NULL)
1211       {
1212       lpAggregate = lpiFind->OpenAggregate();
1213       *phEnum = NULL;
1214       }
1215    else if ((*phEnum = m_lAggregates->FindFirst()) != NULL)
1216       {
1217       lpAggregate = (LPAGGREGATE)( (*phEnum)->GetObject() );
1218       AfsClass_Enter();
1219       }
1220
1221    if (!lpAggregate && pStatus)
1222       *pStatus = ERROR_FILE_NOT_FOUND;
1223    return lpAggregate;
1224 }
1225
1226
1227 LPAGGREGATE SERVER::AggregateFindNext (HENUM *phEnum)
1228 {
1229    LPAGGREGATE lpAggregate = NULL;
1230
1231    if (*phEnum)
1232       {
1233       if ((*phEnum = (*phEnum)->FindNext()) != NULL)
1234          {
1235          lpAggregate = (LPAGGREGATE)( (*phEnum)->GetObject() );
1236          AfsClass_Enter();
1237          }
1238       }
1239
1240    return lpAggregate;
1241 }
1242
1243
1244 void SERVER::AggregateFindClose (HENUM *phEnum)
1245 {
1246    if (*phEnum)
1247       {
1248       Delete (*phEnum);
1249       *phEnum = NULL;
1250       }
1251 }
1252
1253
1254 /*
1255  * SERVICES ___________________________________________________________________
1256  *
1257  */
1258
1259 LPSERVICE SERVER::OpenService (LPTSTR pszName, ULONG *pStatus)
1260 {
1261    if (!RefreshServices (TRUE, pStatus))
1262       return NULL;
1263
1264    LPSERVICE lpService;
1265    if ((lpService = (LPSERVICE)(m_lkServiceName->GetFirstObject (pszName))) != NULL)
1266       AfsClass_Enter();
1267
1268    return lpService;
1269 }
1270
1271
1272 LPSERVICE SERVER::ServiceFindFirst (HENUM *phEnum, BOOL fNotify, ULONG *pStatus)
1273 {
1274    return ServiceFindFirst (phEnum, NULL, fNotify, pStatus);
1275 }
1276
1277
1278 LPSERVICE SERVER::ServiceFindFirst (HENUM *phEnum, LPIDENT lpiFind, BOOL fNotify, ULONG *pStatus)
1279 {
1280    LPSERVICE lpService = NULL;
1281
1282    if (!RefreshServices (fNotify, pStatus))
1283       return NULL;
1284
1285    if (lpiFind != NULL)
1286       {
1287       lpService = lpiFind->OpenService();
1288       *phEnum = NULL;
1289       }
1290    else if ((*phEnum = m_lServices->FindFirst()) != NULL)
1291       {
1292       lpService = (LPSERVICE)( (*phEnum)->GetObject() );
1293       AfsClass_Enter();
1294       }
1295
1296    if (!lpService && pStatus)
1297       *pStatus = ERROR_FILE_NOT_FOUND;
1298    return lpService;
1299 }
1300
1301
1302 LPSERVICE SERVER::ServiceFindNext (HENUM *phEnum)
1303 {
1304    LPSERVICE lpService = NULL;
1305
1306    if (*phEnum)
1307       {
1308       if ((*phEnum = (*phEnum)->FindNext()) != NULL)
1309          {
1310          lpService = (LPSERVICE)( (*phEnum)->GetObject() );
1311          AfsClass_Enter();
1312          }
1313       }
1314
1315    return lpService;
1316 }
1317
1318
1319 void SERVER::ServiceFindClose (HENUM *phEnum)
1320 {
1321    if (*phEnum)
1322       {
1323       Delete (*phEnum);
1324       *phEnum = NULL;
1325       }
1326 }
1327
1328
1329 /*
1330  * HASH KEYS __________________________________________________________________
1331  *
1332  */
1333
1334 BOOL CALLBACK SERVER::KeyAggregateName_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData)
1335 {
1336    return !lstrcmp (((LPAGGREGATE)pObject)->m_szName, (LPTSTR)pData);
1337 }
1338
1339 HASHVALUE CALLBACK SERVER::KeyAggregateName_HashObject (LPHASHLISTKEY pKey, PVOID pObject)
1340 {
1341    return SERVER::KeyAggregateName_HashData (pKey, ((LPAGGREGATE)pObject)->m_szName);
1342 }
1343
1344 HASHVALUE CALLBACK SERVER::KeyAggregateName_HashData (LPHASHLISTKEY pKey, PVOID pData)
1345 {
1346    return HashString ((LPTSTR)pData);
1347 }
1348
1349
1350 BOOL CALLBACK SERVER::KeyAggregateID_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData)
1351 {
1352    return (((LPAGGREGATE)pObject)->m_as.dwID == *(ULONG*)pData);
1353 }
1354
1355 HASHVALUE CALLBACK SERVER::KeyAggregateID_HashObject (LPHASHLISTKEY pKey, PVOID pObject)
1356 {
1357    return SERVER::KeyAggregateID_HashData (pKey, &((LPAGGREGATE)pObject)->m_as.dwID);
1358 }
1359
1360 HASHVALUE CALLBACK SERVER::KeyAggregateID_HashData (LPHASHLISTKEY pKey, PVOID pData)
1361 {
1362    return (HASHVALUE)*(ULONG*)pData;
1363 }
1364
1365
1366 BOOL CALLBACK SERVER::KeyServiceName_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData)
1367 {
1368    return !lstrcmp (((LPSERVICE)pObject)->m_szName, (LPTSTR)pData);
1369 }
1370
1371 HASHVALUE CALLBACK SERVER::KeyServiceName_HashObject (LPHASHLISTKEY pKey, PVOID pObject)
1372 {
1373    return SERVER::KeyServiceName_HashData (pKey, ((LPSERVICE)pObject)->m_szName);
1374 }
1375
1376 HASHVALUE CALLBACK SERVER::KeyServiceName_HashData (LPHASHLISTKEY pKey, PVOID pData)
1377 {
1378    return HashString ((LPTSTR)pData);
1379 }
1380