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