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