1 // Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
3 // This file was part of the VNC system.
5 // The VNC system is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 // If the source code for the VNC system is not available from the place
21 // whence you received this file, check http://www.uk.research.att.com/vnc or contact
22 // the authors on vnc@uk.research.att.com for information on obtaining it.
24 // This file has been adapted to the Win32 version of Bacula
25 // by Kern E. Sibbald. Many thanks to ATT and James Weatherall,
26 // the original author, for providing an excellent template.
28 // Copyright (2000-2003) Kern E. Sibbald
34 // Implementation of service-oriented functionality of Bacula
35 // I.e. command line options that contact a running version of
36 // Bacula and ask it to do something (show about, show status,
42 #include "winbacula.h"
43 #include "winservice.h"
47 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
50 // OS-SPECIFIC ROUTINES
52 // Create an instance of the bacService class to cause the static fields to be
53 // initialised properly
57 extern DWORD g_platform_id;
59 bacService::bacService()
61 OSVERSIONINFO osversioninfo;
62 osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
64 // Get the current OS version
65 if (!GetVersionEx(&osversioninfo)) {
68 g_platform_id = osversioninfo.dwPlatformId;
73 // IsWin95 - returns a BOOL indicating whether the current OS is Win95
77 return (g_platform_id == VER_PLATFORM_WIN32_WINDOWS);
80 // IsWinNT - returns a bool indicating whether the current OS is WinNT
84 return (g_platform_id == VER_PLATFORM_WIN32_NT);
87 // Internal routine to find the Bacula menu class window and
88 // post a message to it!
91 PostToBacula(UINT message, WPARAM wParam, LPARAM lParam)
93 // Locate the hidden Bacula menu window
94 HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
95 if (hservwnd == NULL) {
99 // Post the message to Bacula
100 PostMessage(hservwnd, message, wParam, lParam);
105 // Static routine to show the Properties dialog for a currently-running
106 // copy of Bacula, (usually a servicified version.)
109 bacService::ShowProperties()
114 // Static routine to show the Default Properties dialog for a currently-running
115 // copy of Bacula, (usually a servicified version.)
118 bacService::ShowDefaultProperties()
123 // Static routine to show the About dialog for a currently-running
124 // copy of Bacula, (usually a servicified version.)
127 bacService::ShowAboutBox()
129 // Post to the Bacula menu window
130 if (!PostToBacula(MENU_ABOUTBOX_SHOW, 0, 0)) {
131 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
137 // Static routine to show the Status dialog for a currently-running
138 // copy of Bacula, (usually a servicified version.)
141 bacService::ShowStatus()
143 // Post to the Bacula menu window
144 if (!PostToBacula(MENU_STATUS_SHOW, 0, 0)) {
145 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
151 // SERVICE-MODE ROUTINES
153 // Service-mode defines:
156 #define BAC_APPNAME "bacula"
158 // Internal service name
159 #define BAC_SERVICENAME "Bacula"
161 // Displayed service name
162 #define BAC_SERVICEDISPLAYNAME "Bacula File Server"
164 // List other required serves
165 #define BAC_DEPENDENCIES ""
168 // Internal service state
169 SERVICE_STATUS g_srvstatus; // current status of the service
170 SERVICE_STATUS_HANDLE g_hstatus;
172 DWORD g_servicethread = 0;
173 char* g_errortext[256];
176 // Forward defines of internal service functions
177 void WINAPI ServiceMain(DWORD argc, char **argv);
178 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
180 void WINAPI ServiceCtrl(DWORD ctrlcode);
181 bool WINAPI CtrlHandler (DWORD ctrltype);
182 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
184 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
186 BOOL g_servicemode = FALSE;
189 bacService::RunningAsService()
191 return g_servicemode;
195 bacService::KillRunningCopy()
197 while (PostToBacula(WM_CLOSE, 0, 0))
202 // SERVICE MAIN ROUTINE
204 bacService::BaculaServiceMain()
206 // Mark that we are a service
207 g_servicemode = TRUE;
209 // How to run as a service depends upon the OS being used
210 switch (g_platform_id) {
213 case VER_PLATFORM_WIN32_WINDOWS:
215 // Obtain a handle to the kernel library
216 HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
217 if (kerneldll == NULL) {
218 MessageBox(NULL, "KERNEL32.DLL not found: Bacula service not started",
219 "Bacula Service", MB_OK);
223 // And find the RegisterServiceProcess function
224 DWORD (WINAPI *RegisterService)(DWORD, DWORD);
225 RegisterService = (DWORD (WINAPI *)(DWORD, DWORD))
226 GetProcAddress(kerneldll, "RegisterServiceProcess");
227 if (RegisterService == NULL) {
228 MessageBox(NULL, "Registry service not found: Bacula service not started",
229 "Bacula Service", MB_OK);
230 log_error_message("Registry service not found");
234 // Register this process with the OS as a service!
235 RegisterService(0, 1);
237 // Run the main program as a service
240 // Then remove the service from the system service table
241 RegisterService(0, 0);
243 // Free the kernel library
244 FreeLibrary(kerneldll);
249 // Windows NT, Win2K, WinXP
250 case VER_PLATFORM_WIN32_NT:
252 // Create a service entry table
253 SERVICE_TABLE_ENTRY dispatchTable[] = {
254 {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
258 // Call the service control dispatcher with our entry table
259 if (!StartServiceCtrlDispatcher(dispatchTable)) {
260 log_error_message("StartServiceCtrlDispatcher failed.");
268 // SERVICE MAIN ROUTINE - NT ONLY !!!
269 // NT/Win2K/WinXP ONLY !!!
270 void WINAPI ServiceMain(DWORD argc, char **argv)
274 // Register the service control handler
275 g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
277 if (g_hstatus == 0) {
278 log_error_message("RegisterServiceCtlHandler failed");
279 MessageBox(NULL, "Contact Register Service Handler failure",
280 "Bacula service", MB_OK);
284 // Set up some standard service state values
285 g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
286 g_srvstatus.dwServiceSpecificExitCode = 0;
288 // Give this status to the SCM
290 SERVICE_START_PENDING, // Service state
291 NO_ERROR, // Exit code type
292 45000)) { // Hint as to how long Bacula should have hung before you assume error
294 ReportStatus(SERVICE_STOPPED, g_error, 0);
295 log_error_message("ReportStatus STOPPED failed 1");
299 // Now start the service for real
300 (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
304 // SERVICE START ROUTINE - thread that calls BaculaAppMain
306 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
309 // Save the current thread identifier
310 g_servicethread = GetCurrentThreadId();
312 // report the status to the service control manager.
315 SERVICE_RUNNING, // service state
316 NO_ERROR, // exit code
318 MessageBox(NULL, "Report Service failure", "Bacula Service", MB_OK);
319 log_error_message("ReportStatus RUNNING failed");
323 /* Call Bacula main code */
326 /* Mark that we're no longer running */
329 /* Tell the service manager that we've stopped */
330 ReportStatus(SERVICE_STOPPED, g_error, 0);
335 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
338 // Post a quit message to the main service thread
339 if (g_servicethread != 0) {
340 PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
344 // SERVICE INSTALL ROUTINE
346 bacService::InstallService()
348 const int pathlength = 2048;
349 char path[pathlength];
350 char servicecmd[pathlength];
353 // Get the filename of this executable
354 if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
355 MessageBox(NULL, "Unable to install Bacula service", szAppName, MB_ICONEXCLAMATION | MB_OK);
359 // Append the service-start flag to the end of the path:
360 if ((int)strlen(path) + 20 + (int)strlen(BaculaRunService) < pathlength) {
361 sprintf(servicecmd, "\"%s\" %s -c \"%s\"", path, BaculaRunService, path);
362 len = strlen(servicecmd) - 1;
363 for ( ; len > 0; len--) {
364 if (servicecmd[len] == '\\') {
370 strcat(servicecmd, "\\bacula-fd.conf");
373 log_error_message("Service command length too long");
374 MessageBox(NULL, "Service command length too long. Service not registered.",
375 szAppName, MB_ICONEXCLAMATION | MB_OK);
379 // How to add the Bacula service depends upon the OS
380 switch (g_platform_id) {
383 case VER_PLATFORM_WIN32_WINDOWS:
384 // Locate the RunService registry entry
386 if (RegCreateKey(HKEY_LOCAL_MACHINE,
387 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
388 &runservices) != ERROR_SUCCESS) {
389 log_error_message("Cannot write System Registry");
390 MessageBox(NULL, "The System Registry could not be updated - the Bacula service was not installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
394 // Attempt to add a Bacula key
395 if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS) {
396 RegCloseKey(runservices);
397 log_error_message("Cannot add Bacula key to System Registry");
398 MessageBox(NULL, "The Bacula service could not be installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
402 RegCloseKey(runservices);
404 // We have successfully installed the service!
406 "The Bacula File service was successfully installed.\n"
407 "The service may be started by double clicking on the\n"
408 "Bacula \"Start\" icon and will be automatically\n"
409 "be run the next time this machine is rebooted. ",
411 MB_ICONINFORMATION | MB_OK);
414 // Windows NT, Win2K, WinXP
415 case VER_PLATFORM_WIN32_NT:
417 SC_HANDLE hsrvmanager;
419 // Open the default, local Service Control Manager database
420 hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
421 if (hsrvmanager == NULL) {
422 log_error_message("OpenSCManager failed");
424 "The Service Control Manager could not be contacted - the Bacula service was not installed",
425 szAppName, MB_ICONEXCLAMATION | MB_OK);
429 // Create an entry for the Bacula service
430 hservice = CreateService(
431 hsrvmanager, // SCManager database
432 BAC_SERVICENAME, // name of service
433 BAC_SERVICEDISPLAYNAME, // name to display
434 SERVICE_ALL_ACCESS, // desired access
435 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
437 SERVICE_AUTO_START, // start type
438 SERVICE_ERROR_NORMAL, // error control type
439 servicecmd, // service's binary
440 NULL, // no load ordering group
441 NULL, // no tag identifier
442 BAC_DEPENDENCIES, // dependencies
443 NULL, // LocalSystem account
444 NULL); // no password
445 if (hservice == NULL) {
446 CloseServiceHandle(hsrvmanager);
447 log_error_message("CreateService failed");
449 "The Bacula service could not be installed",
450 szAppName, MB_ICONEXCLAMATION | MB_OK);
454 set_service_description(hsrvmanager,hservice,
455 "Provides file backup and restore services. Bacula -- the network backup solution.");
457 CloseServiceHandle(hsrvmanager);
458 CloseServiceHandle(hservice);
461 // Now install the servicehelper registry setting...
462 // Locate the RunService registry entry
464 if (RegCreateKey(HKEY_LOCAL_MACHINE,
465 "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
466 &runapps) != ERROR_SUCCESS) {
467 MessageBox(NULL, "WARNING: Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded",
468 szAppName, MB_ICONEXCLAMATION | MB_OK);
470 char servicehelpercmd[pathlength];
472 // Append the service-helper-start flag to the end of the path:
473 if ((int)strlen(path) + 4 + (int)strlen(BaculaRunServiceHelper) < pathlength) {
474 sprintf(servicehelpercmd, "\"%s\" %s", path, BaculaRunServiceHelper);
476 // Add the Bacula Service Helper entry
477 if (RegSetValueEx(runapps, szAppName, 0, REG_SZ,
478 (unsigned char *)servicehelpercmd, strlen(servicehelpercmd)+1) != ERROR_SUCCESS) {
479 MessageBox(NULL, "WARNING:Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded", szAppName, MB_ICONEXCLAMATION | MB_OK);
481 RegCloseKey(runapps);
486 // Everything went fine
488 "The Bacula File service was successfully installed.\n"
489 "The service may be started from the Control Panel and will\n"
490 "automatically be run the next time this machine is rebooted.",
492 MB_ICONINFORMATION | MB_OK);
495 log_error_message("Unknown Windows System version");
497 "Unknown Windows operating system.\n"
498 "Cannot install Bacula service.\n",
499 szAppName, MB_ICONEXCLAMATION | MB_OK);
507 // SERVICE REMOVE ROUTINE
509 bacService::RemoveService()
511 // How to remove the Bacula service depends upon the OS
512 switch (g_platform_id) {
515 case VER_PLATFORM_WIN32_WINDOWS:
516 // Locate the RunService registry entry
518 if (RegOpenKey(HKEY_LOCAL_MACHINE,
519 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
520 &runservices) != ERROR_SUCCESS) {
522 "Could not find registry entry.\nService probably not registerd - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
524 // Attempt to delete the Bacula key
525 if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS) {
526 RegCloseKey(runservices);
527 MessageBox(NULL, "Could not delete Registry key.\nThe Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
530 RegCloseKey(runservices);
534 // Try to kill any running copy of Bacula
535 if (!KillRunningCopy()) {
537 "Bacula could not be contacted, probably not running",
538 szAppName, MB_ICONEXCLAMATION | MB_OK);
542 // We have successfully removed the service!
543 MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
546 // Windows NT, Win2K, WinXP
547 case VER_PLATFORM_WIN32_NT:
549 SC_HANDLE hsrvmanager;
552 // Attempt to remove the service-helper hook
554 if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
555 &runapps) == ERROR_SUCCESS) {
556 // Attempt to delete the Bacula key
557 if (RegDeleteValue(runapps, szAppName) != ERROR_SUCCESS) {
558 MessageBox(NULL, "WARNING:The ServiceHelper hook entry could not be removed from the registry", szAppName, MB_ICONEXCLAMATION | MB_OK);
560 RegCloseKey(runapps);
565 hsrvmanager = OpenSCManager(
566 NULL, // machine (NULL == local)
567 NULL, // database (NULL == default)
568 SC_MANAGER_ALL_ACCESS // access required
571 hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
572 if (hservice != NULL) {
573 SERVICE_STATUS status;
575 // Try to stop the Bacula service
576 if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
577 while(QueryServiceStatus(hservice, &status)) {
578 if (status.dwCurrentState == SERVICE_STOP_PENDING) {
585 if (status.dwCurrentState != SERVICE_STOPPED) {
586 MessageBox(NULL, "The Bacula service could not be stopped", szAppName, MB_ICONEXCLAMATION | MB_OK);
590 // Now remove the service from the SCM
591 if(DeleteService(hservice)) {
592 MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
594 MessageBox(NULL, "The Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
597 CloseServiceHandle(hservice);
599 MessageBox(NULL, "The Bacula service could not be found", szAppName, MB_ICONEXCLAMATION | MB_OK);
602 CloseServiceHandle(hsrvmanager);
604 MessageBox(NULL, "The SCM could not be contacted - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
611 // USEFUL SERVICE SUPPORT ROUTINES
613 // Service control routine
614 void WINAPI ServiceCtrl(DWORD ctrlcode)
616 // What control code have we been sent?
618 case SERVICE_CONTROL_STOP:
619 // STOP : The service must stop
620 g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
624 case SERVICE_CONTROL_INTERROGATE:
625 // QUERY : Service control manager just wants to know our state
629 // Control code not recognised
633 // Tell the control manager what we're up to.
634 ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
637 // Service manager status reporting
638 BOOL ReportStatus(DWORD state,
642 static DWORD checkpoint = 1;
645 // If we're in the start state then we don't want the control manager
646 // sending us control messages because they'll confuse us.
647 if (state == SERVICE_START_PENDING) {
648 g_srvstatus.dwControlsAccepted = 0;
650 g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
653 // Save the new status we've been given
654 g_srvstatus.dwCurrentState = state;
655 g_srvstatus.dwWin32ExitCode = exitcode;
656 g_srvstatus.dwWaitHint = waithint;
658 // Update the checkpoint variable to let the SCM know that we
659 // haven't died if requests take a long time
660 if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
661 g_srvstatus.dwCheckPoint = 0;
663 g_srvstatus.dwCheckPoint = checkpoint++;
666 // Tell the SCM our new status
667 if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
668 log_error_message("SetServiceStatus failed");
675 void LogErrorMsg(char *message, char *fname, int lineno)
682 // Get the error code
683 g_error = GetLastError();
684 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
685 FORMAT_MESSAGE_FROM_SYSTEM,
693 // Use event logging to log the error
694 heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
696 sprintf(msgbuff, "\n\n%s error: %ld at %s:%d",
697 BAC_SERVICENAME, g_error, fname, lineno);
698 strings[0] = msgbuff;
699 strings[1] = message;
702 if (heventsrc != NULL) {
706 heventsrc, // handle of event source
707 EVENTLOG_ERROR_TYPE, // event type
710 NULL, // current user's SID
711 3, // strings in 'strings'
712 0, // no bytes of raw data
713 (const char **)strings, // array of error strings
714 NULL); // no raw data
716 DeregisterEventSource(heventsrc);
720 typedef BOOL (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
722 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
726 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
727 SERVICE_DESCRIPTION sdBuf;
729 WinAPI ChangeServiceDescription;
731 HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
735 ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
736 "ChangeServiceConfig2A");
738 if (!ChangeServiceDescription) {
742 // Need to acquire database lock before reconfiguring.
743 sclLock = LockServiceDatabase(hSCManager);
745 // If the database cannot be locked, report the details.
746 if (sclLock == NULL) {
747 // Exit if the database is not locked by another process.
748 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
749 log_error_message("LockServiceDatabase");
753 // Allocate a buffer to get details about the lock.
754 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc(
755 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
756 if (lpqslsBuf == NULL) {
757 log_error_message("LocalAlloc");
761 // Get and print the lock status information.
762 if (!QueryServiceLockStatus(
765 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
767 log_error_message("QueryServiceLockStatus");
770 if (lpqslsBuf->fIsLocked) {
771 printf("Locked by: %s, duration: %ld seconds\n",
772 lpqslsBuf->lpLockOwner,
773 lpqslsBuf->dwLockDuration);
775 printf("No longer locked\n");
778 LocalFree(lpqslsBuf);
779 log_error_message("Could not lock database");
783 // The database is locked, so it is safe to make changes.
785 sdBuf.lpDescription = lpDesc;
787 if (!ChangeServiceDescription(
788 hService, // handle to service
789 SERVICE_CONFIG_DESCRIPTION, // change: description
790 &sdBuf) ) { // value: new description
791 log_error_message("ChangeServiceConfig2");
794 // Release the database lock.
795 UnlockServiceDatabase(sclLock);