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