c01f2140ecaef69318c79d28edeff3d49ec037d1
[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 <softpub.h>
15 #include <psapi.h>
16 #include <winerror.h>
17 #include <string.h>
18 #include <setjmp.h>
19 #include "afsd.h"
20 #include "afsd_init.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <winsock2.h>
24
25 #include <osi.h>
26
27 #ifdef DEBUG
28 //#define NOTSERVICE
29 #endif
30 #ifdef _DEBUG
31 #include <crtdbg.h>
32 #endif
33
34 /*
35 // The following is defined if you want to receive Power notifications,
36 // including Hibernation, and also subsequent flushing of AFS volumes
37 //
38 #define REGISTER_POWER_NOTIFICATIONS 1
39 #define FLUSH_VOLUME                 1
40 //
41 // Check
42 */
43 #include "afsd_flushvol.h"
44
45 extern void afsi_log(char *pattern, ...);
46
47 HANDLE hAFSDMainThread = NULL;
48
49 HANDLE WaitToTerminate;
50
51 int GlobalStatus;
52
53 #ifdef JUMP
54 unsigned int MainThreadId;
55 jmp_buf notifier_jmp;
56 #endif /* JUMP */
57
58 extern int traceOnPanic;
59 extern HANDLE afsi_file;
60
61 int powerEventsRegistered = 0;
62
63 /*
64  * Notifier function for use by osi_panic
65  */
66 static void afsd_notifier(char *msgp, char *filep, long line)
67 {
68     char tbuffer[512];
69     char *ptbuf[1];
70     HANDLE h;
71
72     if (filep)
73         sprintf(tbuffer, "Error at file %s, line %d: %s",
74                  filep, line, msgp);
75     else
76         sprintf(tbuffer, "Error at unknown location: %s", msgp);
77
78     h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
79     ptbuf[0] = tbuffer;
80     ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, line, NULL, 1, 0, ptbuf, NULL);
81     DeregisterEventSource(h);
82
83     GlobalStatus = line;
84
85     osi_LogEnable(afsd_logp);
86
87     afsd_ForceTrace(TRUE);
88     buf_ForceTrace(TRUE);
89
90     afsi_log("--- begin dump ---");
91     cm_DumpSCache(afsi_file, "a");
92 #ifdef keisa
93     cm_dnlcDump(afsi_file, "a");
94 #endif
95     cm_DumpBufHashTable(afsi_file, "a");
96     smb_DumpVCP(afsi_file, "a");                        
97     afsi_log("--- end   dump ---");
98     
99     DebugBreak();       
100
101     SetEvent(WaitToTerminate);
102
103 #ifdef JUMP
104     if (GetCurrentThreadId() == MainThreadId)
105         longjmp(notifier_jmp, 1);
106     else
107 #endif /* JUMP */
108         ExitThread(1);
109 }
110
111 /*
112  * For use miscellaneously in smb.c; need to do better
113  */
114 static int _stdcall DummyMessageBox(HWND h, LPCTSTR l1, LPCTSTR l2, UINT ui)
115 {
116     return 0;
117 }
118
119 static SERVICE_STATUS           ServiceStatus;
120 static SERVICE_STATUS_HANDLE    StatusHandle;
121
122 DWORD
123 afsd_ServiceFlushVolume(DWORD dwlpEventData)
124 {
125     DWORD   dwRet = ERROR_NETWORK_BUSY; /* or NO_ERROR */
126
127     /*
128     **  If UI bit is not set, user interaction is not possible
129     **      BUT, since we are a NON-interactive service, and therefore
130     **  have NO user I/O, it doesn't much matter.
131     **  This benign code left here as example of how to find this out
132     */
133     BOOL bUI = (dwlpEventData & 1);
134
135     /* flush volume */
136     if ( PowerNotificationThreadNotify() )
137     {
138         dwRet = NO_ERROR;
139     }
140     else
141     {
142         /* flush was unsuccessful, or timeout - deny shutdown */
143         dwRet = ERROR_NETWORK_BUSY;
144     }
145
146     /*      to deny hibernate, simply return
147     //      any value besides NO_ERROR.
148     //      For example:
149     //      dwRet = ERROR_NETWORK_BUSY;
150     */
151
152     return dwRet;
153 }
154
155
156 /* service control handler used in nt4 only for backward compat. */
157 VOID WINAPI 
158 afsd_ServiceControlHandler(DWORD ctrlCode)
159 {
160     HKEY parmKey;
161     DWORD dummyLen, doTrace;
162     long code;
163
164     switch (ctrlCode) {
165     case SERVICE_CONTROL_STOP:
166         /* Shutdown RPC */
167         RpcMgmtStopServerListening(NULL);
168
169         /* Force trace if requested */
170         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
171                              AFSConfigKeyName,
172                              0, KEY_QUERY_VALUE, &parmKey);
173         if (code != ERROR_SUCCESS)
174             goto doneTrace;
175
176         dummyLen = sizeof(doTrace);
177         code = RegQueryValueEx(parmKey, "TraceOnShutdown",
178                                 NULL, NULL,
179                                 (BYTE *) &doTrace, &dummyLen);
180         RegCloseKey (parmKey);
181         if (code != ERROR_SUCCESS)
182             doTrace = 0;
183         if (doTrace) {
184             afsd_ForceTrace(FALSE);
185             buf_ForceTrace(FALSE);
186         }
187
188       doneTrace:
189         ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
190         ServiceStatus.dwWin32ExitCode = NO_ERROR;
191         ServiceStatus.dwCheckPoint = 1;
192         ServiceStatus.dwWaitHint = 10000;
193         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
194         SetServiceStatus(StatusHandle, &ServiceStatus);
195         SetEvent(WaitToTerminate);
196         break;
197     case SERVICE_CONTROL_INTERROGATE:
198         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
199         ServiceStatus.dwWin32ExitCode = NO_ERROR;
200         ServiceStatus.dwCheckPoint = 0;
201         ServiceStatus.dwWaitHint = 0;
202         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
203         SetServiceStatus(StatusHandle, &ServiceStatus);
204         break;
205         /* XXX handle system shutdown */
206         /* XXX handle pause & continue */
207     }
208 }       
209
210
211 /*
212 **    Extended ServiceControlHandler that provides Event types
213 **    for monitoring Power events, for example.
214 */
215 DWORD WINAPI
216 afsd_ServiceControlHandlerEx(
217               DWORD  ctrlCode,
218               DWORD  dwEventType,
219               LPVOID lpEventData,
220               LPVOID lpContext
221               )
222 {
223     HKEY parmKey;
224     DWORD dummyLen, doTrace;
225     long code;
226     DWORD dwRet = ERROR_CALL_NOT_IMPLEMENTED;
227
228     switch (ctrlCode) 
229     {
230     case SERVICE_CONTROL_STOP:
231         /* Shutdown RPC */
232         RpcMgmtStopServerListening(NULL);
233
234         /* Force trace if requested */
235         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
236                             AFSConfigKeyName,
237                             0, KEY_QUERY_VALUE, &parmKey);
238         if (code != ERROR_SUCCESS)
239             goto doneTrace;
240
241         dummyLen = sizeof(doTrace);
242         code = RegQueryValueEx(parmKey, "TraceOnShutdown",
243                                NULL, NULL,
244                                (BYTE *) &doTrace, &dummyLen);
245         RegCloseKey (parmKey);
246         if (code != ERROR_SUCCESS)
247             doTrace = 0;
248         if (doTrace) {
249             afsd_ForceTrace(FALSE);
250             buf_ForceTrace(FALSE);
251         }
252
253       doneTrace:
254         ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
255         ServiceStatus.dwWin32ExitCode = NO_ERROR;
256         ServiceStatus.dwCheckPoint = 1;
257         ServiceStatus.dwWaitHint = 10000;
258         ServiceStatus.dwControlsAccepted = 0;
259         SetServiceStatus(StatusHandle, &ServiceStatus);
260         SetEvent(WaitToTerminate);
261         dwRet = NO_ERROR;
262         break;
263
264     case SERVICE_CONTROL_INTERROGATE:
265         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
266         ServiceStatus.dwWin32ExitCode = NO_ERROR;
267         ServiceStatus.dwCheckPoint = 0;
268         ServiceStatus.dwWaitHint = 0;
269         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
270         SetServiceStatus(StatusHandle, &ServiceStatus);
271         dwRet = NO_ERROR;
272         break;
273
274         /* XXX handle system shutdown */
275         /* XXX handle pause & continue */
276     case SERVICE_CONTROL_POWEREVENT:                                              
277         {                                                                                     
278             /*                                                                                
279             **  dwEventType of this notification == WPARAM of WM_POWERBROADCAST               
280             **  Return NO_ERROR == return TRUE for that message, i.e. accept request          
281             **  Return any error code to deny request,                                        
282             **  i.e. as if returning BROADCAST_QUERY_DENY                                     
283             */                                                                                
284             if (powerEventsRegistered) {
285                 switch((int) dwEventType)                                                         
286                 {                                                                               
287                 case PBT_APMQUERYSUSPEND:                                                         
288                 case PBT_APMQUERYSTANDBY:                                                         
289
290 #ifdef  FLUSH_VOLUME
291                     /* handle event */                                                            
292                     dwRet = afsd_ServiceFlushVolume((DWORD) lpEventData);                         
293 #else                                                                                       
294                     dwRet = NO_ERROR;                                                             
295 #endif                                                                                      
296                     break;                                                                        
297                                                                                                                           
298                     /* allow remaining case PBT_WhatEver */                                           
299                 case PBT_APMSUSPEND:                                                              
300                 case PBT_APMSTANDBY:                                                              
301                 case PBT_APMRESUMECRITICAL:                                                       
302                 case PBT_APMRESUMESUSPEND:                                                        
303                 case PBT_APMRESUMESTANDBY:                                                        
304                 case PBT_APMBATTERYLOW:                                                           
305                 case PBT_APMPOWERSTATUSCHANGE:                                                    
306                 case PBT_APMOEMEVENT:                                                             
307                 case PBT_APMRESUMEAUTOMATIC:                                                      
308                 default:                                                                          
309                     dwRet = NO_ERROR;                                                             
310                 }   
311             }
312         }
313     }           /* end switch(ctrlCode) */                                                        
314     return dwRet;   
315 }
316
317 /* There is similar code in client_config\drivemap.cpp GlobalMountDrive()
318  * 
319  * Mount a drive into AFS if there global mapping
320  */
321 /* DEE Could check first if we are run as SYSTEM */
322 #define MAX_RETRIES 30
323 static void MountGlobalDrives(void)
324 {
325     char szAfsPath[_MAX_PATH];
326     char szDriveToMapTo[5];
327     DWORD dwResult;
328     char szKeyName[256];
329     HKEY hKey;
330     DWORD dwIndex = 0, dwRetry = 0;
331     DWORD dwDriveSize;
332     DWORD dwSubMountSize;
333     char szSubMount[256];
334     DWORD dwType;
335
336     sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
337
338     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
339     if (dwResult != ERROR_SUCCESS)
340         return;
341
342     while (dwRetry < MAX_RETRIES) {
343         dwDriveSize = sizeof(szDriveToMapTo);
344         dwSubMountSize = sizeof(szSubMount);
345         dwResult = RegEnumValue(hKey, dwIndex++, szDriveToMapTo, &dwDriveSize, 0, &dwType, szSubMount, &dwSubMountSize);
346         if (dwResult != ERROR_MORE_DATA) {
347             if (dwResult != ERROR_SUCCESS) {
348                 if (dwResult != ERROR_NO_MORE_ITEMS)
349                     afsi_log("Failed to read GlobalAutoMapper values: %d\n", dwResult);
350                 break;
351             }
352         }
353
354         for ( ; dwRetry < MAX_RETRIES; dwRetry++)
355                 {
356                     NETRESOURCE nr;
357                     memset (&nr, 0x00, sizeof(NETRESOURCE));
358  
359                     sprintf(szAfsPath,"\\\\%s\\%s",cm_NetbiosName,szSubMount);
360                     
361                     nr.dwScope = RESOURCE_GLOBALNET;              /* ignored parameter */
362                     nr.dwType=RESOURCETYPE_DISK;
363                     nr.lpLocalName=szDriveToMapTo;
364                     nr.lpRemoteName=szAfsPath;
365                     nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE; /* ignored parameter */
366                     nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;       /* ignored parameter */
367
368                     dwResult = WNetAddConnection2(&nr,NULL,NULL,0);
369             afsi_log("GlobalAutoMap of %s to %s %s (%d)", szDriveToMapTo, szSubMount, 
370                      (dwResult == NO_ERROR) ? "succeeded" : "failed", dwResult);
371             if (dwResult == NO_ERROR) {
372                 break;
373             }
374             /* wait for smb server to come up */
375             Sleep((DWORD)1000 /* miliseconds */);               
376
377             /* Disconnect any previous mappings */
378             dwResult = WNetCancelConnection2(szDriveToMapTo, 0, TRUE);
379         }
380     }        
381
382     RegCloseKey(hKey);
383 }
384
385 static void DismountGlobalDrives()
386 {
387     char szAfsPath[_MAX_PATH];
388     char szDriveToMapTo[5];
389     DWORD dwResult;
390     char szKeyName[256];
391     HKEY hKey;
392     DWORD dwIndex = 0;
393     DWORD dwDriveSize;
394     DWORD dwSubMountSize;
395     char szSubMount[256];
396     DWORD dwType;
397
398     sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
399
400     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
401     if (dwResult != ERROR_SUCCESS)
402         return;
403
404     while (1) {
405         dwDriveSize = sizeof(szDriveToMapTo);
406         dwSubMountSize = sizeof(szSubMount);
407         dwResult = RegEnumValue(hKey, dwIndex++, szDriveToMapTo, &dwDriveSize, 0, &dwType, szSubMount, &dwSubMountSize);
408         if (dwResult != ERROR_MORE_DATA) {
409             if (dwResult != ERROR_SUCCESS) {
410                 if (dwResult != ERROR_NO_MORE_ITEMS)
411                     afsi_log("Failed to read GlobalAutoMapper values: %d\n", dwResult);
412                 break;
413             }
414         }
415
416         sprintf(szAfsPath,"\\\\%s\\%s",cm_NetbiosName,szSubMount);
417                     
418         dwResult = WNetCancelConnection2(szDriveToMapTo, 0, TRUE);
419         dwResult = WNetCancelConnection(szAfsPath, TRUE);
420         
421         afsi_log("Disconnect from GlobalAutoMap of %s to %s %s", szDriveToMapTo, szSubMount, dwResult ? "succeeded" : "failed");
422     }        
423
424     RegCloseKey(hKey);
425 }
426
427 DWORD
428 GetVersionInfo( CHAR * filename, CHAR * szOutput, DWORD dwOutput )
429 {
430     DWORD dwVersionHandle;
431     LPVOID pVersionInfo = 0;
432     DWORD retval = 0;
433     LPDWORD pLangInfo = 0;
434     LPTSTR szVersion = 0;
435     UINT len = 0;
436     TCHAR szVerQ[] = TEXT("\\StringFileInfo\\12345678\\FileVersion");
437     DWORD size = GetFileVersionInfoSize(filename, &dwVersionHandle);
438
439     if (!size) {
440         afsi_log("GetFileVersionInfoSize failed");
441         return GetLastError();
442     }
443
444     pVersionInfo = malloc(size);
445     if (!pVersionInfo) {
446         afsi_log("out of memory 1");
447         return ERROR_NOT_ENOUGH_MEMORY;
448     }
449
450     GetFileVersionInfo(filename, dwVersionHandle, size, pVersionInfo);
451     if (retval = GetLastError())
452     {
453         afsi_log("GetFileVersionInfo failed: %d", retval);
454         goto cleanup;
455     }
456
457     VerQueryValue(pVersionInfo, TEXT("\\VarFileInfo\\Translation"),
458                        (LPVOID*)&pLangInfo, &len);
459     if (retval = GetLastError())
460     {
461         afsi_log("VerQueryValue 1 failed: %d", retval);
462         goto cleanup;
463     }
464
465     wsprintf(szVerQ,
466              TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"),
467              LOWORD(*pLangInfo), HIWORD(*pLangInfo));
468
469     VerQueryValue(pVersionInfo, szVerQ, (LPVOID*)&szVersion, &len);
470     if (retval = GetLastError())
471     {
472         /* try again with language 409 since the old binaries were tagged wrong */
473         wsprintf(szVerQ,
474                   TEXT("\\StringFileInfo\\0409%04x\\FileVersion"),
475                   HIWORD(*pLangInfo));
476
477         VerQueryValue(pVersionInfo, szVerQ, (LPVOID*)&szVersion, &len);
478         if (retval = GetLastError()) {
479             afsi_log("VerQueryValue 2 failed: [%s] %d", szVerQ, retval);
480             goto cleanup;
481         }
482     }
483     snprintf(szOutput, dwOutput, TEXT("%s"), szVersion);
484     szOutput[dwOutput - 1] = 0;
485
486  cleanup:
487     if (pVersionInfo)
488         free(pVersionInfo);
489
490     return retval;
491 }
492
493 BOOL VerifyTrust(CHAR * filename)
494 {
495     WINTRUST_DATA fTrust;
496     WINTRUST_FILE_INFO finfo;
497     GUID trustAction = WINTRUST_ACTION_GENERIC_VERIFY_V2;
498     GUID subject = WIN_TRUST_SUBJTYPE_RAW_FILEEX;
499     wchar_t wfilename[260];
500     LONG ret;
501
502     if (filename == NULL ) 
503         return FALSE;
504
505     mbstowcs(wfilename, filename, 260);
506
507     finfo.cbStruct = sizeof(finfo);
508     finfo.pcwszFilePath= wfilename;
509     finfo.hFile = INVALID_HANDLE_VALUE;
510     finfo.pgKnownSubject = &subject;
511
512     fTrust.cbStruct = sizeof(fTrust);
513     fTrust.pPolicyCallbackData = NULL;
514     fTrust.pSIPClientData = NULL;
515     fTrust.dwUIChoice = WTD_UI_NONE;
516     fTrust.fdwRevocationChecks = WTD_REVOKE_NONE;
517     fTrust.dwUnionChoice = WTD_CHOICE_FILE;
518     fTrust.pFile = &finfo;
519     fTrust.dwStateAction = WTD_STATEACTION_IGNORE;
520     fTrust.hWVTStateData = NULL;
521     fTrust.pwszURLReference = NULL;
522     fTrust.dwProvFlags = WTD_SAFER_FLAG | WTD_REVOCATION_CHECK_NONE;
523     fTrust.dwUIContext = WTD_UICONTEXT_EXECUTE;
524     
525     ret = WinVerifyTrust(INVALID_HANDLE_VALUE, &trustAction, &fTrust);
526
527     if (ret == ERROR_SUCCESS) {
528         return TRUE;
529     } else {
530         DWORD gle = GetLastError();
531         switch (gle) {
532         case TRUST_E_PROVIDER_UNKNOWN:
533             afsi_log("VerifyTrust failed: \"Generic Verify V2\" Provider Unknown");
534             break;  
535         case TRUST_E_NOSIGNATURE:
536             afsi_log("VerifyTrust failed: Unsigned executable");
537             break;
538         case TRUST_E_EXPLICIT_DISTRUST:
539             afsi_log("VerifyTrust failed: Certificate Marked as Untrusted by the user");
540             break;
541         case TRUST_E_SUBJECT_NOT_TRUSTED:
542             afsi_log("VerifyTrust failed: File is not trusted");
543             break;
544         case CRYPT_E_SECURITY_SETTINGS:
545             afsi_log("VerifyTrust failed: local security options prevent verification");
546             break;
547         default:
548             afsi_log("VerifyTrust failed: 0x%X", GetLastError());
549         }
550         return FALSE;
551     }
552 }
553
554 BOOL AFSModulesVerify(void)
555 {
556     CHAR filename[1024];
557     CHAR afsdVersion[128];
558     CHAR modVersion[128];
559     CHAR checkName[1024];
560     BOOL trustVerified = FALSE;
561     HMODULE hMods[1024];
562     HANDLE hProcess;
563     DWORD cbNeeded;
564     unsigned int i;
565     BOOL success = TRUE;
566
567     if (!GetModuleFileName(NULL, filename, sizeof(filename)))
568         return FALSE;
569
570     if (GetVersionInfo(filename, afsdVersion, sizeof(afsdVersion)))
571         return FALSE;
572
573     afsi_log("%s version %s", filename, afsdVersion);
574
575     trustVerified = VerifyTrust(filename);
576
577     // Get a list of all the modules in this process.
578     hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
579                            FALSE, GetCurrentProcessId());
580
581     if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
582     {
583         afsi_log("Num of Process Modules: %d", (cbNeeded / sizeof(HMODULE)));
584
585         for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
586         {
587             char szModName[2048];
588
589             // Get the full path to the module's file.
590             if (GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName)))
591             {
592                 lstrcpy(checkName, szModName);
593                 strlwr(checkName);
594
595                 if ( strstr(checkName, "afspthread.dll") ||
596                      strstr(checkName, "afsauthent.dll") ||
597                      strstr(checkName, "afsrpc.dll") ||
598                      strstr(checkName, "libafsconf.dll") ||
599                      strstr(checkName, "libosi.dll") )
600                 {
601                     if (GetVersionInfo(szModName, modVersion, sizeof(modVersion))) {
602                         success = FALSE;
603                         continue;
604                     }
605
606                     afsi_log("%s version %s", szModName, modVersion);
607                     if (strcmp(afsdVersion,modVersion)) {
608                         afsi_log("Version mismatch: %s", szModName);
609                         success = FALSE;
610                     }
611                     if ( trustVerified && !VerifyTrust(szModName) ) {
612                         afsi_log("Signature Verification failed: %s", szModName);
613                         success = FALSE;
614                     }
615                 }
616             }
617         }
618     }
619
620     CloseHandle(hProcess);
621     return success;
622 }
623
624 typedef BOOL ( APIENTRY * AfsdInitHook )(void);
625 #define AFSD_INIT_HOOK "AfsdInitHook"
626 #define AFSD_HOOK_DLL  "afsdhook.dll"
627
628 /*
629 control serviceex exists only on 2000/xp. These functions will be loaded dynamically.
630 */
631
632 typedef SERVICE_STATUS_HANDLE ( * RegisterServiceCtrlHandlerExFunc )(  LPCTSTR , LPHANDLER_FUNCTION_EX , LPVOID );
633 typedef SERVICE_STATUS_HANDLE ( * RegisterServiceCtrlHandlerFunc   )(  LPCTSTR ,  LPHANDLER_FUNCTION );
634
635 RegisterServiceCtrlHandlerExFunc pRegisterServiceCtrlHandlerEx = NULL;
636 RegisterServiceCtrlHandlerFunc   pRegisterServiceCtrlHandler   = NULL; 
637
638 void afsd_Main(DWORD argc, LPTSTR *argv)
639 {
640     long code;
641     char *reason;
642 #ifdef JUMP
643     int jmpret;
644 #endif /* JUMP */
645     HANDLE hInitHookDll;
646     HANDLE hAdvApi32;
647     AfsdInitHook initHook;
648
649 #ifdef _DEBUG
650     _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF /*| _CRTDBG_CHECK_ALWAYS_DF*/ | 
651                    _CRTDBG_CHECK_CRT_DF /* | _CRTDBG_DELAY_FREE_MEM_DF */ );
652 #endif 
653
654     osi_InitPanic(afsd_notifier);
655     osi_InitTraceOption();
656
657     GlobalStatus = 0;
658
659     afsi_start();
660
661     WaitToTerminate = CreateEvent(NULL, TRUE, FALSE, TEXT("afsd_service_WaitToTerminate"));
662     if ( GetLastError() == ERROR_ALREADY_EXISTS )
663         afsi_log("Event Object Already Exists: %s", TEXT("afsd_service_WaitToTerminate"));
664
665 #ifndef NOTSERVICE
666     hAdvApi32 = LoadLibrary("advapi32.dll");
667     if (hAdvApi32 == NULL)
668     {
669         afsi_log("Fatal: cannot load advapi32.dll");
670         return;
671     }
672
673     pRegisterServiceCtrlHandlerEx = (RegisterServiceCtrlHandlerExFunc)GetProcAddress(hAdvApi32, "RegisterServiceCtrlHandlerExA");
674     if (pRegisterServiceCtrlHandlerEx)
675     {
676         afsi_log("running on 2000+ - using RegisterServiceCtrlHandlerEx");
677         StatusHandle = RegisterServiceCtrlHandlerEx(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandlerEx, NULL );
678     }
679     else
680     {
681         StatusHandle = RegisterServiceCtrlHandler(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandler);
682     }
683
684     ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
685     ServiceStatus.dwServiceSpecificExitCode = 0;
686     ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
687     ServiceStatus.dwWin32ExitCode = NO_ERROR;
688     ServiceStatus.dwCheckPoint = 1;
689     ServiceStatus.dwWaitHint = 30000;
690     /* accept Power Events */
691     ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
692     SetServiceStatus(StatusHandle, &ServiceStatus);
693 #endif
694
695     {       
696     HANDLE h; char *ptbuf[1];
697     h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
698     ptbuf[0] = "AFS start pending";
699     ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
700     DeregisterEventSource(h);
701     }
702
703 #ifdef REGISTER_POWER_NOTIFICATIONS
704     {
705         HKEY hkParm;
706         DWORD code;
707         DWORD dummyLen;
708         int bpower = TRUE;
709
710         /* see if we should handle power notifications */
711         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName, 0, KEY_QUERY_VALUE, &hkParm);
712         if (code == ERROR_SUCCESS) {
713             dummyLen = sizeof(bpower);
714             code = RegQueryValueEx(hkParm, "FlushOnHibernate", NULL, NULL,
715                 (BYTE *) &bpower, &dummyLen);      
716
717             if(code != ERROR_SUCCESS)
718                 bpower = TRUE;
719
720             RegCloseKey(hkParm);
721         }
722         /* create thread used to flush cache */
723         if (bpower) {
724             PowerNotificationThreadCreate();
725             powerEventsRegistered = 1;
726         }
727     }
728 #endif
729
730     /* Verify the versions of the DLLs which were loaded */
731     if (!AFSModulesVerify()) {
732         ServiceStatus.dwCurrentState = SERVICE_STOPPED;
733         ServiceStatus.dwWin32ExitCode = NO_ERROR;
734         ServiceStatus.dwCheckPoint = 0;
735         ServiceStatus.dwWaitHint = 0;
736         ServiceStatus.dwControlsAccepted = 0;
737         SetServiceStatus(StatusHandle, &ServiceStatus);
738                        
739         /* exit if initialization failed */
740         return;
741     }
742
743     /* allow an exit to be called prior to any initialization */
744     hInitHookDll = LoadLibrary(AFSD_HOOK_DLL);
745     if (hInitHookDll)
746     {
747         BOOL hookRc = FALSE;
748         initHook = ( AfsdInitHook ) GetProcAddress(hInitHookDll, AFSD_INIT_HOOK);
749         if (initHook)
750         {
751             hookRc = initHook();
752         }
753         FreeLibrary(hInitHookDll);
754         hInitHookDll = NULL;
755
756         if (hookRc == FALSE)
757         {
758             ServiceStatus.dwCurrentState = SERVICE_STOPPED;
759             ServiceStatus.dwWin32ExitCode = NO_ERROR;
760             ServiceStatus.dwCheckPoint = 0;
761             ServiceStatus.dwWaitHint = 0;
762             ServiceStatus.dwControlsAccepted = 0;
763             SetServiceStatus(StatusHandle, &ServiceStatus);
764                        
765             /* exit if initialization failed */
766             return;
767         }
768         else
769         {
770             /* allow another 15 seconds to start */
771             ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
772             ServiceStatus.dwServiceSpecificExitCode = 0;
773             ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
774             ServiceStatus.dwWin32ExitCode = NO_ERROR;
775             ServiceStatus.dwCheckPoint = 2;
776             ServiceStatus.dwWaitHint = 20000;
777             /* accept Power Events */
778             ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
779             SetServiceStatus(StatusHandle, &ServiceStatus);
780         }
781     }
782
783 #ifdef JUMP
784     MainThreadId = GetCurrentThreadId();
785     jmpret = setjmp(notifier_jmp);
786
787     if (jmpret == 0) 
788 #endif /* JUMP */
789     {
790         code = afsd_InitCM(&reason);
791         if (code != 0) {
792             afsi_log("afsd_InitCM failed: %s (code = %d)", reason, code);
793             osi_panic(reason, __FILE__, __LINE__);
794         }
795
796 #ifndef NOTSERVICE
797         ServiceStatus.dwCheckPoint++;
798         ServiceStatus.dwWaitHint -= 5000;
799         SetServiceStatus(StatusHandle, &ServiceStatus);
800 #endif
801         code = afsd_InitDaemons(&reason);
802         if (code != 0) {
803             afsi_log("afsd_InitDaemons failed: %s (code = %d)", reason, code);
804                         osi_panic(reason, __FILE__, __LINE__);
805         }
806
807 #ifndef NOTSERVICE
808         ServiceStatus.dwCheckPoint++;
809         ServiceStatus.dwWaitHint -= 5000;
810         SetServiceStatus(StatusHandle, &ServiceStatus);
811 #endif
812         code = afsd_InitSMB(&reason, MessageBox);
813         if (code != 0) {
814             afsi_log("afsd_InitSMB failed: %s (code = %d)", reason, code);
815             osi_panic(reason, __FILE__, __LINE__);
816         }
817
818         MountGlobalDrives();
819
820 #ifndef NOTSERVICE
821         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
822         ServiceStatus.dwWin32ExitCode = NO_ERROR;
823         ServiceStatus.dwCheckPoint = 0;
824         ServiceStatus.dwWaitHint = 0;
825
826         /* accept Power events */
827         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
828         SetServiceStatus(StatusHandle, &ServiceStatus);
829 #endif  
830         {
831             HANDLE h; char *ptbuf[1];
832             h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
833             ptbuf[0] = "AFS running";
834             ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
835             DeregisterEventSource(h);
836         }
837     }
838
839     WaitForSingleObject(WaitToTerminate, INFINITE);
840
841     {   
842         HANDLE h; char *ptbuf[1];
843         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
844         ptbuf[0] = "AFS quitting";
845         ReportEvent(h, GlobalStatus ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
846                 0, 0, NULL, 1, 0, ptbuf, NULL);
847         DeregisterEventSource(h);
848     }
849
850     DismountGlobalDrives();
851     smb_Shutdown();
852     rx_Finalize();
853
854 #ifdef  REGISTER_POWER_NOTIFICATIONS
855     /* terminate thread used to flush cache */
856     if (powerEventsRegistered)
857         PowerNotificationThreadExit();
858 #endif
859
860     /* Remove the ExceptionFilter */
861     SetUnhandledExceptionFilter(NULL);
862
863     ServiceStatus.dwCurrentState = SERVICE_STOPPED;
864     ServiceStatus.dwWin32ExitCode = GlobalStatus ? ERROR_EXCEPTION_IN_SERVICE : NO_ERROR;
865     ServiceStatus.dwCheckPoint = 0;
866     ServiceStatus.dwWaitHint = 0;
867     ServiceStatus.dwControlsAccepted = 0;
868     SetServiceStatus(StatusHandle, &ServiceStatus);
869 }       
870
871 DWORD __stdcall afsdMain_thread(void* notUsed)
872 {
873     char * argv[2] = {AFS_DAEMON_SERVICE_NAME, NULL};
874     afsd_Main(1, (LPTSTR*)argv);
875     return(0);
876 }
877
878 int
879 main(void)
880 {
881     static SERVICE_TABLE_ENTRY dispatchTable[] = {
882         {AFS_DAEMON_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) afsd_Main},
883         {NULL, NULL}
884     };
885
886     if (!StartServiceCtrlDispatcher(dispatchTable))
887     {
888         LONG status = GetLastError();
889         if (status == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
890         {
891             DWORD tid;
892             hAFSDMainThread = CreateThread(NULL, 0, afsdMain_thread, 0, 0, &tid);
893                 
894             printf("Hit <Enter> to terminate OpenAFS Client Service\n");
895             getchar();  
896             SetEvent(WaitToTerminate);
897         }
898     }
899
900     if ( hAFSDMainThread ) {
901         WaitForSingleObject( hAFSDMainThread, INFINITE );
902         CloseHandle( hAFSDMainThread );
903     }
904     return(0);
905 }