afsd-service-shutdown-crash-20040325
[openafs.git] / src / WINNT / afsd / afsd_service.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <windows.h>
14 #include <string.h>
15 #include <setjmp.h>
16 #include "afsd.h"
17 #include "afsd_init.h"
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <winsock2.h>
21
22 #include <osi.h>
23
24 #ifdef DEBUG
25 //#define NOTSERVICE
26 #endif
27 #ifdef _DEBUG
28 #include <crtdbg.h>
29 #endif
30
31 /*
32 // The following is defined if you want to receive Power notifications,
33 // including Hibernation, and also subsequent flushing of AFS volumes
34 //
35 #define REGISTER_POWER_NOTIFICATIONS
36 //
37 // Check
38 */
39 #include "afsd_flushvol.h"
40
41 extern void afsi_log(char *pattern, ...);
42
43 HANDLE WaitToTerminate;
44
45 int GlobalStatus;
46
47 #ifdef JUMP
48 unsigned int MainThreadId;
49 jmp_buf notifier_jmp;
50 #endif /* JUMP */
51
52 extern int traceOnPanic;
53 extern HANDLE afsi_file;
54
55 /*
56  * Notifier function for use by osi_panic
57  */
58 static void afsd_notifier(char *msgp, char *filep, long line)
59 {
60         char tbuffer[100];
61         char *ptbuf[1];
62         HANDLE h;
63
64         if (filep)
65                 sprintf(tbuffer, "Error at file %s, line %d: %s",
66                         filep, line, msgp);
67         else
68                 sprintf(tbuffer, "Error at unknown location: %s", msgp);
69
70         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
71         ptbuf[0] = tbuffer;
72         ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, line, NULL, 1, 0, ptbuf, NULL);
73         DeregisterEventSource(h);
74
75         GlobalStatus = line;
76
77         osi_LogEnable(afsd_logp);
78
79         afsd_ForceTrace(TRUE);
80
81     afsi_log("--- begin dump ---");
82     cm_DumpSCache(afsi_file, "a");
83 #ifdef keisa
84     cm_dnlcDump(afsi_file, "a");
85 #endif
86     cm_DumpBufHashTable(afsi_file, "a");
87     smb_DumpVCP(afsi_file, "a");                        
88     afsi_log("--- end   dump ---");
89     
90     DebugBreak();       
91
92         SetEvent(WaitToTerminate);
93
94 #ifdef JUMP
95         if (GetCurrentThreadId() == MainThreadId)
96                 longjmp(notifier_jmp, 1);
97         else
98 #endif /* JUMP */
99                 ExitThread(1);
100 }
101
102 /*
103  * For use miscellaneously in smb.c; need to do better
104  */
105 static int DummyMessageBox(HWND h, LPCTSTR l1, LPCTSTR l2, UINT ui)
106 {
107         return 0;
108 }
109
110 static SERVICE_STATUS           ServiceStatus;
111 static SERVICE_STATUS_HANDLE    StatusHandle;
112
113 DWORD
114 afsd_ServiceFlushVolume(DWORD dwlpEventData)
115 {
116     DWORD   dwRet = ERROR_NETWORK_BUSY; /* or NO_ERROR */
117
118     /*
119     **  If UI bit is not set, user interaction is not possible
120     **      BUT, since we are a NON-interactive service, and therefore
121     **  have NO user I/O, it doesn't much matter.
122     **  This benign code left here as example of how to find this out
123     */
124     BOOL bUI = (dwlpEventData & 1);
125
126     /* flush volume */
127     if ( PowerNotificationThreadNotify() )
128     {
129         dwRet = NO_ERROR;
130     }
131
132     else
133     {
134         /* flush was unsuccessful, or timeout - deny shutdown */
135         dwRet = ERROR_NETWORK_BUSY;
136     }
137
138     /*      to deny hibernate, simply return
139     //      any value besides NO_ERROR.
140     //      For example:
141     //      dwRet = ERROR_NETWORK_BUSY;
142     */
143
144     return dwRet;
145 }
146
147 /*
148 **    Extended ServiceControlHandler that provides Event types
149 **    for monitoring Power events, for example.
150 */
151 DWORD
152 afsd_ServiceControlHandlerEx(
153               DWORD  ctrlCode,
154               DWORD  dwEventType,
155               LPVOID lpEventData,
156               LPVOID lpContext
157               )
158 {
159         HKEY parmKey;
160         DWORD dummyLen, doTrace;
161         long code;
162     DWORD dwRet = ERROR_CALL_NOT_IMPLEMENTED;
163
164         switch (ctrlCode) 
165     {
166     case SERVICE_CONTROL_STOP:
167         /* Shutdown RPC */
168         RpcMgmtStopServerListening(NULL);
169
170         /* Force trace if requested */
171         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
172                             AFSConfigKeyName,
173                             0, KEY_QUERY_VALUE, &parmKey);
174         if (code != ERROR_SUCCESS)
175             goto doneTrace;
176
177         dummyLen = sizeof(doTrace);
178         code = RegQueryValueEx(parmKey, "TraceOnShutdown",
179                                NULL, NULL,
180                                (BYTE *) &doTrace, &dummyLen);
181         RegCloseKey (parmKey);
182         if (code != ERROR_SUCCESS)
183             doTrace = 0;
184         if (doTrace)
185             afsd_ForceTrace(FALSE);
186
187       doneTrace:
188         ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
189         ServiceStatus.dwWin32ExitCode = NO_ERROR;
190         ServiceStatus.dwCheckPoint = 1;
191         ServiceStatus.dwWaitHint = 10000;
192         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
193         SetServiceStatus(StatusHandle, &ServiceStatus);
194         SetEvent(WaitToTerminate);
195         dwRet = NO_ERROR;
196         break;
197
198     case SERVICE_CONTROL_INTERROGATE:
199         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
200         ServiceStatus.dwWin32ExitCode = NO_ERROR;
201         ServiceStatus.dwCheckPoint = 0;
202         ServiceStatus.dwWaitHint = 0;
203         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
204         SetServiceStatus(StatusHandle, &ServiceStatus);
205         dwRet = NO_ERROR;
206         break;
207
208                 /* XXX handle system shutdown */
209                 /* XXX handle pause & continue */
210                 case SERVICE_CONTROL_POWEREVENT:                                              
211                 {                                                                                     
212                         /*                                                                                
213             **  dwEventType of this notification == WPARAM of WM_POWERBROADCAST               
214                         **      Return NO_ERROR == return TRUE for that message, i.e. accept request          
215                         **      Return any error code to deny request,                                        
216                         **      i.e. as if returning BROADCAST_QUERY_DENY                                     
217                         */                                                                                
218                         switch((int) dwEventType)                                                         
219             {                                                                               
220                         case PBT_APMQUERYSUSPEND:                                                         
221                         case PBT_APMQUERYSTANDBY:                                                         
222                                                                                             
223 #ifdef  REGISTER_POWER_NOTIFICATIONS                                                                  
224                                 /* handle event */                                                            
225                                 dwRet = afsd_ServiceFlushVolume((DWORD) lpEventData);                         
226 #else                                                                                       
227                                 dwRet = NO_ERROR;                                                             
228 #endif                                                                                      
229                                 break;                                                                        
230                                                                                                                           
231             /* allow remaining case PBT_WhatEver */                                           
232                         case PBT_APMSUSPEND:                                                              
233                         case PBT_APMSTANDBY:                                                              
234                         case PBT_APMRESUMECRITICAL:                                                       
235                         case PBT_APMRESUMESUSPEND:                                                        
236                         case PBT_APMRESUMESTANDBY:                                                        
237                         case PBT_APMBATTERYLOW:                                                           
238                         case PBT_APMPOWERSTATUSCHANGE:                                                    
239                         case PBT_APMOEMEVENT:                                                             
240                         case PBT_APMRESUMEAUTOMATIC:                                                      
241                         default:                                                                          
242                                 dwRet = NO_ERROR;                                                             
243             }
244         }
245     }           /* end switch(ctrlCode) */                                                        
246         return dwRet;   
247 }
248
249 #if 1
250 /* This code was moved to Drivemap.cpp*/
251 /* Mount a drive into AFS if the user wants us to */
252 /* DEE Could check first if we are run as SYSTEM */
253 void CheckMountDrive()
254 {
255         char szAfsPath[_MAX_PATH];
256         char szDriveToMapTo[5];
257         DWORD dwResult;
258         char szKeyName[256];
259         HKEY hKey;
260         DWORD dwIndex = 0;
261         DWORD dwDriveSize;
262         DWORD dwSubMountSize;
263         char szSubMount[256];
264         DWORD dwType;
265
266         sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
267
268         dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
269         if (dwResult != ERROR_SUCCESS)
270                 return;
271
272         while (1) {
273                 dwDriveSize = sizeof(szDriveToMapTo);
274                 dwSubMountSize = sizeof(szSubMount);
275                 dwResult = RegEnumValue(hKey, dwIndex++, szDriveToMapTo, &dwDriveSize, 0, &dwType, szSubMount, &dwSubMountSize);
276                 if (dwResult != ERROR_MORE_DATA) {
277                         if (dwResult != ERROR_SUCCESS) {
278                                 if (dwResult != ERROR_NO_MORE_ITEMS)
279                                         afsi_log("Failed to read GlobalAutoMapper values: %d\n", dwResult);
280                                 break;
281                         }
282                 }
283                 
284 #if 0
285                 sprintf(szAfsPath, "\\Device\\LanmanRedirector\\%s\\%s-AFS\\%s", szDriveToMapTo, cm_HostName, szSubMount);
286         
287                 dwResult = DefineDosDevice(DDD_RAW_TARGET_PATH, szDriveToMapTo, szAfsPath);
288 #else
289                 {
290                     NETRESOURCE nr;
291                     memset (&nr, 0x00, sizeof(NETRESOURCE));
292  
293                     sprintf(szAfsPath,"\\\\%s-AFS\\%s",cm_HostName,szSubMount);
294                     
295                     nr.dwScope = RESOURCE_GLOBALNET;
296                     nr.dwType=RESOURCETYPE_DISK;
297                     nr.lpLocalName=szDriveToMapTo;
298                     nr.lpRemoteName=szAfsPath;
299                     nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
300                     nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;
301
302                     dwResult = WNetAddConnection2(&nr,NULL,NULL,FALSE);
303                 }
304 #endif
305                 afsi_log("GlobalAutoMap of %s to %s %s", szDriveToMapTo, szSubMount, dwResult ? "succeeded" : "failed");
306         }        
307
308         RegCloseKey(hKey);
309 }
310 #endif
311
312 typedef BOOL ( APIENTRY * AfsdInitHook )(void);
313 #define AFSD_INIT_HOOK "AfsdInitHook"
314 #define AFSD_HOOK_DLL  "afsdhook.dll"
315
316 void afsd_Main(DWORD argc, LPTSTR *argv)
317 {
318         long code;
319         char *reason;
320         int jmpret;
321     HANDLE hInitHookDll;
322     AfsdInitHook initHook;
323
324 #ifdef _DEBUG
325     _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF /*| _CRTDBG_CHECK_ALWAYS_DF*/ | 
326                    _CRTDBG_CHECK_CRT_DF /* | _CRTDBG_DELAY_FREE_MEM_DF */ );
327 #endif 
328
329         osi_InitPanic(afsd_notifier);
330         osi_InitTraceOption();
331
332         GlobalStatus = 0;
333
334         WaitToTerminate = CreateEvent(NULL, TRUE, FALSE, TEXT("afsd_service_WaitToTerminate"));
335     if ( GetLastError() == ERROR_ALREADY_EXISTS )
336         afsi_log("Event Object Already Exists: %s", TEXT("afsd_service_WaitToTerminate"));
337
338 #ifndef NOTSERVICE
339         StatusHandle = RegisterServiceCtrlHandlerEx(AFS_DAEMON_SERVICE_NAME,
340                         (LPHANDLER_FUNCTION_EX) afsd_ServiceControlHandlerEx,
341                                                  NULL /* user context */
342                                                  );
343
344         ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
345         ServiceStatus.dwServiceSpecificExitCode = 0;
346         ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
347         ServiceStatus.dwWin32ExitCode = NO_ERROR;
348         ServiceStatus.dwCheckPoint = 1;
349         ServiceStatus.dwWaitHint = 30000;
350     /* accept Power Events */
351         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
352         SetServiceStatus(StatusHandle, &ServiceStatus);
353 #endif
354
355     {       
356     HANDLE h; char *ptbuf[1];
357     h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
358     ptbuf[0] = "AFS start pending";
359     ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
360     DeregisterEventSource(h);
361     }
362
363 #ifdef REGISTER_POWER_NOTIFICATIONS
364     /* create thread used to flush cache */
365     PowerNotificationThreadCreate();
366 #endif
367
368         afsi_start();
369
370     /* allow an exit to be called prior to any initialization */
371     hInitHookDll = LoadLibrary(AFSD_HOOK_DLL);
372     if (hInitHookDll)
373     {
374         BOOL hookRc = FALSE;
375         initHook = ( AfsdInitHook ) GetProcAddress(hInitHookDll, AFSD_INIT_HOOK);
376         if (initHook)
377         {
378             hookRc = initHook();
379         }
380         FreeLibrary(hInitHookDll);
381                
382         if (hookRc == FALSE)
383         {
384             ServiceStatus.dwCurrentState = SERVICE_STOPPED;
385             ServiceStatus.dwWin32ExitCode = NO_ERROR;
386             ServiceStatus.dwCheckPoint = 0;
387             ServiceStatus.dwWaitHint = 0;
388             ServiceStatus.dwControlsAccepted = 0;
389             SetServiceStatus(StatusHandle, &ServiceStatus);
390                        
391             /* exit if initialization failed */
392             return;
393         }
394         else
395         {
396             /* allow another 15 seconds to start */
397             ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
398             ServiceStatus.dwServiceSpecificExitCode = 0;
399             ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
400             ServiceStatus.dwWin32ExitCode = NO_ERROR;
401             ServiceStatus.dwCheckPoint = 2;
402             ServiceStatus.dwWaitHint = 20000;
403             /* accept Power Events */
404             ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
405             SetServiceStatus(StatusHandle, &ServiceStatus);
406         }
407     }
408
409 #ifdef JUMP
410     MainThreadId = GetCurrentThreadId();
411         jmpret = setjmp(notifier_jmp);
412
413         if (jmpret == 0) 
414 #endif /* JUMP */
415     {
416                 code = afsd_InitCM(&reason);
417                 if (code != 0)
418                         osi_panic(reason, __FILE__, __LINE__);
419
420 #ifndef NOTSERVICE
421         ServiceStatus.dwCheckPoint++;
422         ServiceStatus.dwWaitHint -= 5000;
423         SetServiceStatus(StatusHandle, &ServiceStatus);
424 #endif
425                 code = afsd_InitDaemons(&reason);
426                 if (code != 0)
427                         osi_panic(reason, __FILE__, __LINE__);
428
429 #ifndef NOTSERVICE
430         ServiceStatus.dwCheckPoint++;
431         ServiceStatus.dwWaitHint -= 5000;
432         SetServiceStatus(StatusHandle, &ServiceStatus);
433 #endif
434                 code = afsd_InitSMB(&reason, DummyMessageBox);
435                 if (code != 0)
436                         osi_panic(reason, __FILE__, __LINE__);
437
438 #ifndef NOTSERVICE
439                 ServiceStatus.dwCurrentState = SERVICE_RUNNING;
440                 ServiceStatus.dwWin32ExitCode = NO_ERROR;
441                 ServiceStatus.dwCheckPoint = 0;
442                 ServiceStatus.dwWaitHint = 0;
443
444         /* accept Power events */
445                 ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
446                 SetServiceStatus(StatusHandle, &ServiceStatus);
447 #endif
448         {
449             HANDLE h; char *ptbuf[1];
450                 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
451                 ptbuf[0] = "AFS running";
452                 ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
453                 DeregisterEventSource(h);
454         }
455         }
456
457     /* Check if we should mount a drive into AFS */
458     CheckMountDrive();
459
460         WaitForSingleObject(WaitToTerminate, INFINITE);
461
462     ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
463         ServiceStatus.dwWin32ExitCode = NO_ERROR;
464         ServiceStatus.dwCheckPoint = 0;
465         ServiceStatus.dwWaitHint = 5000;
466         ServiceStatus.dwControlsAccepted = 0;
467         SetServiceStatus(StatusHandle, &ServiceStatus);
468
469     {   
470     HANDLE h; char *ptbuf[1];
471         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
472         ptbuf[0] = "AFS quitting";
473         ReportEvent(h, GlobalStatus ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
474                 0, 0, NULL, 1, 0, ptbuf, NULL);
475     DeregisterEventSource(h);
476     }
477
478 #ifdef  REGISTER_POWER_NOTIFICATIONS
479         /* terminate thread used to flush cache */
480         PowerNotificationThreadExit();
481 #endif
482
483     /* Remove the ExceptionFilter */
484     SetUnhandledExceptionFilter(NULL);
485
486         ServiceStatus.dwCurrentState = SERVICE_STOPPED;
487         ServiceStatus.dwWin32ExitCode = GlobalStatus ? ERROR_EXCEPTION_IN_SERVICE : NO_ERROR;
488         ServiceStatus.dwCheckPoint = 0;
489         ServiceStatus.dwWaitHint = 0;
490         ServiceStatus.dwControlsAccepted = 0;
491         SetServiceStatus(StatusHandle, &ServiceStatus);
492 }
493
494 DWORD __stdcall afsdMain_thread(void* notUsed)
495 {
496         afsd_Main(0, (LPTSTR*)NULL);
497     exit(0);
498 }
499
500 int
501 main(void)
502 {
503         static SERVICE_TABLE_ENTRY dispatchTable[] = {
504                 {AFS_DAEMON_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) afsd_Main},
505                 {NULL, NULL}
506         };
507
508         if (!StartServiceCtrlDispatcher(dispatchTable))
509     {
510         LONG status = GetLastError();
511             if (status == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
512         {
513             DWORD tid;
514             CreateThread(NULL, 0, afsdMain_thread, 0, 0, &tid);
515                 
516             printf("Hit <Enter> to terminate OpenAFS Client Service\n");
517             getchar();              
518             SetEvent(WaitToTerminate);
519         }
520     }
521     return(0);
522 }