flushonhibernate-registry-20041017
[openafs.git] / src / WINNT / afsd / afsd_service.c
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 <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <windows.h>
14 #include <string.h>
15 #include <setjmp.h>
16 #include "afsd.h"
17 #include "afsd_init.h"
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <winsock2.h>
21
22 #include <osi.h>
23
24 #ifdef DEBUG
25 //#define NOTSERVICE
26 #endif
27 #ifdef _DEBUG
28 #include <crtdbg.h>
29 #endif
30
31 /*
32 // The following is defined if you want to receive Power notifications,
33 // including Hibernation, and also subsequent flushing of AFS volumes
34 //
35 #define REGISTER_POWER_NOTIFICATIONS 1
36 #define FLUSH_VOLUME                 1
37 //
38 // Check
39 */
40 #include "afsd_flushvol.h"
41
42 extern void afsi_log(char *pattern, ...);
43
44 HANDLE hAFSDMainThread = NULL;
45
46 HANDLE WaitToTerminate;
47
48 int GlobalStatus;
49
50 #ifdef JUMP
51 unsigned int MainThreadId;
52 jmp_buf notifier_jmp;
53 #endif /* JUMP */
54
55 extern int traceOnPanic;
56 extern HANDLE afsi_file;
57
58 int powerEventsRegsitered = 0;
59
60 /*
61  * Notifier function for use by osi_panic
62  */
63 static void afsd_notifier(char *msgp, char *filep, long line)
64 {
65     char tbuffer[512];
66     char *ptbuf[1];
67     HANDLE h;
68
69     if (filep)
70         sprintf(tbuffer, "Error at file %s, line %d: %s",
71                  filep, line, msgp);
72     else
73         sprintf(tbuffer, "Error at unknown location: %s", msgp);
74
75     h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
76     ptbuf[0] = tbuffer;
77     ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, line, NULL, 1, 0, ptbuf, NULL);
78     DeregisterEventSource(h);
79
80     GlobalStatus = line;
81
82     osi_LogEnable(afsd_logp);
83
84     afsd_ForceTrace(TRUE);
85
86     afsi_log("--- begin dump ---");
87     cm_DumpSCache(afsi_file, "a");
88 #ifdef keisa
89     cm_dnlcDump(afsi_file, "a");
90 #endif
91     cm_DumpBufHashTable(afsi_file, "a");
92     smb_DumpVCP(afsi_file, "a");                        
93     afsi_log("--- end   dump ---");
94     
95     DebugBreak();       
96
97     SetEvent(WaitToTerminate);
98
99 #ifdef JUMP
100     if (GetCurrentThreadId() == MainThreadId)
101         longjmp(notifier_jmp, 1);
102     else
103 #endif /* JUMP */
104         ExitThread(1);
105 }
106
107 /*
108  * For use miscellaneously in smb.c; need to do better
109  */
110 static int _stdcall DummyMessageBox(HWND h, LPCTSTR l1, LPCTSTR l2, UINT ui)
111 {
112     return 0;
113 }
114
115 static SERVICE_STATUS           ServiceStatus;
116 static SERVICE_STATUS_HANDLE    StatusHandle;
117
118 DWORD
119 afsd_ServiceFlushVolume(DWORD dwlpEventData)
120 {
121     DWORD   dwRet = ERROR_NETWORK_BUSY; /* or NO_ERROR */
122
123     /*
124     **  If UI bit is not set, user interaction is not possible
125     **      BUT, since we are a NON-interactive service, and therefore
126     **  have NO user I/O, it doesn't much matter.
127     **  This benign code left here as example of how to find this out
128     */
129     BOOL bUI = (dwlpEventData & 1);
130
131     /* flush volume */
132     if ( PowerNotificationThreadNotify() )
133     {
134         dwRet = NO_ERROR;
135     }
136     else
137     {
138         /* flush was unsuccessful, or timeout - deny shutdown */
139         dwRet = ERROR_NETWORK_BUSY;
140     }
141
142     /*      to deny hibernate, simply return
143     //      any value besides NO_ERROR.
144     //      For example:
145     //      dwRet = ERROR_NETWORK_BUSY;
146     */
147
148     return dwRet;
149 }
150
151
152 /* service control handler used in nt4 only for backward compat. */
153 VOID WINAPI 
154 afsd_ServiceControlHandler(DWORD ctrlCode)
155 {
156     HKEY parmKey;
157     DWORD dummyLen, doTrace;
158     long code;
159
160     switch (ctrlCode) {
161     case SERVICE_CONTROL_STOP:
162         /* Shutdown RPC */
163         RpcMgmtStopServerListening(NULL);
164
165         /* Force trace if requested */
166         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
167                              AFSConfigKeyName,
168                              0, KEY_QUERY_VALUE, &parmKey);
169         if (code != ERROR_SUCCESS)
170             goto doneTrace;
171
172         dummyLen = sizeof(doTrace);
173         code = RegQueryValueEx(parmKey, "TraceOnShutdown",
174                                 NULL, NULL,
175                                 (BYTE *) &doTrace, &dummyLen);
176         RegCloseKey (parmKey);
177         if (code != ERROR_SUCCESS)
178             doTrace = 0;
179         if (doTrace)
180             afsd_ForceTrace(FALSE);
181
182       doneTrace:
183         ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
184         ServiceStatus.dwWin32ExitCode = NO_ERROR;
185         ServiceStatus.dwCheckPoint = 1;
186         ServiceStatus.dwWaitHint = 10000;
187         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
188         SetServiceStatus(StatusHandle, &ServiceStatus);
189         SetEvent(WaitToTerminate);
190         break;
191     case SERVICE_CONTROL_INTERROGATE:
192         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
193         ServiceStatus.dwWin32ExitCode = NO_ERROR;
194         ServiceStatus.dwCheckPoint = 0;
195         ServiceStatus.dwWaitHint = 0;
196         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
197         SetServiceStatus(StatusHandle, &ServiceStatus);
198         break;
199         /* XXX handle system shutdown */
200         /* XXX handle pause & continue */
201     }
202 }       
203
204
205 /*
206 **    Extended ServiceControlHandler that provides Event types
207 **    for monitoring Power events, for example.
208 */
209 DWORD WINAPI
210 afsd_ServiceControlHandlerEx(
211               DWORD  ctrlCode,
212               DWORD  dwEventType,
213               LPVOID lpEventData,
214               LPVOID lpContext
215               )
216 {
217     HKEY parmKey;
218     DWORD dummyLen, doTrace;
219     long code;
220     DWORD dwRet = ERROR_CALL_NOT_IMPLEMENTED;
221
222     switch (ctrlCode) 
223     {
224     case SERVICE_CONTROL_STOP:
225         /* Shutdown RPC */
226         RpcMgmtStopServerListening(NULL);
227
228         /* Force trace if requested */
229         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
230                             AFSConfigKeyName,
231                             0, KEY_QUERY_VALUE, &parmKey);
232         if (code != ERROR_SUCCESS)
233             goto doneTrace;
234
235         dummyLen = sizeof(doTrace);
236         code = RegQueryValueEx(parmKey, "TraceOnShutdown",
237                                NULL, NULL,
238                                (BYTE *) &doTrace, &dummyLen);
239         RegCloseKey (parmKey);
240         if (code != ERROR_SUCCESS)
241             doTrace = 0;
242         if (doTrace)
243             afsd_ForceTrace(FALSE);
244
245       doneTrace:
246         ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
247         ServiceStatus.dwWin32ExitCode = NO_ERROR;
248         ServiceStatus.dwCheckPoint = 1;
249         ServiceStatus.dwWaitHint = 10000;
250         ServiceStatus.dwControlsAccepted = 0;
251         SetServiceStatus(StatusHandle, &ServiceStatus);
252         SetEvent(WaitToTerminate);
253         dwRet = NO_ERROR;
254         break;
255
256     case SERVICE_CONTROL_INTERROGATE:
257         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
258         ServiceStatus.dwWin32ExitCode = NO_ERROR;
259         ServiceStatus.dwCheckPoint = 0;
260         ServiceStatus.dwWaitHint = 0;
261         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
262         SetServiceStatus(StatusHandle, &ServiceStatus);
263         dwRet = NO_ERROR;
264         break;
265
266         /* XXX handle system shutdown */
267         /* XXX handle pause & continue */
268     case SERVICE_CONTROL_POWEREVENT:                                              
269         {                                                                                     
270             /*                                                                                
271             **  dwEventType of this notification == WPARAM of WM_POWERBROADCAST               
272             **  Return NO_ERROR == return TRUE for that message, i.e. accept request          
273             **  Return any error code to deny request,                                        
274             **  i.e. as if returning BROADCAST_QUERY_DENY                                     
275             */                                                                                
276             if(powerEventsRegsitered) {
277             switch((int) dwEventType)                                                         
278             {                                                                               
279             case PBT_APMQUERYSUSPEND:                                                         
280             case PBT_APMQUERYSTANDBY:                                                         
281
282 #ifdef  FLUSH_VOLUME
283                 /* handle event */                                                            
284                 dwRet = afsd_ServiceFlushVolume((DWORD) lpEventData);                         
285 #else                                                                                       
286                 dwRet = NO_ERROR;                                                             
287 #endif                                                                                      
288                 break;                                                                        
289                                                                                                                           
290                 /* allow remaining case PBT_WhatEver */                                           
291             case PBT_APMSUSPEND:                                                              
292             case PBT_APMSTANDBY:                                                              
293             case PBT_APMRESUMECRITICAL:                                                       
294             case PBT_APMRESUMESUSPEND:                                                        
295             case PBT_APMRESUMESTANDBY:                                                        
296             case PBT_APMBATTERYLOW:                                                           
297             case PBT_APMPOWERSTATUSCHANGE:                                                    
298             case PBT_APMOEMEVENT:                                                             
299             case PBT_APMRESUMEAUTOMATIC:                                                      
300             default:                                                                          
301                 dwRet = NO_ERROR;                                                             
302             }   
303         }
304     }           /* end switch(ctrlCode) */                                                        
305     return dwRet;   
306 }
307
308 /* There is similar code in client_config\drivemap.cpp GlobalMountDrive()
309  * 
310  * Mount a drive into AFS if there global mapping
311  */
312 /* DEE Could check first if we are run as SYSTEM */
313 #define MAX_RETRIES 30
314 static void MountGlobalDrives()
315 {
316     char szAfsPath[_MAX_PATH];
317     char szDriveToMapTo[5];
318     DWORD dwResult;
319     char szKeyName[256];
320     HKEY hKey;
321     DWORD dwIndex = 0, dwRetry = 0;
322     DWORD dwDriveSize;
323     DWORD dwSubMountSize;
324     char szSubMount[256];
325     DWORD dwType;
326
327     sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
328
329     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
330     if (dwResult != ERROR_SUCCESS)
331         return;
332
333     while (dwRetry < MAX_RETRIES) {
334         dwDriveSize = sizeof(szDriveToMapTo);
335         dwSubMountSize = sizeof(szSubMount);
336         dwResult = RegEnumValue(hKey, dwIndex++, szDriveToMapTo, &dwDriveSize, 0, &dwType, szSubMount, &dwSubMountSize);
337         if (dwResult != ERROR_MORE_DATA) {
338             if (dwResult != ERROR_SUCCESS) {
339                 if (dwResult != ERROR_NO_MORE_ITEMS)
340                     afsi_log("Failed to read GlobalAutoMapper values: %d\n", dwResult);
341                 break;
342             }
343         }
344
345         for ( ; dwRetry < MAX_RETRIES; dwRetry++)
346                 {
347                     NETRESOURCE nr;
348                     memset (&nr, 0x00, sizeof(NETRESOURCE));
349  
350                     sprintf(szAfsPath,"\\\\%s\\%s",cm_NetbiosName,szSubMount);
351                     
352                     nr.dwScope = RESOURCE_GLOBALNET;              /* ignored parameter */
353                     nr.dwType=RESOURCETYPE_DISK;
354                     nr.lpLocalName=szDriveToMapTo;
355                     nr.lpRemoteName=szAfsPath;
356                     nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE; /* ignored parameter */
357                     nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;       /* ignored parameter */
358
359                     dwResult = WNetAddConnection2(&nr,NULL,NULL,0);
360             afsi_log("GlobalAutoMap of %s to %s %s (%d)", szDriveToMapTo, szSubMount, 
361                      (dwResult == NO_ERROR) ? "succeeded" : "failed", dwResult);
362             if (dwResult == NO_ERROR) {
363                 break;
364             }
365             /* wait for smb server to come up */
366             Sleep((DWORD)1000 /* miliseconds */);               
367
368             /* Disconnect any previous mappings */
369             dwResult = WNetCancelConnection2(szDriveToMapTo, 0, TRUE);
370         }
371     }        
372
373     RegCloseKey(hKey);
374 }
375
376 static void DismountGlobalDrives()
377 {
378     char szAfsPath[_MAX_PATH];
379     char szDriveToMapTo[5];
380     DWORD dwResult;
381     char szKeyName[256];
382     HKEY hKey;
383     DWORD dwIndex = 0;
384     DWORD dwDriveSize;
385     DWORD dwSubMountSize;
386     char szSubMount[256];
387     DWORD dwType;
388
389     sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
390
391     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
392     if (dwResult != ERROR_SUCCESS)
393         return;
394
395     while (1) {
396         dwDriveSize = sizeof(szDriveToMapTo);
397         dwSubMountSize = sizeof(szSubMount);
398         dwResult = RegEnumValue(hKey, dwIndex++, szDriveToMapTo, &dwDriveSize, 0, &dwType, szSubMount, &dwSubMountSize);
399         if (dwResult != ERROR_MORE_DATA) {
400             if (dwResult != ERROR_SUCCESS) {
401                 if (dwResult != ERROR_NO_MORE_ITEMS)
402                     afsi_log("Failed to read GlobalAutoMapper values: %d\n", dwResult);
403                 break;
404             }
405         }
406
407         sprintf(szAfsPath,"\\\\%s\\%s",cm_NetbiosName,szSubMount);
408                     
409         dwResult = WNetCancelConnection2(szDriveToMapTo, 0, TRUE);
410         dwResult = WNetCancelConnection(szAfsPath, TRUE);
411         
412         afsi_log("Disconnect from GlobalAutoMap of %s to %s %s", szDriveToMapTo, szSubMount, dwResult ? "succeeded" : "failed");
413     }        
414
415     RegCloseKey(hKey);
416 }
417
418 typedef BOOL ( APIENTRY * AfsdInitHook )(void);
419 #define AFSD_INIT_HOOK "AfsdInitHook"
420 #define AFSD_HOOK_DLL  "afsdhook.dll"
421
422 /*
423 control serviceex exists only on 2000/xp. These functions will be loaded dynamically.
424 */
425
426 typedef SERVICE_STATUS_HANDLE ( * RegisterServiceCtrlHandlerExFunc )(  LPCTSTR , LPHANDLER_FUNCTION_EX , LPVOID );
427 typedef SERVICE_STATUS_HANDLE ( * RegisterServiceCtrlHandlerFunc   )(  LPCTSTR ,  LPHANDLER_FUNCTION );
428
429 RegisterServiceCtrlHandlerExFunc pRegisterServiceCtrlHandlerEx = NULL;
430 RegisterServiceCtrlHandlerFunc   pRegisterServiceCtrlHandler   = NULL; 
431
432 void afsd_Main(DWORD argc, LPTSTR *argv)
433 {
434     long code;
435     char *reason;
436 #ifdef JUMP
437     int jmpret;
438 #endif /* JUMP */
439     HANDLE hInitHookDll;
440     HANDLE hAdvApi32;
441     AfsdInitHook initHook;
442
443 #ifdef _DEBUG
444     _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF /*| _CRTDBG_CHECK_ALWAYS_DF*/ | 
445                    _CRTDBG_CHECK_CRT_DF /* | _CRTDBG_DELAY_FREE_MEM_DF */ );
446 #endif 
447
448     osi_InitPanic(afsd_notifier);
449     osi_InitTraceOption();
450
451     GlobalStatus = 0;
452
453     afsi_start();
454
455     WaitToTerminate = CreateEvent(NULL, TRUE, FALSE, TEXT("afsd_service_WaitToTerminate"));
456     if ( GetLastError() == ERROR_ALREADY_EXISTS )
457         afsi_log("Event Object Already Exists: %s", TEXT("afsd_service_WaitToTerminate"));
458
459 #ifndef NOTSERVICE
460     hAdvApi32 = LoadLibrary("advapi32.dll");
461     if (hAdvApi32 == NULL)
462     {
463         afsi_log("Fatal: cannot load advapi32.dll");
464         return;
465     }
466
467     pRegisterServiceCtrlHandlerEx = (RegisterServiceCtrlHandlerExFunc)GetProcAddress(hAdvApi32, "RegisterServiceCtrlHandlerExA");
468     if (pRegisterServiceCtrlHandlerEx)
469     {
470         afsi_log("running on 2000+ - using RegisterServiceCtrlHandlerEx");
471         StatusHandle = RegisterServiceCtrlHandlerEx(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandlerEx, NULL );
472     }
473     else
474     {
475         StatusHandle = RegisterServiceCtrlHandler(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandler);
476     }
477
478     ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
479     ServiceStatus.dwServiceSpecificExitCode = 0;
480     ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
481     ServiceStatus.dwWin32ExitCode = NO_ERROR;
482     ServiceStatus.dwCheckPoint = 1;
483     ServiceStatus.dwWaitHint = 30000;
484     /* accept Power Events */
485     ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
486     SetServiceStatus(StatusHandle, &ServiceStatus);
487 #endif
488
489     {       
490     HANDLE h; char *ptbuf[1];
491     h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
492     ptbuf[0] = "AFS start pending";
493     ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
494     DeregisterEventSource(h);
495     }
496
497 #ifdef REGISTER_POWER_NOTIFICATIONS
498     {
499         HKEY hkParm;
500         DWORD code;
501         DWORD dummyLen;
502         int bpower = TRUE;
503
504         /* see if we should handle power notifications */
505         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName, 0, KEY_QUERY_VALUE, &hkParm);
506         if(code == ERROR_SUCCESS) {
507             dummyLen = sizeof(bpower);
508             code = RegQueryValueEx(hkParm, "FlushOnHibernate", NULL, NULL,
509                 (BYTE *) &bpower, &dummyLen);      
510
511             if(code != ERROR_SUCCESS)
512                 bpower = TRUE;
513
514             RegCloseKey(hkParm);
515         }
516     /* create thread used to flush cache */
517         if(bpower) {
518     PowerNotificationThreadCreate();
519             powerEventsRegsitered = 1;
520         }
521     }
522 #endif
523
524     /* allow an exit to be called prior to any initialization */
525     hInitHookDll = LoadLibrary(AFSD_HOOK_DLL);
526     if (hInitHookDll)
527     {
528         BOOL hookRc = FALSE;
529         initHook = ( AfsdInitHook ) GetProcAddress(hInitHookDll, AFSD_INIT_HOOK);
530         if (initHook)
531         {
532             hookRc = initHook();
533         }
534         FreeLibrary(hInitHookDll);
535         hInitHookDll = NULL;
536
537         if (hookRc == FALSE)
538         {
539             ServiceStatus.dwCurrentState = SERVICE_STOPPED;
540             ServiceStatus.dwWin32ExitCode = NO_ERROR;
541             ServiceStatus.dwCheckPoint = 0;
542             ServiceStatus.dwWaitHint = 0;
543             ServiceStatus.dwControlsAccepted = 0;
544             SetServiceStatus(StatusHandle, &ServiceStatus);
545                        
546             /* exit if initialization failed */
547             return;
548         }
549         else
550         {
551             /* allow another 15 seconds to start */
552             ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
553             ServiceStatus.dwServiceSpecificExitCode = 0;
554             ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
555             ServiceStatus.dwWin32ExitCode = NO_ERROR;
556             ServiceStatus.dwCheckPoint = 2;
557             ServiceStatus.dwWaitHint = 20000;
558             /* accept Power Events */
559             ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
560             SetServiceStatus(StatusHandle, &ServiceStatus);
561         }
562     }
563
564 #ifdef JUMP
565     MainThreadId = GetCurrentThreadId();
566     jmpret = setjmp(notifier_jmp);
567
568     if (jmpret == 0) 
569 #endif /* JUMP */
570     {
571         code = afsd_InitCM(&reason);
572         if (code != 0) {
573             afsi_log("afsd_InitCM failed: %s (code = %d)", reason, code);
574             osi_panic(reason, __FILE__, __LINE__);
575         }
576
577 #ifndef NOTSERVICE
578         ServiceStatus.dwCheckPoint++;
579         ServiceStatus.dwWaitHint -= 5000;
580         SetServiceStatus(StatusHandle, &ServiceStatus);
581 #endif
582         code = afsd_InitDaemons(&reason);
583         if (code != 0) {
584             afsi_log("afsd_InitDaemons failed: %s (code = %d)", reason, code);
585                         osi_panic(reason, __FILE__, __LINE__);
586         }
587
588 #ifndef NOTSERVICE
589         ServiceStatus.dwCheckPoint++;
590         ServiceStatus.dwWaitHint -= 5000;
591         SetServiceStatus(StatusHandle, &ServiceStatus);
592 #endif
593         code = afsd_InitSMB(&reason, MessageBox);
594         if (code != 0) {
595             afsi_log("afsd_InitSMB failed: %s (code = %d)", reason, code);
596             osi_panic(reason, __FILE__, __LINE__);
597         }
598
599         MountGlobalDrives();
600
601 #ifndef NOTSERVICE
602         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
603         ServiceStatus.dwWin32ExitCode = NO_ERROR;
604         ServiceStatus.dwCheckPoint = 0;
605         ServiceStatus.dwWaitHint = 0;
606
607         /* accept Power events */
608         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
609         SetServiceStatus(StatusHandle, &ServiceStatus);
610 #endif  
611         {
612             HANDLE h; char *ptbuf[1];
613             h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
614             ptbuf[0] = "AFS running";
615             ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
616             DeregisterEventSource(h);
617         }
618     }
619
620     WaitForSingleObject(WaitToTerminate, INFINITE);
621
622     {   
623         HANDLE h; char *ptbuf[1];
624         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
625         ptbuf[0] = "AFS quitting";
626         ReportEvent(h, GlobalStatus ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
627                 0, 0, NULL, 1, 0, ptbuf, NULL);
628         DeregisterEventSource(h);
629     }
630
631     DismountGlobalDrives();
632     smb_Shutdown();
633     rx_Finalize();
634
635 #ifdef  REGISTER_POWER_NOTIFICATIONS
636     /* terminate thread used to flush cache */
637     if(powerEventsRegsitered)
638     PowerNotificationThreadExit();
639 #endif
640
641     /* Remove the ExceptionFilter */
642     SetUnhandledExceptionFilter(NULL);
643
644     ServiceStatus.dwCurrentState = SERVICE_STOPPED;
645     ServiceStatus.dwWin32ExitCode = GlobalStatus ? ERROR_EXCEPTION_IN_SERVICE : NO_ERROR;
646     ServiceStatus.dwCheckPoint = 0;
647     ServiceStatus.dwWaitHint = 0;
648     ServiceStatus.dwControlsAccepted = 0;
649     SetServiceStatus(StatusHandle, &ServiceStatus);
650 }       
651
652 DWORD __stdcall afsdMain_thread(void* notUsed)
653 {
654     char * argv[2] = {AFS_DAEMON_SERVICE_NAME, NULL};
655     afsd_Main(1, (LPTSTR*)argv);
656     return(0);
657 }
658
659 int
660 main(void)
661 {
662     static SERVICE_TABLE_ENTRY dispatchTable[] = {
663         {AFS_DAEMON_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) afsd_Main},
664         {NULL, NULL}
665     };
666
667     if (!StartServiceCtrlDispatcher(dispatchTable))
668     {
669         LONG status = GetLastError();
670         if (status == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
671         {
672             DWORD tid;
673             hAFSDMainThread = CreateThread(NULL, 0, afsdMain_thread, 0, 0, &tid);
674                 
675             printf("Hit <Enter> to terminate OpenAFS Client Service\n");
676             getchar();  
677             SetEvent(WaitToTerminate);
678         }
679     }
680
681     if ( hAFSDMainThread ) {
682         WaitForSingleObject( hAFSDMainThread, INFINITE );
683         CloseHandle( hAFSDMainThread );
684     }
685     return(0);
686 }