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