8e513310d45b806a09299c2d134340fe2b73951a
[openafs.git] / src / WINNT / afsadmsvr / TaAfsAdmSvrGeneral.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <winsock2.h>
11 #include <ws2tcpip.h>
12
13 extern "C" {
14 #include <afsconfig.h>
15 #include <afs/param.h>
16 #include <afs/stds.h>
17 #include <roken.h>
18 }
19
20 #include "TaAfsAdmSvrInternal.h"
21
22 extern "C" {
23 #include <afs/afs_AdminErrors.h>
24 } // extern "C"
25
26
27 /*
28  * VARIABLES __________________________________________________________________
29  *
30  */
31
32 #define cminREQ_CLIENT_PING   (csecAFSADMSVR_CLIENT_PING * 2L / 60L)
33
34 #define cREALLOC_CLIENTS          1
35 #define cREALLOC_OPERATIONS      16
36
37 #define cminAUTO_SHUTDOWN         2 // stop if idle for more than 2min
38 #define cminAUTO_SHUTDOWN_SLEEP   1 // test for stop every minute
39
40 typedef struct
41    {
42    TCHAR szName[ cchSTRING ];
43    SOCKADDR_IN ipAddress;
44    DWORD timeLastPing;
45    } CLIENTINFO, *LPCLIENTINFO;
46
47 typedef struct
48    {
49    BOOL fInUse;
50    UINT_PTR idClient;
51    LPASACTION pAction;
52    DWORD dwTickStart;
53    } OPERATION, *LPOPERATION;
54
55 static struct
56    {
57    BOOL fOperational;
58    LPCRITICAL_SECTION pcsAfsAdmSvr;
59
60    LPCLIENTINFO *aClients;
61    size_t cClients;
62    size_t cClientsAllocated;
63
64    LPNOTIFYCALLBACK pNotify;
65
66    BOOL fAutoShutdown;
67    HANDLE hThreadShutdown;
68    DWORD timeLastIdleStart;
69
70    OPERATION *aOperations;
71    size_t cOperations;
72    size_t cOperationsAllocated;
73    DWORD idActionLast;
74
75    DWORD dwScopeMin;
76    } l;
77
78
79 /*
80  * PROTOTYPES _________________________________________________________________
81  *
82  */
83
84 void AfsAdmSvr_TestShutdown (void);
85 DWORD WINAPI AfsAdmSvr_AutoShutdownThread (LPVOID lp);
86
87
88 /*
89  * SYNCHRONIZATION ____________________________________________________________
90  *
91  */
92
93 void AfsAdmSvr_Enter (void)
94 {
95    if (!l.pcsAfsAdmSvr)
96       {
97       l.pcsAfsAdmSvr = New (CRITICAL_SECTION);
98       InitializeCriticalSection (l.pcsAfsAdmSvr);
99       }
100    EnterCriticalSection (l.pcsAfsAdmSvr);
101 }
102
103 void AfsAdmSvr_Leave (void)
104 {
105    LeaveCriticalSection (l.pcsAfsAdmSvr);
106 }
107
108
109 /*
110  * CLIENT INFORMATION _________________________________________________________
111  *
112  */
113
114 BOOL AfsAdmSvr_fIsValidClient (UINT_PTR idClient)
115 {
116    BOOL rc = FALSE;
117    AfsAdmSvr_Enter();
118
119    for (size_t iClient = 0; !rc && iClient < l.cClientsAllocated; ++iClient)
120       {
121       if (idClient == (UINT_PTR)l.aClients[ iClient ])
122          {
123          if (l.aClients[ iClient ]->timeLastPing + cminREQ_CLIENT_PING > AfsAdmSvr_GetCurrentTime())
124             rc = TRUE;
125          }
126       }
127
128    AfsAdmSvr_Leave();
129    return rc;
130 }
131
132
133 BOOL AfsAdmSvr_AttachClient (LPCTSTR pszName, PVOID *pidClient, ULONG *pStatus)
134 {
135    AfsAdmSvr_Enter();
136    size_t iClient;
137    for (iClient = 0; iClient < l.cClientsAllocated; ++iClient)
138       {
139       if (!l.aClients[ iClient ])
140          break;
141       }
142    if (!REALLOC (l.aClients, l.cClientsAllocated, 1+iClient, cREALLOC_CLIENTS))
143       {
144       *pidClient = NULL;
145       return FALSE;
146       }
147    if ((l.aClients[ iClient ] = New (CLIENTINFO)) == NULL)
148       {
149       *pidClient = NULL;
150       return FALSE;
151       }
152    memset (l.aClients[ iClient ], 0x00, sizeof(CLIENTINFO));
153    lstrcpy (l.aClients[ iClient ]->szName, pszName);
154    l.aClients[ iClient ]->timeLastPing = AfsAdmSvr_GetCurrentTime();
155    l.cClients ++;
156
157    if (!AfsAdmSvr_ResolveName (&l.aClients[ iClient ]->ipAddress, l.aClients[ iClient ]->szName))
158       memset (&l.aClients[ iClient ]->ipAddress, 0x00, sizeof(SOCKADDR_IN));
159
160    *pidClient = (PVOID)(l.aClients[ iClient ]);
161    AfsAdmSvr_Leave();
162    return TRUE;
163 }
164
165
166 void AfsAdmSvr_DetachClient (UINT_PTR idClient)
167 {
168    AfsAdmSvr_Enter();
169    size_t iClient;
170    for (iClient = 0; iClient < l.cClientsAllocated; ++iClient)
171       {
172       if (idClient == (UINT_PTR)(l.aClients[ iClient ]))
173          break;
174       }
175    if (iClient < l.cClientsAllocated)
176       {
177       Delete (l.aClients[ iClient ]);
178       l.aClients[ iClient ] = NULL;
179       l.cClients --;
180       }
181    AfsAdmSvr_TestShutdown();
182    AfsAdmSvr_Leave();
183 }
184
185
186 LPCTSTR AfsAdmSvr_GetClientName (UINT_PTR idClient)
187 {
188    static TCHAR szName[ cchSTRING ];
189    LPCTSTR pszName = NULL;
190    AfsAdmSvr_Enter();
191
192    for (size_t iClient = 0; !pszName && iClient < l.cClientsAllocated; ++iClient)
193       {
194       if (idClient == (UINT_PTR)(l.aClients[ iClient ]))
195          {
196          lstrcpy (szName, l.aClients[ iClient ]->szName);
197          pszName = szName;
198          }
199       }
200
201    AfsAdmSvr_Leave();
202    return pszName;
203 }
204
205
206 LPSOCKADDR_IN AfsAdmSvr_GetClientAddress (UINT_PTR idClient)
207 {
208    static SOCKADDR_IN ipAddress;
209    LPSOCKADDR_IN pAddress = NULL;
210    AfsAdmSvr_Enter();
211
212    for (size_t iClient = 0; !pAddress && iClient < l.cClientsAllocated; ++iClient)
213       {
214       if (idClient == (UINT_PTR)(l.aClients[ iClient ]))
215          {
216          memcpy (&ipAddress, &l.aClients[ iClient ]->ipAddress, sizeof(SOCKADDR_IN));
217          pAddress = &ipAddress;
218          }
219       }
220
221    AfsAdmSvr_Leave();
222    return pAddress;
223 }
224
225
226 void AfsAdmSvr_PingClient (UINT_PTR idClient)
227 {
228    AfsAdmSvr_Enter();
229
230    for (size_t iClient = 0; iClient < l.cClientsAllocated; ++iClient)
231       {
232       if (idClient == (UINT_PTR)(l.aClients[ iClient ]))
233          {
234          l.aClients[ iClient ]->timeLastPing = AfsAdmSvr_GetCurrentTime();
235          }
236       }
237
238    AfsAdmSvr_Leave();
239 }
240
241
242 DWORD AfsAdmSvr_GetCurrentTime (void) // returns counter in ~minute increments
243 {
244    static WORD wMonthish = 0;  // One "Monthish" is 49.7 days
245    static WORD wTickLast = 0;
246    DWORD dwTick = GetTickCount();
247    WORD wTickNow = HIWORD(dwTick);
248    if (wTickNow < wTickLast)  // wrapped over a Monthish?
249       ++wMonthish;
250    wTickLast = wTickNow;
251    return MAKELONG(wTickNow,wMonthish);
252 }
253
254
255 /*
256  * STARTUP/SHUTDOWN ___________________________________________________________
257  *
258  */
259
260 void AfsAdmSvr_Startup (void)
261 {
262    l.pNotify = New2 (NOTIFYCALLBACK,(AfsAdmSvr_NotifyCallback, 0));
263    l.fOperational = FALSE;
264
265    ULONG status;
266    if (AfsClass_Initialize (&status))
267       l.fOperational = TRUE;
268    else
269       {
270       Print (dlERROR, TEXT("Could not initialize AfsClass (fatal error 0x%08lX)"), status);
271       Print (dlERROR, TEXT("Remaining active to tell potential clients about the problem"));
272       }
273
274    if (!l.hThreadShutdown)
275       {
276       DWORD dwThreadID;
277       l.hThreadShutdown = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)AfsAdmSvr_AutoShutdownThread, (LPVOID)0, 0, &dwThreadID);
278       }
279 }
280
281
282 void AfsAdmSvr_Shutdown (void)
283 {
284    Delete (l.pNotify);
285    l.pNotify = NULL;
286    l.fOperational = FALSE;
287 }
288
289
290 /*
291  * GENERAL ____________________________________________________________________
292  *
293  */
294
295 BOOL FALSE_ (ULONG status, ULONG *pStatus, size_t iOp)
296 {
297    if (pStatus)
298       *pStatus = status;
299    if (iOp != (size_t)-2)
300       AfsAdmSvr_EndOperation (iOp);
301    return FALSE;
302 }
303
304 BOOL Leave_FALSE_ (ULONG status, ULONG *pStatus, size_t iOp)
305 {
306    AfsAdmSvr_Leave();
307    if (pStatus)
308       *pStatus = status;
309    if (iOp != (size_t)-2)
310       AfsAdmSvr_EndOperation (iOp);
311    return FALSE;
312 }
313
314 PVOID NULL_ (ULONG status, ULONG *pStatus, size_t iOp)
315 {
316    if (pStatus)
317       *pStatus = status;
318    if (iOp != (size_t)-2)
319       AfsAdmSvr_EndOperation (iOp);
320    return NULL;
321 }
322
323 PVOID Leave_NULL_ (ULONG status, ULONG *pStatus, size_t iOp)
324 {
325    AfsAdmSvr_Leave();
326    if (pStatus)
327       *pStatus = status;
328    if (iOp != (size_t)-2)
329       AfsAdmSvr_EndOperation (iOp);
330    return NULL;
331 }
332
333 BOOL TRUE_ (ULONG *pStatus, size_t iOp)
334 {
335    if (pStatus)
336       *pStatus = 0;
337    if (iOp != (size_t)-2)
338       AfsAdmSvr_EndOperation (iOp);
339    return TRUE;
340 }
341
342 BOOL Leave_TRUE_ (ULONG *pStatus, size_t iOp)
343 {
344    AfsAdmSvr_Leave();
345    if (pStatus)
346       *pStatus = 0;
347    if (iOp != (size_t)-2)
348       AfsAdmSvr_EndOperation (iOp);
349    return TRUE;
350 }
351
352 IDENTTYPE GetAsidType (ASID idObject)
353 {
354    IDENTTYPE iType;
355    try {
356       iType = ((LPIDENT)idObject)->GetType();
357    } catch(...) {
358       iType = itUNUSED;
359    }
360    return iType;
361 }
362
363
364 BOOL AfsAdmSvr_ResolveName (LPSOCKADDR_IN pAddress, LPTSTR pszName)
365 {
366    if ((pszName[0] >= TEXT('0')) && (pszName[0] <= TEXT('9')))
367       {
368       int ipAddress;
369       if ((ipAddress = inet_addr (pszName)) == INADDR_NONE)
370          return FALSE;
371
372       memset (pAddress, 0x00, sizeof(SOCKADDR_IN));
373       pAddress->sin_family = AF_INET;
374       pAddress->sin_addr.s_addr = ipAddress;
375
376       HOSTENT *pEntry;
377       if ((pEntry = gethostbyaddr ((char*)&ipAddress, sizeof(ipAddress), AF_INET)) != NULL)
378          lstrcpy (pszName, pEntry->h_name);
379       }
380    else // (!isdigit(szServer[0]))
381       {
382       HOSTENT *pEntry;
383       if ((pEntry = gethostbyname (pszName)) == NULL)
384          return FALSE;
385
386       memset (pAddress, 0x00, sizeof(SOCKADDR_IN));
387       pAddress->sin_family = AF_INET;
388       pAddress->sin_addr.s_addr = *(int *)pEntry->h_addr;
389
390       lstrcpy (pszName, pEntry->h_name);
391       }
392
393    return TRUE;
394 }
395
396
397 /*
398  * AUTO-SHUTDOWN ______________________________________________________________
399  *
400  */
401
402 DWORD WINAPI AfsAdmSvr_AutoShutdownThread (LPVOID lp)
403 {
404    for (;;)
405       {
406       AfsAdmSvr_Enter();
407
408       BOOL fShutdown = l.fAutoShutdown;
409
410       // If there are any clients connected, forcably disconnect any
411       // that haven't pinged us for too long
412       //
413       for (size_t iClient = 0; iClient < l.cClientsAllocated; ++iClient)
414          {
415          if (!l.aClients[ iClient ])
416             continue;
417          if (l.aClients[ iClient ]->timeLastPing + cminREQ_CLIENT_PING <= AfsAdmSvr_GetCurrentTime())
418             {
419             Print (dlCONNECTION, "Client 0x%08lX idle for too long; detaching", l.aClients[ iClient ]);
420             AfsAdmSvr_DetachClient ((UINT_PTR)l.aClients[ iClient ]);
421             }
422          }
423
424       // If any operations are in progress, we can't shutdown.
425       //
426       if (l.cOperations)
427          fShutdown = FALSE;
428
429       // If any clients are still connected, we can't shutdown.
430       //
431       if (l.cClients)
432          fShutdown = FALSE;
433
434       // If we haven't been idle long enough, we can't shutdown
435       //
436       if (!l.timeLastIdleStart)
437          fShutdown = FALSE;
438       else if (l.timeLastIdleStart + cminAUTO_SHUTDOWN > AfsAdmSvr_GetCurrentTime())
439          fShutdown = FALSE;
440
441       // That's it; can we stop now?
442       //
443       if (fShutdown)
444          {
445          Print ("Idle for too long; shutting down.");
446          RpcMgmtStopServerListening (NULL);
447          AfsAdmSvr_StopCallbackManagers();
448          }
449
450       AfsAdmSvr_Leave();
451
452       if (fShutdown)
453          break;
454
455       Sleep (cminAUTO_SHUTDOWN_SLEEP * 60L * 1000L);
456       }
457
458    return 0;
459 }
460
461
462
463 void AfsAdmSvr_EnableAutoShutdown (BOOL fEnable)
464 {
465    AfsAdmSvr_Enter();
466
467    l.fAutoShutdown = fEnable;
468
469    if (fEnable)
470       Print (dlDETAIL, TEXT("Auto-shutdown enabled, trigger = %lu minutes idle time"), cminAUTO_SHUTDOWN);
471    else
472       Print (dlDETAIL, TEXT("Auto-shutdown on idle disabled"));
473
474    AfsAdmSvr_Leave();
475 }
476
477
478 void AfsAdmSvr_TestShutdown (void)
479 {
480    if (!l.cOperations && !l.cClients)
481       {
482       l.timeLastIdleStart = AfsAdmSvr_GetCurrentTime();
483       }
484 }
485
486
487 size_t AfsAdmSvr_BeginOperation (UINT_PTR idClient, LPASACTION pAction)
488 {
489    AfsAdmSvr_Enter();
490
491    ++l.cOperations;
492
493    size_t iOp;
494    for (iOp = 0; iOp < l.cOperationsAllocated; ++iOp)
495       {
496       if (!l.aOperations[ iOp ].fInUse)
497          break;
498       }
499    if (!REALLOC (l.aOperations, l.cOperationsAllocated, 1+iOp, cREALLOC_OPERATIONS))
500       {
501       AfsAdmSvr_Leave();
502       return (size_t)(-1);
503       }
504
505    l.aOperations[ iOp ].idClient = idClient;
506    l.aOperations[ iOp ].pAction = NULL;
507    l.aOperations[ iOp ].fInUse = TRUE;
508
509    if (pAction)
510       {
511       l.aOperations[ iOp ].pAction = New (ASACTION);
512       memcpy (l.aOperations[ iOp ].pAction, pAction, sizeof(ASACTION));
513       l.aOperations[ iOp ].pAction->idAction = ++l.idActionLast;
514       l.aOperations[ iOp ].pAction->idClient = idClient;
515       l.aOperations[ iOp ].pAction->csecActive = 0;
516
517       TCHAR szDesc[256];
518       switch (l.aOperations[ iOp ].pAction->Action)
519          {
520          case ACTION_REFRESH:
521             wsprintf (szDesc, TEXT("Refresh (scope=0x%08lX)"), l.aOperations[ iOp ].pAction->u.Refresh.idScope);
522             break;
523          case ACTION_SCOUT:
524             wsprintf (szDesc, TEXT("Scout (scope=0x%08lX)"), l.aOperations[ iOp ].pAction->u.Refresh.idScope);
525             break;
526          case ACTION_USER_CHANGE:
527             wsprintf (szDesc, TEXT("ChangeUser (user=0x%08lX)"), l.aOperations[ iOp ].pAction->u.User_Change.idUser);
528             break;
529          case ACTION_USER_PW_CHANGE:
530             wsprintf (szDesc, TEXT("SetUserPassword (user=0x%08lX)"), l.aOperations[ iOp ].pAction->u.User_Pw_Change.idUser);
531             break;
532          case ACTION_USER_UNLOCK:
533             wsprintf (szDesc, TEXT("UnlockUser (user=0x%08lX)"), l.aOperations[ iOp ].pAction->u.User_Unlock.idUser);
534             break;
535          case ACTION_USER_CREATE:
536             wsprintf (szDesc, TEXT("CreateUser (user=%s)"), l.aOperations[ iOp ].pAction->u.User_Create.szUser);
537             break;
538          case ACTION_USER_DELETE:
539             wsprintf (szDesc, TEXT("CreateUser (user=0x%08lX)"), l.aOperations[ iOp ].pAction->u.User_Delete.idUser);
540             break;
541          case ACTION_GROUP_CHANGE:
542             wsprintf (szDesc, TEXT("ChangeGroup (group=0x%08lX)"), l.aOperations[ iOp ].pAction->u.Group_Change.idGroup);
543             break;
544          case ACTION_GROUP_MEMBER_ADD:
545             wsprintf (szDesc, TEXT("AddGroupMember (group=0x%08lX, user=0x%08lX)"), l.aOperations[ iOp ].pAction->u.Group_Member_Add.idGroup, l.aOperations[ iOp ].pAction->u.Group_Member_Add.idUser);
546             break;
547          case ACTION_GROUP_MEMBER_REMOVE:
548             wsprintf (szDesc, TEXT("RemoveGroupMember (group=0x%08lX, user=0x%08lX)"), l.aOperations[ iOp ].pAction->u.Group_Member_Remove.idGroup, l.aOperations[ iOp ].pAction->u.Group_Member_Remove.idUser);
549             break;
550          case ACTION_GROUP_RENAME:
551             wsprintf (szDesc, TEXT("RenameGroup (group=0x%08lX, new name=%s)"), l.aOperations[ iOp ].pAction->u.Group_Rename.idGroup, l.aOperations[ iOp ].pAction->u.Group_Rename.szNewName);
552             break;
553          case ACTION_GROUP_DELETE:
554             wsprintf (szDesc, TEXT("CreateGroup (group=0x%08lX)"), l.aOperations[ iOp ].pAction->u.Group_Delete.idGroup);
555             break;
556          case ACTION_CELL_CHANGE:
557             wsprintf (szDesc, TEXT("ChangeCell (cell=0x%08lX)"), l.aOperations[ iOp ].pAction->idCell);
558             break;
559          default:
560             wsprintf (szDesc, TEXT("Unknown Action (#%lu)"), l.aOperations[ iOp ].pAction->Action);
561             break;
562          }
563       Print (dlOPERATION, TEXT("Starting action 0x%08lX: %s"), l.aOperations[ iOp ].pAction->idAction, szDesc);
564
565       AfsAdmSvr_PostCallback (cbtACTION, FALSE, l.aOperations[ iOp ].pAction);
566       }
567
568    l.aOperations[ iOp ].dwTickStart = GetTickCount();
569    AfsAdmSvr_Leave();
570    return iOp;
571 }
572
573
574 void AfsAdmSvr_EndOperation (size_t iOp)
575 {
576    AfsAdmSvr_Enter();
577
578    if ((iOp != (size_t)-1) && (iOp < l.cOperationsAllocated) && (l.aOperations[ iOp ].fInUse))
579       {
580       if (l.aOperations[ iOp ].pAction)
581          {
582          Print (dlOPERATION, TEXT("Ending action 0x%08lX"), l.aOperations[ iOp ].pAction->idAction);
583          AfsAdmSvr_PostCallback (cbtACTION, TRUE, l.aOperations[ iOp ].pAction);
584          Delete (l.aOperations[ iOp ].pAction);
585          }
586       memset (&l.aOperations[ iOp ], 0x00, sizeof(l.aOperations[ iOp ]));
587       l.cOperations --;
588       }
589
590    AfsAdmSvr_TestShutdown();
591    AfsAdmSvr_Leave();
592 }
593
594
595 BOOL AfsAdmSvr_GetOperation (DWORD idAction, LPASACTION pAction)
596 {
597    AfsAdmSvr_Enter();
598
599    for (size_t iOp = 0; iOp < l.cOperationsAllocated; ++iOp)
600       {
601       if (!l.aOperations[ iOp ].fInUse)
602          continue;
603       if (!l.aOperations[ iOp ].pAction)
604          continue;
605       if (l.aOperations[ iOp ].pAction->idAction != idAction)
606          continue;
607
608       memcpy (pAction, l.aOperations[ iOp ].pAction, sizeof(ASACTION));
609       pAction->csecActive = (GetTickCount() - l.aOperations[ iOp ].dwTickStart) / 1000;
610       AfsAdmSvr_Leave();
611       return TRUE;
612       }
613
614    AfsAdmSvr_Leave();
615    return FALSE;
616 }
617
618
619 LPASACTIONLIST AfsAdmSvr_GetOperations (UINT_PTR idClientSearch, ASID idCellSearch)
620 {
621    LPASACTIONLIST pList = AfsAdmSvr_CreateActionList();
622    AfsAdmSvr_Enter();
623
624    for (WORD iOp = 0; iOp < l.cOperationsAllocated; ++iOp)
625       {
626       if (!l.aOperations[ iOp ].fInUse)
627          continue;
628       if (!l.aOperations[ iOp ].pAction)
629          continue;
630       if (idClientSearch && ((UINT_PTR)idClientSearch != l.aOperations[ iOp ].pAction->idClient))
631          continue;
632       if (idCellSearch && (idCellSearch != l.aOperations[ iOp ].pAction->idCell))
633          continue;
634
635       ASACTION Action;
636       memcpy (&Action, l.aOperations[ iOp ].pAction, sizeof(ASACTION));
637       Action.csecActive = (GetTickCount() - l.aOperations[ iOp ].dwTickStart) / 1000;
638       if (!AfsAdmSvr_AddToActionList (&pList, &Action))
639          {
640          AfsAdmSvr_FreeActionList (&pList);
641          break;
642          }
643       }
644
645    AfsAdmSvr_Leave();
646    return pList;
647 }
648
649
650 void AfsAdmSvr_Action_StartRefresh (ASID idScope)
651 {
652    switch (GetAsidType (idScope))
653       {
654       case itCELL:
655          AfsAdmSvr_MarkRefreshThread (idScope);
656          // fall through
657
658       case itSERVER:
659          ASACTION Action;
660          memset (&Action, 0x00, sizeof(Action));
661          Action.Action = ACTION_REFRESH;
662          Action.idCell = (ASID)( ((LPIDENT)idScope)->GetCell() );
663          Action.u.Refresh.idScope = idScope;
664          (void)AfsAdmSvr_BeginOperation (0, &Action);
665          break;
666
667       default:
668          // Don't bother listing status-refreshes as ongoing operations
669          // for any granularity smaller than the server; they'll occur
670          // really frequently, and finish really quickly.
671          break;
672       }
673 }
674
675
676 void AfsAdmSvr_Action_StopRefresh (ASID idScope)
677 {
678    AfsAdmSvr_Enter();
679
680    for (size_t iOp = 0; iOp < l.cOperationsAllocated; ++iOp)
681       {
682       if (!l.aOperations[ iOp ].fInUse)
683          continue;
684       if (!l.aOperations[ iOp ].pAction)
685          continue;
686       if (l.aOperations[ iOp ].pAction->Action != ACTION_REFRESH)
687          continue;
688       if (l.aOperations[ iOp ].pAction->u.Refresh.idScope != idScope)
689          continue;
690
691       AfsAdmSvr_EndOperation (iOp);
692       break;
693       }
694
695    if (GetAsidType (idScope) == itCELL)
696       {
697       AfsAdmSvr_MarkRefreshThread (idScope);
698       }
699
700    AfsAdmSvr_Leave();
701 }
702
703
704 DWORD WINAPI AfsAdmSvr_AutoOpen_ThreadProc (PVOID lp)
705 {
706    DWORD dwScope = PtrToUlong(lp);
707    ULONG status;
708
709    if (!l.fOperational)
710       return 0;
711
712    // First we'll have to find out which cell to open
713    //
714    TCHAR szCell[ cchNAME ];
715    if (!CELL::GetDefaultCell (szCell, &status))
716       {
717       Print (dlERROR, TEXT("CELL::GetDefaultCell failed; error 0x%08lX"), status);
718       }
719    else
720       {
721       // Then try to actually open the cell
722       //
723       Print (dlSTANDARD, TEXT("Auto-opening cell %s; scope=%s"), szCell, (dwScope == (AFSADMSVR_SCOPE_VOLUMES | AFSADMSVR_SCOPE_USERS)) ? TEXT("full") : (dwScope == AFSADMSVR_SCOPE_VOLUMES) ? TEXT("volumes") : TEXT("users"));
724
725       LPIDENT lpiCell;
726       if ((lpiCell = CELL::OpenCell ((LPTSTR)szCell, &status)) == NULL)
727          {
728          Print (dlERROR, TEXT("Auto-open of cell %s failed; error 0x%08lX"), szCell, status);
729          }
730       else
731          {
732          LPCELL lpCell;
733          if ((lpCell = lpiCell->OpenCell (&status)) == NULL)
734             {
735             Print (dlERROR, TEXT("Auto-open: OpenCell failed; error 0x%08lX"), status);
736             }
737          else
738             {
739             AfsAdmSvr_AddToMinScope (dwScope);
740             if (!lpCell->RefreshAll (&status))
741                Print (dlERROR, TEXT("Auto-open: RefreshCell failed; error 0x%08lX"), status);
742             else
743                Print (dlSTANDARD, TEXT("Auto-open of cell %s successful"), szCell);
744             lpCell->Close();
745
746             // We intentionally do not call CELL::CloseCell() here--as would
747             // ordinarily be necessary to balance our CELL::OpenCell() call
748             // above--because we never want to close our cache for this cell.
749             // The point of calling AutoOpen() up front is to keep an admin
750             // server alive and ready for use on a particular cell--calling
751             // CELL::CloseCell() here negates that purpose.
752
753             }
754          }
755       }
756
757    return 0;
758 }
759
760
761 void AfsAdmSvr_AddToMinScope (DWORD dwScope)
762 {
763    l.dwScopeMin |= dwScope;
764    AfsClass_SpecifyRefreshDomain (l.dwScopeMin);
765 }
766
767
768 void AfsAdmSvr_SetMinScope (DWORD dwScope)
769 {
770    l.dwScopeMin = dwScope;
771 }
772
773
774 DWORD AfsAdmSvr_GetMinScope (void)
775 {
776    return l.dwScopeMin;
777 }
778