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