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