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