]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/libwin32/service.cpp
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / win32 / libwin32 / service.cpp
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2018 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /* 
20  * 
21  *  Kern Sibbald, August 2007
22  *
23  * This is a generic service routine, which is used by all three
24  *  of the daemons. Each one compiles it with slightly different
25  *  #defines.
26  *
27  */
28
29 #include "bacula.h"
30 #include "win32.h"
31
32 /* Forward reference */
33 static void set_service_description(SC_HANDLE hSCManager, 
34                                     SC_HANDLE hService, LPSTR lpDesc);
35   
36 /* Other Window component dependencies */
37 #define BAC_DEPENDENCIES __TEXT("tcpip\0afd\0") 
38
39 /* Service globals */
40 SERVICE_STATUS_HANDLE  service_handle;
41 SERVICE_STATUS service_status;
42 DWORD service_error = 0;
43 static bool is_service = false;
44
45 /* Forward references */
46 void WINAPI serviceControlCallback(DWORD ctrlcode);
47 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
48 DWORD WINAPI baculaWorkerThread(LPVOID lpwThreadParam);
49
50
51 /*
52  * Post a message to a running instance of the app
53  */
54 bool postToBacula(UINT message, WPARAM wParam, LPARAM lParam)
55 {
56    /* Locate the Bacula menu window */
57    HWND hservwnd = FindWindow(APP_NAME, NULL);
58    if (hservwnd == NULL) {
59       return false;
60    }
61
62    /* Post the message to Bacula */
63    PostMessage(hservwnd, message, wParam, lParam);
64    return true;
65 }
66
67
68 /*
69  * Running as a service?
70  */
71 bool isAService()
72 {
73    return is_service;
74 }
75
76 /* 
77  * terminate any running Bacula
78  */
79 int stopRunningBacula()
80 {
81    postToBacula(WM_CLOSE, 0, 0);
82    sleep(5);
83    return 0;
84 }
85
86 /*
87  * New style service start callback handler for the OS.
88  *   the OS returns control here immediately after starting
89  *   the service.
90  */
91 void WINAPI serviceStartCallback(DWORD argc, char **argv)
92 {
93    DWORD dwThreadID;
94   
95    /* Register our service */
96    service_handle = RegisterServiceCtrlHandler(APP_NAME, serviceControlCallback);
97    if (!service_handle) {
98       log_error_message(_("RegisterServiceCtlHandler failed")); 
99       MessageBox(NULL, _("Failure contacting the Service Handler"),
100          APP_DESC, MB_OK);
101       return;
102    }
103
104    service_status.dwServiceType = SERVICE_WIN32;
105    service_status.dwServiceSpecificExitCode = 0;
106
107    /* Report status */
108    if (!ReportStatus(SERVICE_START_PENDING, NO_ERROR, 45000)) {
109        ReportStatus(SERVICE_STOPPED, service_error,  0);
110        log_error_message(_("Service start report failed")); 
111        return;
112    }
113
114    /* Now create the Bacula worker thread */
115    (void)CreateThread(NULL, 0, baculaWorkerThread, NULL, 0, &dwThreadID);
116    return;
117 }
118
119 /*
120  *  Stop our service 
121  */
122 static void serviceStop()
123 {
124    /* Post a quit message our service thread */
125    if (service_thread_id != 0) {
126       PostThreadMessage(service_thread_id, WM_QUIT, 0, 0);
127    }
128 }
129
130 /*
131  * Service Control callback handler.  The OS can call us here
132  *   at any time, most often to stop the service.
133  */
134 void WINAPI serviceControlCallback(DWORD ctrlcode)
135 {
136    switch(ctrlcode) {
137    case SERVICE_CONTROL_STOP:
138       service_status.dwCurrentState = SERVICE_STOP_PENDING;
139       serviceStop();  /* our stop service routine */
140       break;
141    }
142
143    /* Report our status */
144    ReportStatus(service_status.dwCurrentState, NO_ERROR, 0);
145 }
146
147
148 /*
149  * Run Bacula as a service 
150  */
151 int baculaServiceMain()
152 {
153    is_service = true;                 /* indicate we are running as a service */
154
155    if (have_service_api) {            /* New style service API */
156       /* Tell OS where to dispatch service calls to us */
157       SERVICE_TABLE_ENTRY dispatchTable[] = {
158          {(char *)APP_NAME, (LPSERVICE_MAIN_FUNCTION)serviceStartCallback},
159          {NULL, NULL}};
160
161       /* Start the service control dispatcher */
162       if (!StartServiceCtrlDispatcher(dispatchTable)) {
163          log_error_message(_("StartServiceCtrlDispatcher failed."));
164       }
165       /* Note, this thread continues in the ServiceCallback routine */
166
167    } else {                           /* old style Win95/98/Me */
168       HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
169       if (kerneldll == NULL) {
170          MessageBox(NULL, _("KERNEL32.DLL not found: Bacula service not started"), 
171              APP_DESC, MB_OK);
172          return 1;
173       }
174
175       /* Get entry point for RegisterServiceProcess function */
176       DWORD (WINAPI *RegisterService)(DWORD, DWORD);
177       RegisterService = (DWORD (WINAPI *)(DWORD, DWORD))
178               GetProcAddress(kerneldll, "RegisterServiceProcess");
179       if (RegisterService == NULL) {
180          MessageBox(NULL, _("Registry service not found: Bacula service not started"),
181             APP_DESC, MB_OK);
182          log_error_message(_("Registry service entry point not found")); 
183          FreeLibrary(kerneldll);         /* free up kernel dll */
184          return 1;
185       }
186       
187       RegisterService(0, 1);             /* register us as a service */
188       BaculaAppMain();                   /* call the main Bacula code */
189       RegisterService(0, 0);             /* terminate the service */
190       FreeLibrary(kerneldll);            /* free up kernel dll */
191    } 
192    return 0;
193 }
194
195
196 /* 
197  * New style service bacula worker thread
198  */
199 DWORD WINAPI baculaWorkerThread(LPVOID lpwThreadParam)
200 {
201    service_thread_id = GetCurrentThreadId();
202
203    if (!ReportStatus(SERVICE_RUNNING, NO_ERROR, 0)) {
204       MessageBox(NULL, _("Report Service failure"), APP_DESC, MB_OK);
205       log_error_message("ReportStatus RUNNING failed"); 
206       return 0;
207    }
208
209    /* Call Bacula main code */
210    BaculaAppMain();
211
212    /* Mark that we're no longer running */
213    service_thread_id = 0;
214
215    /* Tell the service manager that we've stopped */
216    ReportStatus(SERVICE_STOPPED, service_error, 0);
217    return 0;
218 }
219
220
221
222 /*
223  * Install the Bacula service on the OS -- very complicated
224  */
225 int installService(const char *cmdOpts)
226 {
227    const int maxlen = 2048;
228    char path[maxlen];
229    char svcmd[maxlen];
230
231    bsnprintf(svcmd, sizeof(svcmd), "service: install: %s", cmdOpts, APP_DESC, MB_OK);
232
233    /* Get our filename */
234    if (GetModuleFileName(NULL, path, maxlen-11) == 0) {
235       MessageBox(NULL, _("Unable to install the service"), APP_DESC, MB_ICONEXCLAMATION | MB_OK);
236       return 0;
237    }
238
239    /* Create a valid command for starting the service */
240    if ((int)strlen(path) + (int)strlen(cmdOpts) + 30  < maxlen) {
241       bsnprintf(svcmd, sizeof(svcmd), "\"%s\" /service %s", path, cmdOpts);
242    } else {
243       log_error_message(_("Service command length too long")); 
244       MessageBox(NULL, _("Service command length too long. Service not registered."),
245           APP_DESC, MB_ICONEXCLAMATION | MB_OK);
246       return 0;
247    }
248
249    if (have_service_api) {
250       SC_HANDLE baculaService, serviceManager;
251
252       /* Open the service control manager */
253       serviceManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
254       if (!serviceManager) {
255          log_error_message("Open Service Manager failed"); 
256          MessageBox(NULL,
257             _("The Service Control Manager could not be contacted - the service was not installed"),
258             APP_DESC, MB_ICONEXCLAMATION | MB_OK);
259          return 0;
260       }
261
262       /* Now actually create the Bacula service entry */
263       baculaService = CreateService(
264               serviceManager, 
265               APP_NAME,                       /* Our service name */
266               APP_DESC,                       /* Display name */
267               SERVICE_ALL_ACCESS,
268               SERVICE_WIN32_OWN_PROCESS,      /* | SERVICE_INTERACTIVE_PROCESS, */
269               SERVICE_AUTO_START,
270               SERVICE_ERROR_NORMAL,
271               svcmd,                          /* Command string to start the service */
272               NULL,
273               NULL,
274               BAC_DEPENDENCIES,               /* Services to start before us */
275               NULL,                           /* Use default SYSTEM account */
276               NULL);
277       if (!baculaService) {
278          CloseServiceHandle(serviceManager);
279          log_error_message("CreateService failed for " APP_DESC); 
280          MessageBox(NULL, _("The Bacula service: " APP_NAME " could not be installed"),
281               APP_DESC, MB_ICONEXCLAMATION | MB_OK);
282          return 0;
283       }
284
285       /* Set a text description in the service manager's control panel */
286       set_service_description(serviceManager, baculaService,
287 (char *)_("Provides file backup and restore services. Bacula -- the network backup solution."));
288
289       CloseServiceHandle(serviceManager);
290       CloseServiceHandle(baculaService);
291
292    } else {
293       /* Old style service -- create appropriate registry key path */
294       HKEY runservices;
295       if (RegCreateKey(HKEY_LOCAL_MACHINE, 
296               "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
297               &runservices) != ERROR_SUCCESS) {
298          log_error_message(_("Cannot write System Registry for " APP_DESC)); 
299          MessageBox(NULL, _("The System Registry could not be updated - the Bacula service was not installed"), 
300              APP_DESC, MB_ICONEXCLAMATION | MB_OK);
301          return 0;
302       }
303
304       /* Add the Bacula values */
305       if (RegSetValueEx(runservices, APP_NAME, 0, REG_SZ, 
306                         (unsigned char *)svcmd, strlen(svcmd)+1) != ERROR_SUCCESS) {
307          RegCloseKey(runservices);
308          log_error_message(_("Cannot add Bacula key to System Registry")); 
309          MessageBox(NULL, _("The Bacula service: " APP_NAME " could not be installed"), 
310              APP_DESC, MB_ICONEXCLAMATION | MB_OK);
311          return 0;
312       }
313       RegCloseKey(runservices);
314    }
315
316    /* At this point the service is installed */
317    if (opt_debug) {
318       MessageBox(NULL,
319            _("The " APP_DESC "was successfully installed.\n"
320              "The service may be started by double clicking on the\n"
321              "Bacula \"Start\" icon and will be automatically\n"
322              "be run the next time this machine is rebooted. "),
323          APP_DESC, MB_ICONINFORMATION | MB_OK);
324    }
325    return 0;
326 }
327
328
329 /*
330  * Remove a service from the OS (normally done when we are installing
331  *   a new version).
332  */
333 int removeService()
334 {
335    SC_HANDLE serviceManager, baculaService;
336    int stat = 0;
337
338    if (have_service_api) {      /* Newer Windows platforms (NT, Win2K, ...) */
339       serviceManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
340       if (serviceManager) {
341          /* Now get the Bacula service entry */
342          baculaService = OpenService(serviceManager, APP_NAME, SERVICE_ALL_ACCESS);
343          if (baculaService) {
344             SERVICE_STATUS status;
345             /* If the service is running, stop it */
346             if (ControlService(baculaService, SERVICE_CONTROL_STOP, &status)) {
347                while(QueryServiceStatus(baculaService, &status)) {
348                   if (status.dwCurrentState != SERVICE_STOP_PENDING) {
349                      break; 
350                   }
351                   sleep(1);
352                }
353                if (status.dwCurrentState != SERVICE_STOPPED) {
354                   if (opt_debug) {
355                      MessageBox(NULL, _("The Bacula service: " APP_NAME " could not be stopped"), 
356                         APP_DESC, MB_ICONEXCLAMATION | MB_OK);
357                   }
358                }
359             }
360             if (DeleteService(baculaService)) {
361                if (opt_debug) {
362                   MessageBox(NULL, _("The Bacula service: " APP_NAME " has been removed"), 
363                      APP_DESC, MB_ICONINFORMATION | MB_OK);
364                }
365             } else {
366                MessageBox(NULL, _("The Bacula service: " APP_NAME " could not be removed"), 
367                   APP_DESC, MB_ICONEXCLAMATION | MB_OK);
368                stat = 1; /* error */
369             }
370             CloseServiceHandle(baculaService);
371          } else {
372             if (opt_debug) {
373                MessageBox(NULL, _("An existing Bacula service: " APP_NAME " could not be found for "
374                    "removal. This is not normally an error."),
375                    APP_DESC, MB_ICONEXCLAMATION | MB_OK);
376             }
377          }
378          CloseServiceHandle(serviceManager);
379          return stat;
380       } else {
381          MessageBox(NULL, _("The service Manager could not be contacted - the Bacula service was not removed"), 
382             APP_DESC, MB_ICONEXCLAMATION | MB_OK);
383          return 1; /* error */
384       }
385
386    } else {                     /* Old Win95/98/Me OS */
387       /* Open the registry path key */
388       HKEY runservices;
389       if (RegOpenKey(HKEY_LOCAL_MACHINE, 
390             "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
391             &runservices) != ERROR_SUCCESS) {
392          if (opt_debug) {
393             MessageBox(NULL, 
394                _("Could not find registry entry.\nService probably not registerd - the Bacula service was not removed"), 
395                   APP_DESC, MB_ICONEXCLAMATION | MB_OK);
396          }
397       } else {
398          /* Now delete the Bacula entry */
399          if (RegDeleteValue(runservices, APP_NAME) != ERROR_SUCCESS) {
400             RegCloseKey(runservices);
401             MessageBox(NULL, _("Could not delete Registry key for " APP_NAME ".\n"
402                "The Bacula service could not be removed"), APP_DESC, MB_ICONEXCLAMATION | MB_OK);
403          }
404          RegCloseKey(runservices);
405          return 1;
406       }
407       /* Stop any running Bacula */
408       if (!stopRunningBacula()) {
409          if (opt_debug) {
410             MessageBox(NULL,
411                 _("Bacula could not be contacted, probably not running"),
412                 APP_DESC, MB_ICONEXCLAMATION | MB_OK);
413          }
414          return 0;   /* not really an error */
415       }
416       /* At this point, the service has been removed */
417       if (opt_debug) {
418          MessageBox(NULL, _("The Bacula service has been removed"), APP_DESC, MB_ICONINFORMATION | MB_OK);
419       }
420    }
421    return 0;
422 }
423
424
425 /*
426  * This subroutine is called to report our current status to the
427  *  new style service manager
428  */
429 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint)
430 {
431    static DWORD checkpoint = 1;
432    BOOL result = TRUE;
433
434    /* No callbacks until we are started */
435    if (state == SERVICE_START_PENDING) {
436       service_status.dwControlsAccepted = 0;
437    } else {
438       service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
439    }
440
441    /* Save global service_status state */
442    service_status.dwCurrentState = state;
443    service_status.dwWin32ExitCode = exitcode;
444    service_status.dwWaitHint = waithint;
445
446    /*
447     * Update the checkpoint variable so the service manager knows
448     *   we are alive.
449     */
450    if (state == SERVICE_RUNNING || state == SERVICE_STOPPED) {
451       service_status.dwCheckPoint = 0;
452    } else {
453       service_status.dwCheckPoint = checkpoint++;
454    }
455
456    /* Send our new status */
457    result = SetServiceStatus(service_handle, &service_status);
458    if (!result) {
459       log_error_message(_("SetServiceStatus failed"));
460    }
461    return result;
462 }
463
464 /* Log an error message for the last Windows error */
465 void LogLastErrorMsg(const char *message, const char *fname, int lineno)
466 {
467    char msgbuf[500];
468    HANDLE eventHandler;
469    const char *strings[3];
470    LPTSTR msg;
471
472    service_error = GetLastError();
473    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
474                  FORMAT_MESSAGE_FROM_SYSTEM,
475                  NULL,
476                  service_error,
477                  0,
478                  (LPTSTR)&msg,
479                  0,
480                  NULL);
481
482    /* Use the OS event logging to log the error */
483    eventHandler = RegisterEventSource(NULL, APP_NAME);
484
485    bsnprintf(msgbuf, sizeof(msgbuf), _("\n\n%s error: %ld at %s:%d"), 
486       APP_NAME, service_error, fname, lineno);
487
488    strings[0] = msgbuf;
489    strings[1] = message;
490    strings[2] = msg;
491
492    if (eventHandler) {
493       ReportEvent(eventHandler, EVENTLOG_ERROR_TYPE,
494               0,                      /* category */
495               0,                      /* ID */
496               NULL,                   /* SID */
497               3,                      /* Number of strings */
498               0,                      /* raw data size */
499               (const char **)strings, /* error strings */
500               NULL);                  /* raw data */
501       DeregisterEventSource(eventHandler);
502    }
503    LocalFree(msg);
504 }
505
506 typedef BOOL  (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
507
508 /*
509  * This is amazingly complicated just to get a bit of English explanation
510  *  in the service manager's dialog box.
511  */
512 static void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
513                              LPSTR lpDesc) 
514
515     SC_LOCK sclLock; 
516     LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf; 
517     SERVICE_DESCRIPTION sdBuf;
518     DWORD dwBytesNeeded;
519     WinAPI ChangeServiceDescription;
520  
521     HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
522     if (!hLib) {
523        return;
524     }
525     ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
526        "ChangeServiceConfig2A");
527     FreeLibrary(hLib);
528     if (!ChangeServiceDescription) {
529        return;
530     }
531     
532     // Need to acquire database lock before reconfiguring. 
533     sclLock = LockServiceDatabase(hSCManager); 
534  
535     // If the database cannot be locked, report the details. 
536     if (sclLock == NULL) {
537        // Exit if the database is not locked by another process. 
538        if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
539           log_error_message("LockServiceDatabase"); 
540           return;
541        }
542  
543        // Allocate a buffer to get details about the lock. 
544        lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc( 
545             LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256); 
546        if (lpqslsBuf == NULL) {
547           log_error_message("LocalAlloc"); 
548           return;
549        }
550  
551        // Get and print the lock status information. 
552        if (!QueryServiceLockStatus( 
553               hSCManager, 
554               lpqslsBuf, 
555               sizeof(QUERY_SERVICE_LOCK_STATUS)+256, 
556               &dwBytesNeeded)) {
557           log_error_message("QueryServiceLockStatus"); 
558        }
559  
560        if (lpqslsBuf->fIsLocked) {
561           printf(_("Locked by: %s, duration: %ld seconds\n"), 
562                 lpqslsBuf->lpLockOwner, 
563                 lpqslsBuf->dwLockDuration); 
564        } else {
565           printf(_("No longer locked\n")); 
566        }
567  
568        LocalFree(lpqslsBuf); 
569        log_error_message(_("Could not lock database")); 
570        return;
571     } 
572  
573     // The database is locked, so it is safe to make changes. 
574  
575     sdBuf.lpDescription = lpDesc;
576
577     if (!ChangeServiceDescription(
578          hService,                   // handle to service
579          SERVICE_CONFIG_DESCRIPTION, // change: description
580          &sdBuf) ) {                 // value: new description
581        log_error_message("ChangeServiceConfig2");
582     }
583
584     // Release the database lock. 
585     UnlockServiceDatabase(sclLock); 
586 }