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