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