flushonhibernate-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 powerEventsRegistered = 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 (powerEventsRegistered) {
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         }
305     }           /* end switch(ctrlCode) */                                                        
306     return dwRet;   
307 }
308
309 /* There is similar code in client_config\drivemap.cpp GlobalMountDrive()
310  * 
311  * Mount a drive into AFS if there global mapping
312  */
313 /* DEE Could check first if we are run as SYSTEM */
314 #define MAX_RETRIES 30
315 static void MountGlobalDrives(void)
316 {
317     char szAfsPath[_MAX_PATH];
318     char szDriveToMapTo[5];
319     DWORD dwResult;
320     char szKeyName[256];
321     HKEY hKey;
322     DWORD dwIndex = 0, dwRetry = 0;
323     DWORD dwDriveSize;
324     DWORD dwSubMountSize;
325     char szSubMount[256];
326     DWORD dwType;
327
328     sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
329
330     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
331     if (dwResult != ERROR_SUCCESS)
332         return;
333
334     while (dwRetry < MAX_RETRIES) {
335         dwDriveSize = sizeof(szDriveToMapTo);
336         dwSubMountSize = sizeof(szSubMount);
337         dwResult = RegEnumValue(hKey, dwIndex++, szDriveToMapTo, &dwDriveSize, 0, &dwType, szSubMount, &dwSubMountSize);
338         if (dwResult != ERROR_MORE_DATA) {
339             if (dwResult != ERROR_SUCCESS) {
340                 if (dwResult != ERROR_NO_MORE_ITEMS)
341                     afsi_log("Failed to read GlobalAutoMapper values: %d\n", dwResult);
342                 break;
343             }
344         }
345
346         for ( ; dwRetry < MAX_RETRIES; dwRetry++)
347                 {
348                     NETRESOURCE nr;
349                     memset (&nr, 0x00, sizeof(NETRESOURCE));
350  
351                     sprintf(szAfsPath,"\\\\%s\\%s",cm_NetbiosName,szSubMount);
352                     
353                     nr.dwScope = RESOURCE_GLOBALNET;              /* ignored parameter */
354                     nr.dwType=RESOURCETYPE_DISK;
355                     nr.lpLocalName=szDriveToMapTo;
356                     nr.lpRemoteName=szAfsPath;
357                     nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE; /* ignored parameter */
358                     nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;       /* ignored parameter */
359
360                     dwResult = WNetAddConnection2(&nr,NULL,NULL,0);
361             afsi_log("GlobalAutoMap of %s to %s %s (%d)", szDriveToMapTo, szSubMount, 
362                      (dwResult == NO_ERROR) ? "succeeded" : "failed", dwResult);
363             if (dwResult == NO_ERROR) {
364                 break;
365             }
366             /* wait for smb server to come up */
367             Sleep((DWORD)1000 /* miliseconds */);               
368
369             /* Disconnect any previous mappings */
370             dwResult = WNetCancelConnection2(szDriveToMapTo, 0, TRUE);
371         }
372     }        
373
374     RegCloseKey(hKey);
375 }
376
377 static void DismountGlobalDrives()
378 {
379     char szAfsPath[_MAX_PATH];
380     char szDriveToMapTo[5];
381     DWORD dwResult;
382     char szKeyName[256];
383     HKEY hKey;
384     DWORD dwIndex = 0;
385     DWORD dwDriveSize;
386     DWORD dwSubMountSize;
387     char szSubMount[256];
388     DWORD dwType;
389
390     sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
391
392     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
393     if (dwResult != ERROR_SUCCESS)
394         return;
395
396     while (1) {
397         dwDriveSize = sizeof(szDriveToMapTo);
398         dwSubMountSize = sizeof(szSubMount);
399         dwResult = RegEnumValue(hKey, dwIndex++, szDriveToMapTo, &dwDriveSize, 0, &dwType, szSubMount, &dwSubMountSize);
400         if (dwResult != ERROR_MORE_DATA) {
401             if (dwResult != ERROR_SUCCESS) {
402                 if (dwResult != ERROR_NO_MORE_ITEMS)
403                     afsi_log("Failed to read GlobalAutoMapper values: %d\n", dwResult);
404                 break;
405             }
406         }
407
408         sprintf(szAfsPath,"\\\\%s\\%s",cm_NetbiosName,szSubMount);
409                     
410         dwResult = WNetCancelConnection2(szDriveToMapTo, 0, TRUE);
411         dwResult = WNetCancelConnection(szAfsPath, TRUE);
412         
413         afsi_log("Disconnect from GlobalAutoMap of %s to %s %s", szDriveToMapTo, szSubMount, dwResult ? "succeeded" : "failed");
414     }        
415
416     RegCloseKey(hKey);
417 }
418
419 typedef BOOL ( APIENTRY * AfsdInitHook )(void);
420 #define AFSD_INIT_HOOK "AfsdInitHook"
421 #define AFSD_HOOK_DLL  "afsdhook.dll"
422
423 /*
424 control serviceex exists only on 2000/xp. These functions will be loaded dynamically.
425 */
426
427 typedef SERVICE_STATUS_HANDLE ( * RegisterServiceCtrlHandlerExFunc )(  LPCTSTR , LPHANDLER_FUNCTION_EX , LPVOID );
428 typedef SERVICE_STATUS_HANDLE ( * RegisterServiceCtrlHandlerFunc   )(  LPCTSTR ,  LPHANDLER_FUNCTION );
429
430 RegisterServiceCtrlHandlerExFunc pRegisterServiceCtrlHandlerEx = NULL;
431 RegisterServiceCtrlHandlerFunc   pRegisterServiceCtrlHandler   = NULL; 
432
433 void afsd_Main(DWORD argc, LPTSTR *argv)
434 {
435     long code;
436     char *reason;
437 #ifdef JUMP
438     int jmpret;
439 #endif /* JUMP */
440     HANDLE hInitHookDll;
441     HANDLE hAdvApi32;
442     AfsdInitHook initHook;
443
444 #ifdef _DEBUG
445     _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF /*| _CRTDBG_CHECK_ALWAYS_DF*/ | 
446                    _CRTDBG_CHECK_CRT_DF /* | _CRTDBG_DELAY_FREE_MEM_DF */ );
447 #endif 
448
449     osi_InitPanic(afsd_notifier);
450     osi_InitTraceOption();
451
452     GlobalStatus = 0;
453
454     afsi_start();
455
456     WaitToTerminate = CreateEvent(NULL, TRUE, FALSE, TEXT("afsd_service_WaitToTerminate"));
457     if ( GetLastError() == ERROR_ALREADY_EXISTS )
458         afsi_log("Event Object Already Exists: %s", TEXT("afsd_service_WaitToTerminate"));
459
460 #ifndef NOTSERVICE
461     hAdvApi32 = LoadLibrary("advapi32.dll");
462     if (hAdvApi32 == NULL)
463     {
464         afsi_log("Fatal: cannot load advapi32.dll");
465         return;
466     }
467
468     pRegisterServiceCtrlHandlerEx = (RegisterServiceCtrlHandlerExFunc)GetProcAddress(hAdvApi32, "RegisterServiceCtrlHandlerExA");
469     if (pRegisterServiceCtrlHandlerEx)
470     {
471         afsi_log("running on 2000+ - using RegisterServiceCtrlHandlerEx");
472         StatusHandle = RegisterServiceCtrlHandlerEx(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandlerEx, NULL );
473     }
474     else
475     {
476         StatusHandle = RegisterServiceCtrlHandler(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandler);
477     }
478
479     ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
480     ServiceStatus.dwServiceSpecificExitCode = 0;
481     ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
482     ServiceStatus.dwWin32ExitCode = NO_ERROR;
483     ServiceStatus.dwCheckPoint = 1;
484     ServiceStatus.dwWaitHint = 30000;
485     /* accept Power Events */
486     ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
487     SetServiceStatus(StatusHandle, &ServiceStatus);
488 #endif
489
490     {       
491     HANDLE h; char *ptbuf[1];
492     h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
493     ptbuf[0] = "AFS start pending";
494     ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
495     DeregisterEventSource(h);
496     }
497
498 #ifdef REGISTER_POWER_NOTIFICATIONS
499     {
500         HKEY hkParm;
501         DWORD code;
502         DWORD dummyLen;
503         int bpower = TRUE;
504
505         /* see if we should handle power notifications */
506         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName, 0, KEY_QUERY_VALUE, &hkParm);
507         if (code == ERROR_SUCCESS) {
508             dummyLen = sizeof(bpower);
509             code = RegQueryValueEx(hkParm, "FlushOnHibernate", NULL, NULL,
510                 (BYTE *) &bpower, &dummyLen);      
511
512             if(code != ERROR_SUCCESS)
513                 bpower = TRUE;
514
515             RegCloseKey(hkParm);
516         }
517         /* create thread used to flush cache */
518         if (bpower) {
519             PowerNotificationThreadCreate();
520             powerEventsRegistered = 1;
521         }
522     }
523 #endif
524
525     /* allow an exit to be called prior to any initialization */
526     hInitHookDll = LoadLibrary(AFSD_HOOK_DLL);
527     if (hInitHookDll)
528     {
529         BOOL hookRc = FALSE;
530         initHook = ( AfsdInitHook ) GetProcAddress(hInitHookDll, AFSD_INIT_HOOK);
531         if (initHook)
532         {
533             hookRc = initHook();
534         }
535         FreeLibrary(hInitHookDll);
536         hInitHookDll = NULL;
537
538         if (hookRc == FALSE)
539         {
540             ServiceStatus.dwCurrentState = SERVICE_STOPPED;
541             ServiceStatus.dwWin32ExitCode = NO_ERROR;
542             ServiceStatus.dwCheckPoint = 0;
543             ServiceStatus.dwWaitHint = 0;
544             ServiceStatus.dwControlsAccepted = 0;
545             SetServiceStatus(StatusHandle, &ServiceStatus);
546                        
547             /* exit if initialization failed */
548             return;
549         }
550         else
551         {
552             /* allow another 15 seconds to start */
553             ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
554             ServiceStatus.dwServiceSpecificExitCode = 0;
555             ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
556             ServiceStatus.dwWin32ExitCode = NO_ERROR;
557             ServiceStatus.dwCheckPoint = 2;
558             ServiceStatus.dwWaitHint = 20000;
559             /* accept Power Events */
560             ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
561             SetServiceStatus(StatusHandle, &ServiceStatus);
562         }
563     }
564
565 #ifdef JUMP
566     MainThreadId = GetCurrentThreadId();
567     jmpret = setjmp(notifier_jmp);
568
569     if (jmpret == 0) 
570 #endif /* JUMP */
571     {
572         code = afsd_InitCM(&reason);
573         if (code != 0) {
574             afsi_log("afsd_InitCM failed: %s (code = %d)", reason, code);
575             osi_panic(reason, __FILE__, __LINE__);
576         }
577
578 #ifndef NOTSERVICE
579         ServiceStatus.dwCheckPoint++;
580         ServiceStatus.dwWaitHint -= 5000;
581         SetServiceStatus(StatusHandle, &ServiceStatus);
582 #endif
583         code = afsd_InitDaemons(&reason);
584         if (code != 0) {
585             afsi_log("afsd_InitDaemons failed: %s (code = %d)", reason, code);
586                         osi_panic(reason, __FILE__, __LINE__);
587         }
588
589 #ifndef NOTSERVICE
590         ServiceStatus.dwCheckPoint++;
591         ServiceStatus.dwWaitHint -= 5000;
592         SetServiceStatus(StatusHandle, &ServiceStatus);
593 #endif
594         code = afsd_InitSMB(&reason, MessageBox);
595         if (code != 0) {
596             afsi_log("afsd_InitSMB failed: %s (code = %d)", reason, code);
597             osi_panic(reason, __FILE__, __LINE__);
598         }
599
600         MountGlobalDrives();
601
602 #ifndef NOTSERVICE
603         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
604         ServiceStatus.dwWin32ExitCode = NO_ERROR;
605         ServiceStatus.dwCheckPoint = 0;
606         ServiceStatus.dwWaitHint = 0;
607
608         /* accept Power events */
609         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
610         SetServiceStatus(StatusHandle, &ServiceStatus);
611 #endif  
612         {
613             HANDLE h; char *ptbuf[1];
614             h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
615             ptbuf[0] = "AFS running";
616             ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
617             DeregisterEventSource(h);
618         }
619     }
620
621     WaitForSingleObject(WaitToTerminate, INFINITE);
622
623     {   
624         HANDLE h; char *ptbuf[1];
625         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
626         ptbuf[0] = "AFS quitting";
627         ReportEvent(h, GlobalStatus ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
628                 0, 0, NULL, 1, 0, ptbuf, NULL);
629         DeregisterEventSource(h);
630     }
631
632     DismountGlobalDrives();
633     smb_Shutdown();
634     rx_Finalize();
635
636 #ifdef  REGISTER_POWER_NOTIFICATIONS
637     /* terminate thread used to flush cache */
638     if (powerEventsRegistered)
639         PowerNotificationThreadExit();
640 #endif
641
642     /* Remove the ExceptionFilter */
643     SetUnhandledExceptionFilter(NULL);
644
645     ServiceStatus.dwCurrentState = SERVICE_STOPPED;
646     ServiceStatus.dwWin32ExitCode = GlobalStatus ? ERROR_EXCEPTION_IN_SERVICE : NO_ERROR;
647     ServiceStatus.dwCheckPoint = 0;
648     ServiceStatus.dwWaitHint = 0;
649     ServiceStatus.dwControlsAccepted = 0;
650     SetServiceStatus(StatusHandle, &ServiceStatus);
651 }       
652
653 DWORD __stdcall afsdMain_thread(void* notUsed)
654 {
655     char * argv[2] = {AFS_DAEMON_SERVICE_NAME, NULL};
656     afsd_Main(1, (LPTSTR*)argv);
657     return(0);
658 }
659
660 int
661 main(void)
662 {
663     static SERVICE_TABLE_ENTRY dispatchTable[] = {
664         {AFS_DAEMON_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) afsd_Main},
665         {NULL, NULL}
666     };
667
668     if (!StartServiceCtrlDispatcher(dispatchTable))
669     {
670         LONG status = GetLastError();
671         if (status == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
672         {
673             DWORD tid;
674             hAFSDMainThread = CreateThread(NULL, 0, afsdMain_thread, 0, 0, &tid);
675                 
676             printf("Hit <Enter> to terminate OpenAFS Client Service\n");
677             getchar();  
678             SetEvent(WaitToTerminate);
679         }
680     }
681
682     if ( hAFSDMainThread ) {
683         WaitForSingleObject( hAFSDMainThread, INFINITE );
684         CloseHandle( hAFSDMainThread );
685     }
686     return(0);
687 }