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