2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2008 Free Software Foundation Europe e.V.
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
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.
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
23 Bacula® is a registered trademark of John Walker.
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.
30 * Kern Sibbald, August 2007
34 * This is a generic service routine, which is used by all three
35 * of the daemons. Each one compiles it with slightly different
43 /* Forward reference */
44 static void set_service_description(SC_HANDLE hSCManager,
45 SC_HANDLE hService, LPSTR lpDesc);
47 /* Other Window component dependencies */
48 #define BAC_DEPENDENCIES __TEXT("tcpip\0afd\0")
51 SERVICE_STATUS_HANDLE service_handle;
52 SERVICE_STATUS service_status;
53 DWORD service_error = 0;
54 static bool is_service = false;
56 /* Forward references */
57 void WINAPI serviceControlCallback(DWORD ctrlcode);
58 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
59 DWORD WINAPI baculaWorkerThread(LPVOID lpwThreadParam);
63 * Post a message to a running instance of the app
65 bool postToBacula(UINT message, WPARAM wParam, LPARAM lParam)
67 /* Locate the Bacula menu window */
68 HWND hservwnd = FindWindow(APP_NAME, NULL);
69 if (hservwnd == NULL) {
73 /* Post the message to Bacula */
74 PostMessage(hservwnd, message, wParam, lParam);
80 * Running as a service?
88 * terminate any running Bacula
90 int stopRunningBacula()
92 postToBacula(WM_CLOSE, 0, 0);
98 * New style service start callback handler for the OS.
99 * the OS returns control here immediately after starting
102 void WINAPI serviceStartCallback(DWORD argc, char **argv)
106 /* Register our service */
107 service_handle = RegisterServiceCtrlHandler(APP_NAME, serviceControlCallback);
108 if (!service_handle) {
109 log_error_message(_("RegisterServiceCtlHandler failed"));
110 MessageBox(NULL, _("Failure contacting the Service Handler"),
115 service_status.dwServiceType = SERVICE_WIN32|SERVICE_INTERACTIVE_PROCESS;
116 service_status.dwServiceSpecificExitCode = 0;
119 if (!ReportStatus(SERVICE_START_PENDING, NO_ERROR, 45000)) {
120 ReportStatus(SERVICE_STOPPED, service_error, 0);
121 log_error_message(_("Service start report failed"));
125 /* Now create the Bacula worker thread */
126 (void)CreateThread(NULL, 0, baculaWorkerThread, NULL, 0, &dwThreadID);
133 static void serviceStop()
135 /* Post a quit message our service thread */
136 if (service_thread_id != 0) {
137 PostThreadMessage(service_thread_id, WM_QUIT, 0, 0);
142 * Service Control callback handler. The OS can call us here
143 * at any time, most often to stop the service.
145 void WINAPI serviceControlCallback(DWORD ctrlcode)
148 case SERVICE_CONTROL_STOP:
149 service_status.dwCurrentState = SERVICE_STOP_PENDING;
150 serviceStop(); /* our stop service routine */
154 /* Report our status */
155 ReportStatus(service_status.dwCurrentState, NO_ERROR, 0);
160 * Run Bacula as a service
162 int baculaServiceMain()
164 is_service = true; /* indicate we are running as a service */
166 if (have_service_api) { /* New style service API */
167 /* Tell OS where to dispatch service calls to us */
168 SERVICE_TABLE_ENTRY dispatchTable[] = {
169 {APP_NAME, (LPSERVICE_MAIN_FUNCTION)serviceStartCallback},
172 /* Start the service control dispatcher */
173 if (!StartServiceCtrlDispatcher(dispatchTable)) {
174 log_error_message(_("StartServiceCtrlDispatcher failed."));
176 /* Note, this thread continues in the ServiceCallback routine */
178 } else { /* old style Win95/98/Me */
179 HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
180 if (kerneldll == NULL) {
181 MessageBox(NULL, _("KERNEL32.DLL not found: Bacula service not started"),
186 /* Get entry point for RegisterServiceProcess function */
187 DWORD (WINAPI *RegisterService)(DWORD, DWORD);
188 RegisterService = (DWORD (WINAPI *)(DWORD, DWORD))
189 GetProcAddress(kerneldll, "RegisterServiceProcess");
190 if (RegisterService == NULL) {
191 MessageBox(NULL, _("Registry service not found: Bacula service not started"),
193 log_error_message(_("Registry service entry point not found"));
197 RegisterService(0, 1); /* register us as a service */
198 BaculaAppMain(); /* call the main Bacula code */
199 RegisterService(0, 0); /* terminate the service */
200 FreeLibrary(kerneldll); /* free up kernel dll */
207 * New style service bacula worker thread
209 DWORD WINAPI baculaWorkerThread(LPVOID lpwThreadParam)
211 service_thread_id = GetCurrentThreadId();
213 if (!ReportStatus(SERVICE_RUNNING, NO_ERROR, 0)) {
214 MessageBox(NULL, _("Report Service failure"), APP_DESC, MB_OK);
215 log_error_message("ReportStatus RUNNING failed");
219 /* Call Bacula main code */
222 /* Mark that we're no longer running */
223 service_thread_id = 0;
225 /* Tell the service manager that we've stopped */
226 ReportStatus(SERVICE_STOPPED, service_error, 0);
233 * Install the Bacula service on the OS -- very complicated
235 int installService(const char *cmdOpts)
237 const int maxlen = 2048;
241 bsnprintf(svcmd, sizeof(svcmd), "service: install: %s", cmdOpts, APP_DESC, MB_OK);
243 /* Get our filename */
244 if (GetModuleFileName(NULL, path, maxlen-11) == 0) {
245 MessageBox(NULL, _("Unable to install the service"), APP_DESC, MB_ICONEXCLAMATION | MB_OK);
249 /* Create a valid command for starting the service */
250 if ((int)strlen(path) + (int)strlen(cmdOpts) + 30 < maxlen) {
251 bsnprintf(svcmd, sizeof(svcmd), "\"%s\" /service %s", path, cmdOpts);
253 log_error_message(_("Service command length too long"));
254 MessageBox(NULL, _("Service command length too long. Service not registered."),
255 APP_DESC, MB_ICONEXCLAMATION | MB_OK);
259 if (have_service_api) {
260 SC_HANDLE baculaService, serviceManager;
262 /* Open the service control manager */
263 serviceManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
264 if (!serviceManager) {
265 log_error_message("Open Service Manager failed");
267 _("The Service Control Manager could not be contacted - the service was not installed"),
268 APP_DESC, MB_ICONEXCLAMATION | MB_OK);
272 /* Now actually create the Bacula service entry */
273 baculaService = CreateService(
275 APP_NAME, /* Our service name */
276 APP_DESC, /* Display name */
278 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
280 SERVICE_ERROR_NORMAL,
281 svcmd, /* Command string to start the service */
284 BAC_DEPENDENCIES, /* Services to start before us */
285 NULL, /* Use default SYSTEM account */
287 if (!baculaService) {
288 CloseServiceHandle(serviceManager);
289 log_error_message("CreateService failed for " APP_DESC);
290 MessageBox(NULL, _("The Bacula service: " APP_NAME " could not be installed"),
291 APP_DESC, MB_ICONEXCLAMATION | MB_OK);
295 /* Set a text description in the service manager's control panel */
296 set_service_description(serviceManager, baculaService,
297 _("Provides file backup and restore services. Bacula -- the network backup solution."));
299 CloseServiceHandle(serviceManager);
300 CloseServiceHandle(baculaService);
303 /* Old style service -- create appropriate registry key path */
305 if (RegCreateKey(HKEY_LOCAL_MACHINE,
306 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
307 &runservices) != ERROR_SUCCESS) {
308 log_error_message(_("Cannot write System Registry for " APP_DESC));
309 MessageBox(NULL, _("The System Registry could not be updated - the Bacula service was not installed"),
310 APP_DESC, MB_ICONEXCLAMATION | MB_OK);
314 /* Add the Bacula values */
315 if (RegSetValueEx(runservices, APP_NAME, 0, REG_SZ,
316 (unsigned char *)svcmd, strlen(svcmd)+1) != ERROR_SUCCESS) {
317 RegCloseKey(runservices);
318 log_error_message(_("Cannot add Bacula key to System Registry"));
319 MessageBox(NULL, _("The Bacula service: " APP_NAME " could not be installed"),
320 APP_DESC, MB_ICONEXCLAMATION | MB_OK);
323 RegCloseKey(runservices);
326 /* At this point the service is installed */
329 _("The " APP_DESC "was successfully installed.\n"
330 "The service may be started by double clicking on the\n"
331 "Bacula \"Start\" icon and will be automatically\n"
332 "be run the next time this machine is rebooted. "),
333 APP_DESC, MB_ICONINFORMATION | MB_OK);
340 * Remove a service from the OS (normally done when we are installing
345 if (have_service_api) { /* Newer Windows platform (NT, Win2K, ...) */
346 SC_HANDLE serviceManager, baculaService;
349 serviceManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
350 if (serviceManager) {
351 /* Now get the Bacula service entry */
352 baculaService = OpenService(serviceManager, APP_NAME, SERVICE_ALL_ACCESS);
354 SERVICE_STATUS status;
356 /* If the service is running, stop it */
357 if (ControlService(baculaService, SERVICE_CONTROL_STOP, &status)) {
358 while(QueryServiceStatus(baculaService, &status)) {
359 if (status.dwCurrentState == SERVICE_STOP_PENDING) {
365 if (status.dwCurrentState != SERVICE_STOPPED) {
366 MessageBox(NULL, _("The Bacula service: " APP_NAME " could not be stopped"),
367 APP_DESC, MB_ICONEXCLAMATION | MB_OK);
371 if (DeleteService(baculaService)) {
373 MessageBox(NULL, _("The Bacula service: " APP_NAME " has been removed"),
374 APP_DESC, MB_ICONINFORMATION | MB_OK);
377 MessageBox(NULL, _("The Bacula service: " APP_NAME " could not be removed"),
378 APP_DESC, MB_ICONEXCLAMATION | MB_OK);
379 stat = 1; /* error */
381 CloseServiceHandle(baculaService);
384 MessageBox(NULL, _("A existing Bacula service: " APP_NAME " could not be found for "
385 "removal. This is not normally an error."),
386 APP_DESC, MB_ICONEXCLAMATION | MB_OK);
388 CloseServiceHandle(serviceManager);
392 MessageBox(NULL, _("The service Manager could not be contacted - the Bacula service was not removed"),
393 APP_DESC, MB_ICONEXCLAMATION | MB_OK);
394 return 1; /* error */
397 } else { /* Old Win95/98/Me OS */
398 /* Open the registry path key */
400 if (RegOpenKey(HKEY_LOCAL_MACHINE,
401 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
402 &runservices) != ERROR_SUCCESS) {
404 _("Could not find registry entry.\nService probably not registerd - the Bacula service was not removed"),
405 APP_DESC, MB_ICONEXCLAMATION | MB_OK);
407 /* Now delete the Bacula entry */
408 if (RegDeleteValue(runservices, APP_NAME) != ERROR_SUCCESS) {
409 RegCloseKey(runservices);
410 MessageBox(NULL, _("Could not delete Registry key for " APP_NAME ".\n"
411 "The Bacula service could not be removed"), APP_DESC, MB_ICONEXCLAMATION | MB_OK);
413 RegCloseKey(runservices);
417 /* Stop any running Bacula */
418 if (!stopRunningBacula()) {
420 _("Bacula could not be contacted, probably not running"),
421 APP_DESC, MB_ICONEXCLAMATION | MB_OK);
422 return 0; /* not really an error */
425 /* At this point, the service has been removed */
427 MessageBox(NULL, _("The Bacula service has been removed"), APP_DESC, MB_ICONINFORMATION | MB_OK);
435 * This subroutine is called to report our current status to the
436 * new style service manager
438 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint)
440 static DWORD checkpoint = 1;
443 /* No callbacks until we are started */
444 if (state == SERVICE_START_PENDING) {
445 service_status.dwControlsAccepted = 0;
447 service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
450 /* Save global service_status state */
451 service_status.dwCurrentState = state;
452 service_status.dwWin32ExitCode = exitcode;
453 service_status.dwWaitHint = waithint;
456 * Update the checkpoint variable so the service manager knows
459 if (state == SERVICE_RUNNING || state == SERVICE_STOPPED) {
460 service_status.dwCheckPoint = 0;
462 service_status.dwCheckPoint = checkpoint++;
465 /* Send our new status */
466 result = SetServiceStatus(service_handle, &service_status);
468 log_error_message(_("SetServiceStatus failed"));
473 /* Log an error message */
474 void LogErrorMsg(char *message, char *fname, int lineno)
481 service_error = GetLastError();
482 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
483 FORMAT_MESSAGE_FROM_SYSTEM,
491 /* Use the OS event logging to log the error */
492 eventHandler = RegisterEventSource(NULL, APP_NAME);
494 bsnprintf(msgbuf, sizeof(msgbuf), _("\n\n%s error: %ld at %s:%d"),
495 APP_NAME, service_error, fname, lineno);
498 strings[1] = message;
502 ReportEvent(eventHandler, EVENTLOG_ERROR_TYPE,
506 3, /* Number of strings */
507 0, /* raw data size */
508 (const char **)strings, /* error strings */
509 NULL); /* raw data */
510 DeregisterEventSource(eventHandler);
515 typedef BOOL (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
518 * This is amazingly complicated just to get a bit of English explanation
519 * in the service manager's dialog box.
521 static void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
525 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
526 SERVICE_DESCRIPTION sdBuf;
528 WinAPI ChangeServiceDescription;
530 HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
534 ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
535 "ChangeServiceConfig2A");
537 if (!ChangeServiceDescription) {
541 // Need to acquire database lock before reconfiguring.
542 sclLock = LockServiceDatabase(hSCManager);
544 // If the database cannot be locked, report the details.
545 if (sclLock == NULL) {
546 // Exit if the database is not locked by another process.
547 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
548 log_error_message("LockServiceDatabase");
552 // Allocate a buffer to get details about the lock.
553 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc(
554 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
555 if (lpqslsBuf == NULL) {
556 log_error_message("LocalAlloc");
560 // Get and print the lock status information.
561 if (!QueryServiceLockStatus(
564 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
566 log_error_message("QueryServiceLockStatus");
569 if (lpqslsBuf->fIsLocked) {
570 printf(_("Locked by: %s, duration: %ld seconds\n"),
571 lpqslsBuf->lpLockOwner,
572 lpqslsBuf->dwLockDuration);
574 printf(_("No longer locked\n"));
577 LocalFree(lpqslsBuf);
578 log_error_message(_("Could not lock database"));
582 // The database is locked, so it is safe to make changes.
584 sdBuf.lpDescription = lpDesc;
586 if (!ChangeServiceDescription(
587 hService, // handle to service
588 SERVICE_CONFIG_DESCRIPTION, // change: description
589 &sdBuf) ) { // value: new description
590 log_error_message("ChangeServiceConfig2");
593 // Release the database lock.
594 UnlockServiceDatabase(sclLock);