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