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