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