windows-virtual-memory-20041224
[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 static SERVICE_STATUS           ServiceStatus;
48 static SERVICE_STATUS_HANDLE    StatusHandle;
49
50 HANDLE hAFSDMainThread = NULL;
51
52 HANDLE WaitToTerminate;
53
54 int GlobalStatus;
55
56 #ifdef JUMP
57 unsigned int MainThreadId;
58 jmp_buf notifier_jmp;
59 #endif /* JUMP */
60
61 extern int traceOnPanic;
62 extern HANDLE afsi_file;
63
64 int powerEventsRegistered = 0;
65
66 /*
67  * Notifier function for use by osi_panic
68  */
69 static void afsd_notifier(char *msgp, char *filep, long line)
70 {
71     char tbuffer[512];
72     char *ptbuf[1];
73     HANDLE h;
74
75     if (filep)
76         sprintf(tbuffer, "Error at file %s, line %d: %s",
77                  filep, line, msgp);
78     else
79         sprintf(tbuffer, "Error at unknown location: %s", msgp);
80
81     h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
82     ptbuf[0] = tbuffer;
83     ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, line, NULL, 1, 0, ptbuf, NULL);
84     DeregisterEventSource(h);
85
86     GlobalStatus = line;
87
88     osi_LogEnable(afsd_logp);
89
90     afsd_ForceTrace(TRUE);
91     buf_ForceTrace(TRUE);
92
93     afsi_log("--- begin dump ---");
94     cm_DumpSCache(afsi_file, "a");
95 #ifdef keisa
96     cm_dnlcDump(afsi_file, "a");
97 #endif
98     cm_DumpBufHashTable(afsi_file, "a");
99     smb_DumpVCP(afsi_file, "a");                        
100     afsi_log("--- end   dump ---");
101     
102 #ifdef DEBUG
103     DebugBreak();       
104 #endif
105
106     SetEvent(WaitToTerminate);
107
108 #ifdef JUMP
109     if (GetCurrentThreadId() == MainThreadId)
110         longjmp(notifier_jmp, 1);
111 #endif /* JUMP */
112
113     ServiceStatus.dwCurrentState = SERVICE_STOPPED;
114     ServiceStatus.dwWin32ExitCode = NO_ERROR;
115     ServiceStatus.dwCheckPoint = 0;
116     ServiceStatus.dwWaitHint = 0;
117     ServiceStatus.dwControlsAccepted = 0;
118     SetServiceStatus(StatusHandle, &ServiceStatus);
119
120     exit(1);
121 }
122
123 /*
124  * For use miscellaneously in smb.c; need to do better
125  */
126 static int _stdcall DummyMessageBox(HWND h, LPCTSTR l1, LPCTSTR l2, UINT ui)
127 {
128     return 0;
129 }
130
131 DWORD
132 afsd_ServiceFlushVolume(DWORD dwlpEventData)
133 {
134     DWORD   dwRet = ERROR_NETWORK_BUSY; /* or NO_ERROR */
135
136     /*
137     **  If UI bit is not set, user interaction is not possible
138     **      BUT, since we are a NON-interactive service, and therefore
139     **  have NO user I/O, it doesn't much matter.
140     **  This benign code left here as example of how to find this out
141     */
142     BOOL bUI = (dwlpEventData & 1);
143
144     /* flush volume */
145     if ( PowerNotificationThreadNotify() )
146     {
147         dwRet = NO_ERROR;
148     }
149     else
150     {
151         /* flush was unsuccessful, or timeout - deny shutdown */
152         dwRet = ERROR_NETWORK_BUSY;
153     }
154
155     /*      to deny hibernate, simply return
156     //      any value besides NO_ERROR.
157     //      For example:
158     //      dwRet = ERROR_NETWORK_BUSY;
159     */
160
161     return dwRet;
162 }
163
164
165 /* service control handler used in nt4 only for backward compat. */
166 VOID WINAPI 
167 afsd_ServiceControlHandler(DWORD ctrlCode)
168 {
169     HKEY parmKey;
170     DWORD dummyLen, doTrace;
171     long code;
172
173     switch (ctrlCode) {
174     case SERVICE_CONTROL_STOP:
175         /* Shutdown RPC */
176         RpcMgmtStopServerListening(NULL);
177
178         /* Force trace if requested */
179         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
180                              AFSConfigKeyName,
181                              0, KEY_QUERY_VALUE, &parmKey);
182         if (code != ERROR_SUCCESS)
183             goto doneTrace;
184
185         dummyLen = sizeof(doTrace);
186         code = RegQueryValueEx(parmKey, "TraceOnShutdown",
187                                 NULL, NULL,
188                                 (BYTE *) &doTrace, &dummyLen);
189         RegCloseKey (parmKey);
190         if (code != ERROR_SUCCESS)
191             doTrace = 0;
192         if (doTrace) {
193             afsd_ForceTrace(FALSE);
194             buf_ForceTrace(FALSE);
195         }
196
197       doneTrace:
198         ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
199         ServiceStatus.dwWin32ExitCode = NO_ERROR;
200         ServiceStatus.dwCheckPoint = 1;
201         ServiceStatus.dwWaitHint = 10000;
202         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
203         SetServiceStatus(StatusHandle, &ServiceStatus);
204         SetEvent(WaitToTerminate);
205         break;
206     case SERVICE_CONTROL_INTERROGATE:
207         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
208         ServiceStatus.dwWin32ExitCode = NO_ERROR;
209         ServiceStatus.dwCheckPoint = 0;
210         ServiceStatus.dwWaitHint = 0;
211         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
212         SetServiceStatus(StatusHandle, &ServiceStatus);
213         break;
214         /* XXX handle system shutdown */
215         /* XXX handle pause & continue */
216     }
217 }       
218
219
220 /*
221 **    Extended ServiceControlHandler that provides Event types
222 **    for monitoring Power events, for example.
223 */
224 DWORD WINAPI
225 afsd_ServiceControlHandlerEx(
226               DWORD  ctrlCode,
227               DWORD  dwEventType,
228               LPVOID lpEventData,
229               LPVOID lpContext
230               )
231 {
232     HKEY parmKey;
233     DWORD dummyLen, doTrace;
234     long code;
235     DWORD dwRet = ERROR_CALL_NOT_IMPLEMENTED;
236
237     switch (ctrlCode) 
238     {
239     case SERVICE_CONTROL_STOP:
240         /* Shutdown RPC */
241         RpcMgmtStopServerListening(NULL);
242
243         /* Force trace if requested */
244         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
245                             AFSConfigKeyName,
246                             0, KEY_QUERY_VALUE, &parmKey);
247         if (code != ERROR_SUCCESS)
248             goto doneTrace;
249
250         dummyLen = sizeof(doTrace);
251         code = RegQueryValueEx(parmKey, "TraceOnShutdown",
252                                NULL, NULL,
253                                (BYTE *) &doTrace, &dummyLen);
254         RegCloseKey (parmKey);
255         if (code != ERROR_SUCCESS)
256             doTrace = 0;
257         if (doTrace) {
258             afsd_ForceTrace(FALSE);
259             buf_ForceTrace(FALSE);
260         }
261
262       doneTrace:
263         ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
264         ServiceStatus.dwWin32ExitCode = NO_ERROR;
265         ServiceStatus.dwCheckPoint = 1;
266         ServiceStatus.dwWaitHint = 10000;
267         ServiceStatus.dwControlsAccepted = 0;
268         SetServiceStatus(StatusHandle, &ServiceStatus);
269         SetEvent(WaitToTerminate);
270         dwRet = NO_ERROR;
271         break;
272
273     case SERVICE_CONTROL_INTERROGATE:
274         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
275         ServiceStatus.dwWin32ExitCode = NO_ERROR;
276         ServiceStatus.dwCheckPoint = 0;
277         ServiceStatus.dwWaitHint = 0;
278         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
279         SetServiceStatus(StatusHandle, &ServiceStatus);
280         dwRet = NO_ERROR;
281         break;
282
283         /* XXX handle system shutdown */
284         /* XXX handle pause & continue */
285     case SERVICE_CONTROL_POWEREVENT:                                              
286         {                                                                                     
287             /*                                                                                
288             **  dwEventType of this notification == WPARAM of WM_POWERBROADCAST               
289             **  Return NO_ERROR == return TRUE for that message, i.e. accept request          
290             **  Return any error code to deny request,                                        
291             **  i.e. as if returning BROADCAST_QUERY_DENY                                     
292             */                                                                                
293             if (powerEventsRegistered) {
294                 switch((int) dwEventType)                                                         
295                 {                                                                               
296                 case PBT_APMQUERYSUSPEND:                                                         
297                 case PBT_APMQUERYSTANDBY:                                                         
298
299 #ifdef  FLUSH_VOLUME
300                     /* handle event */                                                            
301                     dwRet = afsd_ServiceFlushVolume((DWORD) lpEventData);                         
302 #else                                                                                       
303                     dwRet = NO_ERROR;                                                             
304 #endif                                                                                      
305                     break;                                                                        
306                                                                                                                           
307                     /* allow remaining case PBT_WhatEver */                                           
308                 case PBT_APMSUSPEND:                                                              
309                 case PBT_APMSTANDBY:                                                              
310                 case PBT_APMRESUMECRITICAL:                                                       
311                 case PBT_APMRESUMESUSPEND:                                                        
312                 case PBT_APMRESUMESTANDBY:                                                        
313                 case PBT_APMBATTERYLOW:                                                           
314                 case PBT_APMPOWERSTATUSCHANGE:                                                    
315                 case PBT_APMOEMEVENT:                                                             
316                 case PBT_APMRESUMEAUTOMATIC:                                                      
317                 default:                                                                          
318                     dwRet = NO_ERROR;                                                             
319                 }   
320             }
321         }
322     }           /* end switch(ctrlCode) */                                                        
323     return dwRet;   
324 }
325
326 /* There is similar code in client_config\drivemap.cpp GlobalMountDrive()
327  * 
328  * Mount a drive into AFS if there global mapping
329  */
330 /* DEE Could check first if we are run as SYSTEM */
331 #define MAX_RETRIES 30
332 static void MountGlobalDrives(void)
333 {
334     char szAfsPath[_MAX_PATH];
335     char szDriveToMapTo[5];
336     DWORD dwResult;
337     char szKeyName[256];
338     HKEY hKey;
339     DWORD dwIndex = 0, dwRetry = 0;
340     DWORD dwDriveSize;
341     DWORD dwSubMountSize;
342     char szSubMount[256];
343     DWORD dwType;
344
345     sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
346
347     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
348     if (dwResult != ERROR_SUCCESS)
349         return;
350
351     while (dwRetry < MAX_RETRIES) {
352         dwDriveSize = sizeof(szDriveToMapTo);
353         dwSubMountSize = sizeof(szSubMount);
354         dwResult = RegEnumValue(hKey, dwIndex++, szDriveToMapTo, &dwDriveSize, 0, &dwType, szSubMount, &dwSubMountSize);
355         if (dwResult != ERROR_MORE_DATA) {
356             if (dwResult != ERROR_SUCCESS) {
357                 if (dwResult != ERROR_NO_MORE_ITEMS)
358                     afsi_log("Failed to read GlobalAutoMapper values: %d\n", dwResult);
359                 break;
360             }
361         }
362
363         for ( ; dwRetry < MAX_RETRIES; dwRetry++)
364                 {
365                     NETRESOURCE nr;
366                     memset (&nr, 0x00, sizeof(NETRESOURCE));
367  
368                     sprintf(szAfsPath,"\\\\%s\\%s",cm_NetbiosName,szSubMount);
369                     
370                     nr.dwScope = RESOURCE_GLOBALNET;              /* ignored parameter */
371                     nr.dwType=RESOURCETYPE_DISK;
372                     nr.lpLocalName=szDriveToMapTo;
373                     nr.lpRemoteName=szAfsPath;
374                     nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE; /* ignored parameter */
375                     nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;       /* ignored parameter */
376
377                     dwResult = WNetAddConnection2(&nr,NULL,NULL,0);
378             afsi_log("GlobalAutoMap of %s to %s %s (%d)", szDriveToMapTo, szSubMount, 
379                      (dwResult == NO_ERROR) ? "succeeded" : "failed", dwResult);
380             if (dwResult == NO_ERROR) {
381                 break;
382             }
383             /* wait for smb server to come up */
384             Sleep((DWORD)1000 /* miliseconds */);               
385
386             /* Disconnect any previous mappings */
387             dwResult = WNetCancelConnection2(szDriveToMapTo, 0, TRUE);
388         }
389     }        
390
391     RegCloseKey(hKey);
392 }
393
394 static void DismountGlobalDrives()
395 {
396     char szAfsPath[_MAX_PATH];
397     char szDriveToMapTo[5];
398     DWORD dwResult;
399     char szKeyName[256];
400     HKEY hKey;
401     DWORD dwIndex = 0;
402     DWORD dwDriveSize;
403     DWORD dwSubMountSize;
404     char szSubMount[256];
405     DWORD dwType;
406
407     sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
408
409     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
410     if (dwResult != ERROR_SUCCESS)
411         return;
412
413     while (1) {
414         dwDriveSize = sizeof(szDriveToMapTo);
415         dwSubMountSize = sizeof(szSubMount);
416         dwResult = RegEnumValue(hKey, dwIndex++, szDriveToMapTo, &dwDriveSize, 0, &dwType, szSubMount, &dwSubMountSize);
417         if (dwResult != ERROR_MORE_DATA) {
418             if (dwResult != ERROR_SUCCESS) {
419                 if (dwResult != ERROR_NO_MORE_ITEMS)
420                     afsi_log("Failed to read GlobalAutoMapper values: %d\n", dwResult);
421                 break;
422             }
423         }
424
425         sprintf(szAfsPath,"\\\\%s\\%s",cm_NetbiosName,szSubMount);
426                     
427         dwResult = WNetCancelConnection2(szDriveToMapTo, 0, TRUE);
428         dwResult = WNetCancelConnection(szAfsPath, TRUE);
429         
430         afsi_log("Disconnect from GlobalAutoMap of %s to %s %s", szDriveToMapTo, szSubMount, dwResult ? "succeeded" : "failed");
431     }        
432
433     RegCloseKey(hKey);
434 }
435
436 DWORD
437 GetVersionInfo( CHAR * filename, CHAR * szOutput, DWORD dwOutput )
438 {
439     DWORD dwVersionHandle;
440     LPVOID pVersionInfo = 0;
441     DWORD retval = 0;
442     LPDWORD pLangInfo = 0;
443     LPTSTR szVersion = 0;
444     UINT len = 0;
445     TCHAR szVerQ[] = TEXT("\\StringFileInfo\\12345678\\FileVersion");
446     DWORD size = GetFileVersionInfoSize(filename, &dwVersionHandle);
447
448     if (!size) {
449         afsi_log("GetFileVersionInfoSize failed");
450         return GetLastError();
451     }
452
453     pVersionInfo = malloc(size);
454     if (!pVersionInfo) {
455         afsi_log("out of memory 1");
456         return ERROR_NOT_ENOUGH_MEMORY;
457     }
458
459     GetFileVersionInfo(filename, dwVersionHandle, size, pVersionInfo);
460     if (retval = GetLastError())
461     {
462         afsi_log("GetFileVersionInfo failed: %d", retval);
463         goto cleanup;
464     }
465
466     VerQueryValue(pVersionInfo, TEXT("\\VarFileInfo\\Translation"),
467                        (LPVOID*)&pLangInfo, &len);
468     if (retval = GetLastError())
469     {
470         afsi_log("VerQueryValue 1 failed: %d", retval);
471         goto cleanup;
472     }
473
474     wsprintf(szVerQ,
475              TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"),
476              LOWORD(*pLangInfo), HIWORD(*pLangInfo));
477
478     VerQueryValue(pVersionInfo, szVerQ, (LPVOID*)&szVersion, &len);
479     if (retval = GetLastError())
480     {
481         /* try again with language 409 since the old binaries were tagged wrong */
482         wsprintf(szVerQ,
483                   TEXT("\\StringFileInfo\\0409%04x\\FileVersion"),
484                   HIWORD(*pLangInfo));
485
486         VerQueryValue(pVersionInfo, szVerQ, (LPVOID*)&szVersion, &len);
487         if (retval = GetLastError()) {
488             afsi_log("VerQueryValue 2 failed: [%s] %d", szVerQ, retval);
489             goto cleanup;
490         }
491     }
492     snprintf(szOutput, dwOutput, TEXT("%s"), szVersion);
493     szOutput[dwOutput - 1] = 0;
494
495  cleanup:
496     if (pVersionInfo)
497         free(pVersionInfo);
498
499     return retval;
500 }
501
502 static HINSTANCE hCrypt32;
503 static DWORD (WINAPI *pCertGetNameString)(PCCERT_CONTEXT pCertContext,  DWORD dwType,  DWORD dwFlags,
504                                           void* pvTypePara, LPTSTR pszNameString, DWORD cchNameString);
505 static BOOL (WINAPI *pCryptQueryObject)(DWORD dwObjectType, const void* pvObject, DWORD dwExpectedContentTypeFlags,
506                                         DWORD dwExpectedFormatTypeFlags, DWORD dwFlags,
507                                         DWORD* pdwMsgAndCertEncodingType, DWORD* pdwContentType,
508                                         DWORD* pdwFormatType, HCERTSTORE* phCertStore,
509                                         HCRYPTMSG* phMsg, const void** ppvContext);
510 static BOOL (WINAPI *pCryptMsgGetParam)(HCRYPTMSG hCryptMsg, DWORD dwParamType, DWORD dwIndex,
511                                         void* pvData, DWORD* pcbData);
512 static PCCERT_CONTEXT (WINAPI *pCertFindCertificateInStore)(HCERTSTORE hCertStore, DWORD dwCertEncodingType,
513                                                             DWORD dwFindFlags, DWORD dwFindType,
514                                                             const void* pvFindPara,
515                                                             PCCERT_CONTEXT pPrevCertContext);
516 static BOOL (WINAPI *pCertCloseStore)(HCERTSTORE hCertStore, DWORD dwFlags);
517 static BOOL (WINAPI *pCryptMsgClose)(HCRYPTMSG hCryptMsg);
518 static BOOL (WINAPI *pCertCompareCertificate)(DWORD dwCertEncodingType, PCERT_INFO pCertId1,
519                                               PCERT_INFO pCertId2);
520 static BOOL (WINAPI *pCertFreeCertificateContext)(PCCERT_CONTEXT pCertContext);
521
522 void LoadCrypt32(void)
523 {
524     hCrypt32 = LoadLibrary("crypt32");
525     if ( !hCrypt32 )
526         return;
527
528     (FARPROC) pCertGetNameString = GetProcAddress( hCrypt32, "CertGetNameString" );
529     (FARPROC) pCryptQueryObject = GetProcAddress( hCrypt32, "CryptQueryObject" );
530     (FARPROC) pCryptMsgGetParam = GetProcAddress( hCrypt32, "CryptMsgGetParam" );
531     (FARPROC) pCertFindCertificateInStore = GetProcAddress( hCrypt32, "CertFindCertificateInStore" );
532     (FARPROC) pCertCloseStore = GetProcAddress( hCrypt32, "CertCloseStore" );
533     (FARPROC) pCryptMsgClose = GetProcAddress( hCrypt32, "CryptMsgClose" );
534     (FARPROC) pCertCompareCertificate = GetProcAddress( hCrypt32, "CertCompareCertificate" );
535     (FARPROC) pCertFreeCertificateContext = GetProcAddress( hCrypt32, "CertFreeCertificateContext" );
536     
537     if ( !pCertGetNameString ||
538          !pCryptQueryObject ||
539          !pCryptMsgGetParam ||
540          !pCertFindCertificateInStore ||
541          !pCertCloseStore ||
542          !pCryptMsgClose ||
543          !pCertCompareCertificate ||
544          !pCertFreeCertificateContext)
545     {
546         FreeLibrary(hCrypt32);
547         hCrypt32 = NULL;
548     }
549 }
550
551 void UnloadCrypt32(void)
552 {
553     FreeLibrary(hCrypt32);
554 }
555
556 #define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
557
558 PCCERT_CONTEXT GetCertCtx(CHAR * filename)
559 {
560     wchar_t wfilename[260];
561     BOOL fResult;
562     DWORD dwEncoding;
563     DWORD dwContentType;
564     DWORD dwFormatType;
565     DWORD dwSignerInfo;
566     HCERTSTORE hStore = NULL;
567     HCRYPTMSG hMsg = NULL;
568     PCMSG_SIGNER_INFO pSignerInfo = NULL;
569     PCCERT_CONTEXT pCertContext = NULL;
570     CERT_INFO CertInfo;
571
572     if ( hCrypt32 == NULL )
573         return NULL;
574
575     ZeroMemory(&CertInfo, sizeof(CertInfo));
576     mbstowcs(wfilename, filename, 260);
577
578     fResult = pCryptQueryObject(CERT_QUERY_OBJECT_FILE,
579                                 wfilename,
580                                 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
581                                 CERT_QUERY_FORMAT_FLAG_BINARY,
582                                 0,
583                                 &dwEncoding,
584                                 &dwContentType,
585                                 &dwFormatType,
586                                 &hStore,
587                                 &hMsg,
588                                 NULL);
589
590     if (!fResult) {
591         afsi_log("CryptQueryObject failed for [%s] with error 0x%x",
592                  filename,
593                  GetLastError());
594         goto __exit;
595     }
596
597     fResult = pCryptMsgGetParam(hMsg,
598                                 CMSG_SIGNER_INFO_PARAM,
599                                 0,
600                                 NULL,
601                                 &dwSignerInfo);
602
603     if (!fResult) {
604         afsi_log("CryptMsgGetParam failed for [%s] with error 0x%x",
605                  filename,
606                  GetLastError());
607         goto __exit;
608     }
609
610     pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo);
611
612     fResult = pCryptMsgGetParam(hMsg,
613                                 CMSG_SIGNER_INFO_PARAM,
614                                 0,
615                                 (PVOID)pSignerInfo,
616                                 &dwSignerInfo);
617     
618     if (!fResult) {
619         afsi_log("CryptMsgGetParam failed for [%s] with error 0x%x",
620                  filename,
621                  GetLastError());
622         goto __exit;
623     }
624
625     CertInfo.Issuer = pSignerInfo->Issuer;
626     CertInfo.SerialNumber = pSignerInfo->SerialNumber;
627
628     pCertContext = pCertFindCertificateInStore(hStore,
629                                               ENCODING,
630                                               0,
631                                               CERT_FIND_SUBJECT_CERT,
632                                               (PVOID) &CertInfo,
633                                               NULL);
634
635     if (!pCertContext) {
636       afsi_log("CertFindCertificateInStore for file [%s] failed with 0x%x",
637                filename,
638                GetLastError());
639       goto __exit;
640     }
641
642   __exit:
643     if (pSignerInfo)
644       LocalFree(pSignerInfo);
645
646     /*    if (pCertContext)
647           CertFreeCertificateContext(pCertContext);*/
648
649     if (hStore)
650       pCertCloseStore(hStore,0);
651
652     if (hMsg)
653       pCryptMsgClose(hMsg);
654
655     return pCertContext;
656 }
657
658 BOOL VerifyTrust(CHAR * filename)
659 {
660     WIN_TRUST_ACTDATA_CONTEXT_WITH_SUBJECT fContextWSubject;
661     WIN_TRUST_SUBJECT_FILE fSubjectFile;
662     GUID trustAction = WIN_SPUB_ACTION_PUBLISHED_SOFTWARE;
663     GUID subject = WIN_TRUST_SUBJTYPE_PE_IMAGE;
664     wchar_t wfilename[260];
665     LONG ret;
666     BOOL success = FALSE;
667
668     LONG (WINAPI *pWinVerifyTrust)(HWND hWnd, GUID* pgActionID, WINTRUST_DATA* pWinTrustData) = NULL;
669     HINSTANCE hWinTrust;
670
671     if (filename == NULL ) 
672         return FALSE;
673
674     hWinTrust = LoadLibrary("wintrust");
675     if ( !hWinTrust )
676         return FALSE;
677
678     if (((FARPROC) pWinVerifyTrust =
679           GetProcAddress( hWinTrust, "WinVerifyTrust" )) == NULL )
680     {
681         FreeLibrary(hWinTrust);
682         return FALSE;
683     }
684
685     mbstowcs(wfilename, filename, 260);
686
687     fSubjectFile.hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
688                                     0, NULL);
689     fSubjectFile.lpPath = wfilename;
690     fContextWSubject.hClientToken = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
691                                                 FALSE, GetCurrentProcessId());
692     fContextWSubject.SubjectType = &subject;
693     fContextWSubject.Subject = &fSubjectFile;
694
695     ret = pWinVerifyTrust(INVALID_HANDLE_VALUE, &trustAction, (WINTRUST_DATA *)&fContextWSubject);
696
697     if ( fSubjectFile.hFile != INVALID_HANDLE_VALUE )
698         CloseHandle( fSubjectFile.hFile );
699     if ( fContextWSubject.hClientToken != INVALID_HANDLE_VALUE )
700         CloseHandle( fContextWSubject.hClientToken );
701
702     if (ret == ERROR_SUCCESS) {
703         success = TRUE;
704     } else {
705         DWORD gle = GetLastError();
706         switch (gle) {
707         case TRUST_E_PROVIDER_UNKNOWN:
708             afsi_log("VerifyTrust failed: \"Generic Verify V2\" Provider Unknown");
709             break;  
710         case TRUST_E_NOSIGNATURE:
711             afsi_log("VerifyTrust failed: Unsigned executable");
712             break;
713         case TRUST_E_EXPLICIT_DISTRUST:
714             afsi_log("VerifyTrust failed: Certificate Marked as Untrusted by the user");
715             break;
716         case TRUST_E_SUBJECT_NOT_TRUSTED:
717             afsi_log("VerifyTrust failed: File is not trusted");
718             break;
719         case TRUST_E_BAD_DIGEST:
720             afsi_log("VerifyTrust failed: Executable has been modified");
721             break;
722         case CRYPT_E_SECURITY_SETTINGS:
723             afsi_log("VerifyTrust failed: local security options prevent verification");
724             break;
725         default:
726             afsi_log("VerifyTrust failed: 0x%X", GetLastError());
727         }
728         success = FALSE;
729     }
730     FreeLibrary(hWinTrust);
731     return success;
732 }
733
734 void LogCertCtx(PCCERT_CONTEXT pCtx) {
735     DWORD dwData;
736     LPTSTR szName = NULL;
737
738     if ( hCrypt32 == NULL )
739         return;
740
741     // Get Issuer name size.
742     if (!(dwData = pCertGetNameString(pCtx,
743                                       CERT_NAME_SIMPLE_DISPLAY_TYPE,
744                                       CERT_NAME_ISSUER_FLAG,
745                                       NULL,
746                                       NULL,
747                                       0))) {
748         afsi_log("CertGetNameString failed: 0x%x", GetLastError());
749         goto __exit;
750     }
751
752     // Allocate memory for Issuer name.
753     szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
754
755     // Get Issuer name.
756     if (!(pCertGetNameString(pCtx,
757                              CERT_NAME_SIMPLE_DISPLAY_TYPE,
758                              CERT_NAME_ISSUER_FLAG,
759                              NULL,
760                              szName,
761                              dwData))) {
762         afsi_log("CertGetNameString failed: 0x%x", GetLastError());
763         goto __exit;
764     }
765
766     // print Issuer name.
767     afsi_log("Issuer Name: %s", szName);
768     LocalFree(szName);
769     szName = NULL;
770
771     // Get Subject name size.
772     if (!(dwData = pCertGetNameString(pCtx,
773                                       CERT_NAME_SIMPLE_DISPLAY_TYPE,
774                                       0,
775                                       NULL,
776                                       NULL,
777                                       0))) {
778         afsi_log("CertGetNameString failed: 0x%x", GetLastError());
779         goto __exit;
780     }
781
782     // Allocate memory for subject name.
783     szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
784
785     // Get subject name.
786     if (!(pCertGetNameString(pCtx,
787                              CERT_NAME_SIMPLE_DISPLAY_TYPE,
788                              0,
789                              NULL,
790                              szName,
791                              dwData))) {
792         afsi_log("CertGetNameString failed: 0x%x", GetLastError());
793         goto __exit;
794     }
795
796     // Print Subject Name.
797     afsi_log("Subject Name: %s", szName);
798
799   __exit:
800
801     if (szName)
802         LocalFree(szName);
803 }
804
805 BOOL AFSModulesVerify(void)
806 {
807     CHAR filename[1024];
808     CHAR afsdVersion[128];
809     CHAR modVersion[128];
810     CHAR checkName[1024];
811     BOOL trustVerified = FALSE;
812     HMODULE hMods[1024];
813     HANDLE hProcess;
814     DWORD cbNeeded;
815     unsigned int i;
816     BOOL success = TRUE;
817     PCCERT_CONTEXT pCtxService = NULL;
818     HINSTANCE hPSAPI;
819     DWORD (WINAPI *pGetModuleFileNameExA)(HANDLE hProcess, HMODULE hModule, LPTSTR lpFilename, DWORD nSize);
820     BOOL (WINAPI *pEnumProcessModules)(HANDLE hProcess, HMODULE* lphModule, DWORD cb, LPDWORD lpcbNeeded);
821     DWORD dummyLen, code;
822     DWORD cacheSize = CM_CONFIGDEFAULT_CACHESIZE;
823     DWORD verifyServiceSig = TRUE;
824     HKEY parmKey;
825
826     hPSAPI = LoadLibrary("psapi");
827
828     if ( hPSAPI == NULL )
829         return FALSE;
830
831     if (!GetModuleFileName(NULL, filename, sizeof(filename)))
832         return FALSE;
833
834     if (GetVersionInfo(filename, afsdVersion, sizeof(afsdVersion)))
835         return FALSE;
836
837     afsi_log("%s version %s", filename, afsdVersion);
838
839     if (((FARPROC) pGetModuleFileNameExA =
840           GetProcAddress( hPSAPI, "GetModuleFileNameExA" )) == NULL ||
841          ((FARPROC) pEnumProcessModules =
842            GetProcAddress( hPSAPI, "EnumProcessModules" )) == NULL)
843     {
844         FreeLibrary(hPSAPI);
845         return FALSE;
846     }
847
848
849     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
850                         "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters",
851                         0, KEY_QUERY_VALUE, &parmKey);
852     if (code == ERROR_SUCCESS) {
853         dummyLen = sizeof(cacheSize);
854         code = RegQueryValueEx(parmKey, "CacheSize", NULL, NULL,
855                                (BYTE *) &cacheSize, &dummyLen);
856         RegCloseKey (parmKey);
857     }
858
859     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\OpenAFS\\Client",
860                          0, KEY_QUERY_VALUE, &parmKey);
861     if (code == ERROR_SUCCESS) {
862         dummyLen = sizeof(verifyServiceSig);
863         code = RegQueryValueEx(parmKey, "VerifyServiceSignature", NULL, NULL,
864                                 (BYTE *) &verifyServiceSig, &dummyLen);
865         RegCloseKey (parmKey);
866     }
867
868     if (verifyServiceSig && cacheSize < 716800) {
869         trustVerified = VerifyTrust(filename);
870     } else {
871         afsi_log("Signature Verification disabled");
872     }
873
874     if (trustVerified) {
875         LoadCrypt32();
876
877         // get a certificate context for the signer of afsd_service.
878         pCtxService = GetCertCtx(filename);
879         if (pCtxService)
880             LogCertCtx(pCtxService);
881     }
882
883     // Get a list of all the modules in this process.
884     hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
885                            FALSE, GetCurrentProcessId());
886
887     if (pEnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
888     {
889         afsi_log("Num of Process Modules: %d", (cbNeeded / sizeof(HMODULE)));
890
891         for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
892         {
893             char szModName[2048];
894
895             // Get the full path to the module's file.
896             if (pGetModuleFileNameExA(hProcess, hMods[i], szModName, sizeof(szModName)))
897             {
898                 lstrcpy(checkName, szModName);
899                 strlwr(checkName);
900
901                 if ( strstr(checkName, "afspthread.dll") ||
902                      strstr(checkName, "afsauthent.dll") ||
903                      strstr(checkName, "afsrpc.dll") ||
904                      strstr(checkName, "libafsconf.dll") ||
905                      strstr(checkName, "libosi.dll") )
906                 {
907                     if (GetVersionInfo(szModName, modVersion, sizeof(modVersion))) {
908                         success = FALSE;
909                         continue;
910                     }
911
912                     afsi_log("%s version %s", szModName, modVersion);
913                     if (strcmp(afsdVersion,modVersion)) {
914                         afsi_log("Version mismatch: %s", szModName);
915                         success = FALSE;
916                     }
917                     if ( trustVerified ) {
918                         if ( !VerifyTrust(szModName) ) {
919                             afsi_log("Signature Verification failed: %s", szModName);
920                             success = FALSE;
921                         } 
922                         else if (pCtxService) {
923                             PCCERT_CONTEXT pCtx = GetCertCtx(szModName);
924
925                             if (!pCtx || !pCertCompareCertificate(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
926                                                                   pCtxService->pCertInfo,
927                                                                   pCtx->pCertInfo)) {
928                                 afsi_log("Certificate mismatch: %s", szModName);
929                                 if (pCtx)
930                                     LogCertCtx(pCtx);
931                                 
932                                 success = FALSE;
933                             }
934                             
935                             if (pCtx)
936                                 pCertFreeCertificateContext(pCtx);
937                         }
938                     }
939                 }
940             }
941         }
942     }
943
944     if (pCtxService) {
945         pCertFreeCertificateContext(pCtxService);
946         UnloadCrypt32();
947     }
948
949     FreeLibrary(hPSAPI);
950
951     CloseHandle(hProcess);
952     return success;
953 }
954
955 typedef BOOL ( APIENTRY * AfsdInitHook )(void);
956 #define AFSD_INIT_HOOK "AfsdInitHook"
957 #define AFSD_HOOK_DLL  "afsdhook.dll"
958
959 /*
960 control serviceex exists only on 2000/xp. These functions will be loaded dynamically.
961 */
962
963 typedef SERVICE_STATUS_HANDLE ( * RegisterServiceCtrlHandlerExFunc )(  LPCTSTR , LPHANDLER_FUNCTION_EX , LPVOID );
964 typedef SERVICE_STATUS_HANDLE ( * RegisterServiceCtrlHandlerFunc   )(  LPCTSTR ,  LPHANDLER_FUNCTION );
965
966 RegisterServiceCtrlHandlerExFunc pRegisterServiceCtrlHandlerEx = NULL;
967 RegisterServiceCtrlHandlerFunc   pRegisterServiceCtrlHandler   = NULL; 
968
969 void afsd_Main(DWORD argc, LPTSTR *argv)
970 {
971     long code;
972     char *reason;
973 #ifdef JUMP
974     int jmpret;
975 #endif /* JUMP */
976     HANDLE hInitHookDll;
977     HANDLE hAdvApi32;
978     AfsdInitHook initHook;
979
980 #ifdef _DEBUG
981     _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF /*| _CRTDBG_CHECK_ALWAYS_DF*/ | 
982                    _CRTDBG_CHECK_CRT_DF /* | _CRTDBG_DELAY_FREE_MEM_DF */ );
983 #endif 
984
985     osi_InitPanic(afsd_notifier);
986     osi_InitTraceOption();
987
988     GlobalStatus = 0;
989
990     afsi_start();
991
992     WaitToTerminate = CreateEvent(NULL, TRUE, FALSE, TEXT("afsd_service_WaitToTerminate"));
993     if ( GetLastError() == ERROR_ALREADY_EXISTS )
994         afsi_log("Event Object Already Exists: %s", TEXT("afsd_service_WaitToTerminate"));
995
996 #ifndef NOTSERVICE
997     hAdvApi32 = LoadLibrary("advapi32.dll");
998     if (hAdvApi32 == NULL)
999     {
1000         afsi_log("Fatal: cannot load advapi32.dll");
1001         return;
1002     }
1003
1004     pRegisterServiceCtrlHandlerEx = (RegisterServiceCtrlHandlerExFunc)GetProcAddress(hAdvApi32, "RegisterServiceCtrlHandlerExA");
1005     if (pRegisterServiceCtrlHandlerEx)
1006     {
1007         afsi_log("running on 2000+ - using RegisterServiceCtrlHandlerEx");
1008         StatusHandle = RegisterServiceCtrlHandlerEx(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandlerEx, NULL );
1009     }
1010     else
1011     {
1012         StatusHandle = RegisterServiceCtrlHandler(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandler);
1013     }
1014
1015     ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
1016     ServiceStatus.dwServiceSpecificExitCode = 0;
1017     ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
1018     ServiceStatus.dwWin32ExitCode = NO_ERROR;
1019     ServiceStatus.dwCheckPoint = 1;
1020     ServiceStatus.dwWaitHint = 30000;
1021     /* accept Power Events */
1022     ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
1023     SetServiceStatus(StatusHandle, &ServiceStatus);
1024 #endif
1025
1026     {       
1027     HANDLE h; char *ptbuf[1];
1028     h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
1029     ptbuf[0] = "AFS start pending";
1030     ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
1031     DeregisterEventSource(h);
1032     }
1033
1034 #ifdef REGISTER_POWER_NOTIFICATIONS
1035     {
1036         HKEY hkParm;
1037         DWORD code;
1038         DWORD dummyLen;
1039         int bpower = TRUE;
1040
1041         /* see if we should handle power notifications */
1042         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName, 0, KEY_QUERY_VALUE, &hkParm);
1043         if (code == ERROR_SUCCESS) {
1044             dummyLen = sizeof(bpower);
1045             code = RegQueryValueEx(hkParm, "FlushOnHibernate", NULL, NULL,
1046                 (BYTE *) &bpower, &dummyLen);      
1047
1048             if(code != ERROR_SUCCESS)
1049                 bpower = TRUE;
1050
1051             RegCloseKey(hkParm);
1052         }
1053         /* create thread used to flush cache */
1054         if (bpower) {
1055             PowerNotificationThreadCreate();
1056             powerEventsRegistered = 1;
1057         }
1058     }
1059 #endif
1060
1061     /* Verify the versions of the DLLs which were loaded */
1062     if (!AFSModulesVerify()) {
1063         ServiceStatus.dwCurrentState = SERVICE_STOPPED;
1064         ServiceStatus.dwWin32ExitCode = NO_ERROR;
1065         ServiceStatus.dwCheckPoint = 0;
1066         ServiceStatus.dwWaitHint = 0;
1067         ServiceStatus.dwControlsAccepted = 0;
1068         SetServiceStatus(StatusHandle, &ServiceStatus);
1069                        
1070         /* exit if initialization failed */
1071         return;
1072     }
1073
1074     /* allow an exit to be called prior to any initialization */
1075     hInitHookDll = LoadLibrary(AFSD_HOOK_DLL);
1076     if (hInitHookDll)
1077     {
1078         BOOL hookRc = FALSE;
1079         initHook = ( AfsdInitHook ) GetProcAddress(hInitHookDll, AFSD_INIT_HOOK);
1080         if (initHook)
1081         {
1082             hookRc = initHook();
1083         }
1084         FreeLibrary(hInitHookDll);
1085         hInitHookDll = NULL;
1086
1087         if (hookRc == FALSE)
1088         {
1089             ServiceStatus.dwCurrentState = SERVICE_STOPPED;
1090             ServiceStatus.dwWin32ExitCode = NO_ERROR;
1091             ServiceStatus.dwCheckPoint = 0;
1092             ServiceStatus.dwWaitHint = 0;
1093             ServiceStatus.dwControlsAccepted = 0;
1094             SetServiceStatus(StatusHandle, &ServiceStatus);
1095                        
1096             /* exit if initialization failed */
1097             return;
1098         }
1099         else
1100         {
1101             /* allow another 15 seconds to start */
1102             ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
1103             ServiceStatus.dwServiceSpecificExitCode = 0;
1104             ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
1105             ServiceStatus.dwWin32ExitCode = NO_ERROR;
1106             ServiceStatus.dwCheckPoint = 2;
1107             ServiceStatus.dwWaitHint = 20000;
1108             /* accept Power Events */
1109             ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
1110             SetServiceStatus(StatusHandle, &ServiceStatus);
1111         }
1112     }
1113
1114 #ifdef JUMP
1115     MainThreadId = GetCurrentThreadId();
1116     jmpret = setjmp(notifier_jmp);
1117
1118     if (jmpret == 0) 
1119 #endif /* JUMP */
1120     {
1121         code = afsd_InitCM(&reason);
1122         if (code != 0) {
1123             afsi_log("afsd_InitCM failed: %s (code = %d)", reason, code);
1124             osi_panic(reason, __FILE__, __LINE__);
1125         }
1126
1127 #ifndef NOTSERVICE
1128         ServiceStatus.dwCheckPoint++;
1129         ServiceStatus.dwWaitHint -= 5000;
1130         SetServiceStatus(StatusHandle, &ServiceStatus);
1131 #endif
1132         code = afsd_InitDaemons(&reason);
1133         if (code != 0) {
1134             afsi_log("afsd_InitDaemons failed: %s (code = %d)", reason, code);
1135                         osi_panic(reason, __FILE__, __LINE__);
1136         }
1137
1138 #ifndef NOTSERVICE
1139         ServiceStatus.dwCheckPoint++;
1140         ServiceStatus.dwWaitHint -= 5000;
1141         SetServiceStatus(StatusHandle, &ServiceStatus);
1142 #endif
1143         code = afsd_InitSMB(&reason, MessageBox);
1144         if (code != 0) {
1145             afsi_log("afsd_InitSMB failed: %s (code = %d)", reason, code);
1146             osi_panic(reason, __FILE__, __LINE__);
1147         }
1148
1149         MountGlobalDrives();
1150
1151 #ifndef NOTSERVICE
1152         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
1153         ServiceStatus.dwWin32ExitCode = NO_ERROR;
1154         ServiceStatus.dwCheckPoint = 0;
1155         ServiceStatus.dwWaitHint = 0;
1156
1157         /* accept Power events */
1158         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
1159         SetServiceStatus(StatusHandle, &ServiceStatus);
1160 #endif  
1161         {
1162             HANDLE h; char *ptbuf[1];
1163             h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
1164             ptbuf[0] = "AFS running";
1165             ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
1166             DeregisterEventSource(h);
1167         }
1168     }
1169
1170     WaitForSingleObject(WaitToTerminate, INFINITE);
1171
1172     {   
1173         HANDLE h; char *ptbuf[1];
1174         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
1175         ptbuf[0] = "AFS quitting";
1176         ReportEvent(h, GlobalStatus ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
1177                 0, 0, NULL, 1, 0, ptbuf, NULL);
1178         DeregisterEventSource(h);
1179     }
1180
1181     DismountGlobalDrives();
1182     smb_Shutdown();
1183     rx_Finalize();
1184     buf_Shutdown();
1185
1186 #ifdef  REGISTER_POWER_NOTIFICATIONS
1187     /* terminate thread used to flush cache */
1188     if (powerEventsRegistered)
1189         PowerNotificationThreadExit();
1190 #endif
1191
1192     /* Remove the ExceptionFilter */
1193     SetUnhandledExceptionFilter(NULL);
1194
1195     ServiceStatus.dwCurrentState = SERVICE_STOPPED;
1196     ServiceStatus.dwWin32ExitCode = GlobalStatus ? ERROR_EXCEPTION_IN_SERVICE : NO_ERROR;
1197     ServiceStatus.dwCheckPoint = 0;
1198     ServiceStatus.dwWaitHint = 0;
1199     ServiceStatus.dwControlsAccepted = 0;
1200     SetServiceStatus(StatusHandle, &ServiceStatus);
1201 }       
1202
1203 DWORD __stdcall afsdMain_thread(void* notUsed)
1204 {
1205     char * argv[2] = {AFS_DAEMON_SERVICE_NAME, NULL};
1206     afsd_Main(1, (LPTSTR*)argv);
1207     return(0);
1208 }
1209
1210 int
1211 main(void)
1212 {
1213     static SERVICE_TABLE_ENTRY dispatchTable[] = {
1214         {AFS_DAEMON_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) afsd_Main},
1215         {NULL, NULL}
1216     };
1217
1218     if (!StartServiceCtrlDispatcher(dispatchTable))
1219     {
1220         LONG status = GetLastError();
1221         if (status == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
1222         {
1223             DWORD tid;
1224             hAFSDMainThread = CreateThread(NULL, 0, afsdMain_thread, 0, 0, &tid);
1225                 
1226             printf("Hit <Enter> to terminate OpenAFS Client Service\n");
1227             getchar();  
1228             SetEvent(WaitToTerminate);
1229         }
1230     }
1231
1232     if ( hAFSDMainThread ) {
1233         WaitForSingleObject( hAFSDMainThread, INFINITE );
1234         CloseHandle( hAFSDMainThread );
1235     }
1236     return(0);
1237 }