down-before-busy-20040723
[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
36 //
37 // Check
38 */
39 #include "afsd_flushvol.h"
40
41 extern void afsi_log(char *pattern, ...);
42
43 HANDLE hAFSDMainThread = NULL;
44
45 HANDLE WaitToTerminate;
46
47 int GlobalStatus;
48
49 #ifdef JUMP
50 unsigned int MainThreadId;
51 jmp_buf notifier_jmp;
52 #endif /* JUMP */
53
54 extern int traceOnPanic;
55 extern HANDLE afsi_file;
56
57 /*
58  * Notifier function for use by osi_panic
59  */
60 static void afsd_notifier(char *msgp, char *filep, long line)
61 {
62         char tbuffer[512];
63         char *ptbuf[1];
64         HANDLE h;
65
66         if (filep)
67                 sprintf(tbuffer, "Error at file %s, line %d: %s",
68                         filep, line, msgp);
69         else
70                 sprintf(tbuffer, "Error at unknown location: %s", msgp);
71
72         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
73         ptbuf[0] = tbuffer;
74         ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, line, NULL, 1, 0, ptbuf, NULL);
75         DeregisterEventSource(h);
76
77         GlobalStatus = line;
78
79         osi_LogEnable(afsd_logp);
80
81         afsd_ForceTrace(TRUE);
82
83     afsi_log("--- begin dump ---");
84     cm_DumpSCache(afsi_file, "a");
85 #ifdef keisa
86     cm_dnlcDump(afsi_file, "a");
87 #endif
88     cm_DumpBufHashTable(afsi_file, "a");
89     smb_DumpVCP(afsi_file, "a");                        
90     afsi_log("--- end   dump ---");
91     
92     DebugBreak();       
93
94         SetEvent(WaitToTerminate);
95
96 #ifdef JUMP
97         if (GetCurrentThreadId() == MainThreadId)
98                 longjmp(notifier_jmp, 1);
99         else
100 #endif /* JUMP */
101                 ExitThread(1);
102 }
103
104 /*
105  * For use miscellaneously in smb.c; need to do better
106  */
107 static int _stdcall DummyMessageBox(HWND h, LPCTSTR l1, LPCTSTR l2, UINT ui)
108 {
109         return 0;
110 }
111
112 static SERVICE_STATUS           ServiceStatus;
113 static SERVICE_STATUS_HANDLE    StatusHandle;
114
115 DWORD
116 afsd_ServiceFlushVolume(DWORD dwlpEventData)
117 {
118     DWORD   dwRet = ERROR_NETWORK_BUSY; /* or NO_ERROR */
119
120     /*
121     **  If UI bit is not set, user interaction is not possible
122     **      BUT, since we are a NON-interactive service, and therefore
123     **  have NO user I/O, it doesn't much matter.
124     **  This benign code left here as example of how to find this out
125     */
126     BOOL bUI = (dwlpEventData & 1);
127
128     /* flush volume */
129     if ( PowerNotificationThreadNotify() )
130     {
131         dwRet = NO_ERROR;
132     }
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  REGISTER_POWER_NOTIFICATIONS                                                                  
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;
350                     nr.dwType=RESOURCETYPE_DISK;
351                     nr.lpLocalName=szDriveToMapTo;
352                     nr.lpRemoteName=szAfsPath;
353                     nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
354                     nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;
355
356                     dwResult = WNetAddConnection2(&nr,NULL,NULL,FALSE);
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     }        
366
367     RegCloseKey(hKey);
368 }
369
370 static void DismountGlobalDrives()
371 {
372     char szAfsPath[_MAX_PATH];
373     char szDriveToMapTo[5];
374     DWORD dwResult;
375     char szKeyName[256];
376     HKEY hKey;
377     DWORD dwIndex = 0;
378     DWORD dwDriveSize;
379     DWORD dwSubMountSize;
380     char szSubMount[256];
381     DWORD dwType;
382
383     sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
384
385         dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
386         if (dwResult != ERROR_SUCCESS)
387         return;
388
389     while (1) {
390         dwDriveSize = sizeof(szDriveToMapTo);
391         dwSubMountSize = sizeof(szSubMount);
392         dwResult = RegEnumValue(hKey, dwIndex++, szDriveToMapTo, &dwDriveSize, 0, &dwType, szSubMount, &dwSubMountSize);
393         if (dwResult != ERROR_MORE_DATA) {
394             if (dwResult != ERROR_SUCCESS) {
395                 if (dwResult != ERROR_NO_MORE_ITEMS)
396                     afsi_log("Failed to read GlobalAutoMapper values: %d\n", dwResult);
397                 break;
398             }
399         }
400
401         sprintf(szAfsPath,"\\\\%s\\%s",cm_NetbiosName,szSubMount);
402                     
403         dwResult = WNetCancelConnection(szAfsPath, TRUE);
404         
405         afsi_log("Disconnect from GlobalAutoMap of %s to %s %s", szDriveToMapTo, szSubMount, dwResult ? "succeeded" : "failed");
406     }        
407
408     RegCloseKey(hKey);
409 }
410
411 typedef BOOL ( APIENTRY * AfsdInitHook )(void);
412 #define AFSD_INIT_HOOK "AfsdInitHook"
413 #define AFSD_HOOK_DLL  "afsdhook.dll"
414
415 /*
416 control serviceex exists only on 2000/xp. These functions will be loaded dynamically.
417 */
418
419 typedef SERVICE_STATUS_HANDLE ( * RegisterServiceCtrlHandlerExFunc )(  LPCTSTR , LPHANDLER_FUNCTION_EX , LPVOID );
420 typedef SERVICE_STATUS_HANDLE ( * RegisterServiceCtrlHandlerFunc   )(  LPCTSTR ,  LPHANDLER_FUNCTION );
421
422 RegisterServiceCtrlHandlerExFunc pRegisterServiceCtrlHandlerEx = NULL;
423 RegisterServiceCtrlHandlerFunc   pRegisterServiceCtrlHandler   = NULL; 
424
425 void afsd_Main(DWORD argc, LPTSTR *argv)
426 {
427         long code;
428         char *reason;
429 #ifdef JUMP
430         int jmpret;
431 #endif /* JUMP */
432     HANDLE hInitHookDll;
433     HANDLE hAdvApi32;
434     AfsdInitHook initHook;
435
436 #ifdef _DEBUG
437     _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF /*| _CRTDBG_CHECK_ALWAYS_DF*/ | 
438                    _CRTDBG_CHECK_CRT_DF /* | _CRTDBG_DELAY_FREE_MEM_DF */ );
439 #endif 
440
441     osi_InitPanic(afsd_notifier);
442         osi_InitTraceOption();
443
444         GlobalStatus = 0;
445
446         afsi_start();
447
448         WaitToTerminate = CreateEvent(NULL, TRUE, FALSE, TEXT("afsd_service_WaitToTerminate"));
449     if ( GetLastError() == ERROR_ALREADY_EXISTS )
450         afsi_log("Event Object Already Exists: %s", TEXT("afsd_service_WaitToTerminate"));
451
452 #ifndef NOTSERVICE
453     hAdvApi32 = LoadLibrary("advapi32.dll");
454     if (hAdvApi32 == NULL)
455     {
456         afsi_log("Fatal: cannot load advapi32.dll");
457         return;
458     }
459
460     pRegisterServiceCtrlHandlerEx = (RegisterServiceCtrlHandlerExFunc)GetProcAddress(hAdvApi32, "RegisterServiceCtrlHandlerExA");
461     if (pRegisterServiceCtrlHandlerEx)
462     {
463         afsi_log("running on 2000+ - using RegisterServiceCtrlHandlerEx");
464         StatusHandle = RegisterServiceCtrlHandlerEx(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandlerEx, NULL );
465     }
466     else
467     {
468         StatusHandle = RegisterServiceCtrlHandler(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandler);
469     }
470
471         ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
472         ServiceStatus.dwServiceSpecificExitCode = 0;
473         ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
474         ServiceStatus.dwWin32ExitCode = NO_ERROR;
475         ServiceStatus.dwCheckPoint = 1;
476         ServiceStatus.dwWaitHint = 30000;
477     /* accept Power Events */
478         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
479         SetServiceStatus(StatusHandle, &ServiceStatus);
480 #endif
481
482     {       
483     HANDLE h; char *ptbuf[1];
484     h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
485     ptbuf[0] = "AFS start pending";
486     ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
487     DeregisterEventSource(h);
488     }
489
490 #ifdef REGISTER_POWER_NOTIFICATIONS
491     /* create thread used to flush cache */
492     PowerNotificationThreadCreate();
493 #endif
494
495     /* allow an exit to be called prior to any initialization */
496     hInitHookDll = LoadLibrary(AFSD_HOOK_DLL);
497     if (hInitHookDll)
498     {
499         BOOL hookRc = FALSE;
500         initHook = ( AfsdInitHook ) GetProcAddress(hInitHookDll, AFSD_INIT_HOOK);
501         if (initHook)
502         {
503             hookRc = initHook();
504         }
505         FreeLibrary(hInitHookDll);
506         hInitHookDll = NULL;
507
508         if (hookRc == FALSE)
509         {
510             ServiceStatus.dwCurrentState = SERVICE_STOPPED;
511             ServiceStatus.dwWin32ExitCode = NO_ERROR;
512             ServiceStatus.dwCheckPoint = 0;
513             ServiceStatus.dwWaitHint = 0;
514             ServiceStatus.dwControlsAccepted = 0;
515             SetServiceStatus(StatusHandle, &ServiceStatus);
516                        
517             /* exit if initialization failed */
518             return;
519         }
520         else
521         {
522             /* allow another 15 seconds to start */
523             ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
524             ServiceStatus.dwServiceSpecificExitCode = 0;
525             ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
526             ServiceStatus.dwWin32ExitCode = NO_ERROR;
527             ServiceStatus.dwCheckPoint = 2;
528             ServiceStatus.dwWaitHint = 20000;
529             /* accept Power Events */
530             ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
531             SetServiceStatus(StatusHandle, &ServiceStatus);
532         }
533     }
534
535 #ifdef JUMP
536     MainThreadId = GetCurrentThreadId();
537         jmpret = setjmp(notifier_jmp);
538
539         if (jmpret == 0) 
540 #endif /* JUMP */
541     {
542                 code = afsd_InitCM(&reason);
543                 if (code != 0) {
544             afsi_log("afsd_InitCM failed: %s (code = %d)", reason, code);
545                         osi_panic(reason, __FILE__, __LINE__);
546         }
547
548 #ifndef NOTSERVICE
549         ServiceStatus.dwCheckPoint++;
550         ServiceStatus.dwWaitHint -= 5000;
551         SetServiceStatus(StatusHandle, &ServiceStatus);
552 #endif
553                 code = afsd_InitDaemons(&reason);
554                 if (code != 0) {
555             afsi_log("afsd_InitDaemons failed: %s (code = %d)", reason, code);
556                         osi_panic(reason, __FILE__, __LINE__);
557         }
558
559 #ifndef NOTSERVICE
560         ServiceStatus.dwCheckPoint++;
561         ServiceStatus.dwWaitHint -= 5000;
562         SetServiceStatus(StatusHandle, &ServiceStatus);
563 #endif
564                 code = afsd_InitSMB(&reason, MessageBox);
565                 if (code != 0) {
566             afsi_log("afsd_InitSMB failed: %s (code = %d)", reason, code);
567                         osi_panic(reason, __FILE__, __LINE__);
568         }
569
570         MountGlobalDrives();
571
572 #ifndef NOTSERVICE
573                 ServiceStatus.dwCurrentState = SERVICE_RUNNING;
574                 ServiceStatus.dwWin32ExitCode = NO_ERROR;
575                 ServiceStatus.dwCheckPoint = 0;
576                 ServiceStatus.dwWaitHint = 0;
577
578         /* accept Power events */
579                 ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
580                 SetServiceStatus(StatusHandle, &ServiceStatus);
581 #endif
582         {
583             HANDLE h; char *ptbuf[1];
584                 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
585                 ptbuf[0] = "AFS running";
586                 ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
587                 DeregisterEventSource(h);
588         }
589         }
590
591         WaitForSingleObject(WaitToTerminate, INFINITE);
592
593     {   
594     HANDLE h; char *ptbuf[1];
595         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
596         ptbuf[0] = "AFS quitting";
597         ReportEvent(h, GlobalStatus ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
598                 0, 0, NULL, 1, 0, ptbuf, NULL);
599     DeregisterEventSource(h);
600     }
601
602     DismountGlobalDrives();
603     smb_Shutdown();
604     rx_Finalize();
605
606 #ifdef  REGISTER_POWER_NOTIFICATIONS
607         /* terminate thread used to flush cache */
608         PowerNotificationThreadExit();
609 #endif
610
611     /* Remove the ExceptionFilter */
612     SetUnhandledExceptionFilter(NULL);
613
614     ServiceStatus.dwCurrentState = SERVICE_STOPPED;
615         ServiceStatus.dwWin32ExitCode = GlobalStatus ? ERROR_EXCEPTION_IN_SERVICE : NO_ERROR;
616         ServiceStatus.dwCheckPoint = 0;
617         ServiceStatus.dwWaitHint = 0;
618         ServiceStatus.dwControlsAccepted = 0;
619         SetServiceStatus(StatusHandle, &ServiceStatus);
620 }
621
622 DWORD __stdcall afsdMain_thread(void* notUsed)
623 {
624     char * argv[2] = {AFS_DAEMON_SERVICE_NAME, NULL};
625     afsd_Main(1, (LPTSTR*)argv);
626     return(0);
627 }
628
629 int
630 main(void)
631 {
632         static SERVICE_TABLE_ENTRY dispatchTable[] = {
633                 {AFS_DAEMON_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) afsd_Main},
634                 {NULL, NULL}
635         };
636
637         if (!StartServiceCtrlDispatcher(dispatchTable))
638     {
639         LONG status = GetLastError();
640             if (status == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
641         {
642             DWORD tid;
643             hAFSDMainThread = CreateThread(NULL, 0, afsdMain_thread, 0, 0, &tid);
644                 
645             printf("Hit <Enter> to terminate OpenAFS Client Service\n");
646             getchar();  
647             SetEvent(WaitToTerminate);
648         }
649     }
650
651     if ( hAFSDMainThread ) {
652         WaitForSingleObject( hAFSDMainThread, INFINITE );
653         CloseHandle( hAFSDMainThread );
654     }
655     return(0);
656 }