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 (C) 2000-2006 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 __TEXT("tcpip\0afd\0+File System\0")
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(bool silent)
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!
407 _("The Bacula File service was successfully installed.\n"
408 "The service may be started by double clicking on the\n"
409 "Bacula \"Start\" icon and will be automatically\n"
410 "be run the next time this machine is rebooted. "),
412 MB_ICONINFORMATION | MB_OK);
416 // Windows NT, Win2K, WinXP
417 case VER_PLATFORM_WIN32_NT:
419 SC_HANDLE hsrvmanager;
421 // Open the default, local Service Control Manager database
422 hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
423 if (hsrvmanager == NULL) {
424 log_error_message("OpenSCManager failed");
426 _("The Service Control Manager could not be contacted - the Bacula service was not installed"),
427 szAppName, MB_ICONEXCLAMATION | MB_OK);
431 // Create an entry for the Bacula service
432 hservice = CreateService(
433 hsrvmanager, // SCManager database
434 BAC_SERVICENAME, // name of service
435 BAC_SERVICEDISPLAYNAME, // name to display
436 SERVICE_ALL_ACCESS, // desired access
437 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
439 SERVICE_AUTO_START, // start type
440 SERVICE_ERROR_NORMAL, // error control type
441 servicecmd, // service's binary
442 NULL, // no load ordering group
443 NULL, // no tag identifier
444 BAC_DEPENDENCIES, // dependencies
445 NULL, // LocalSystem account
446 NULL); // no password
447 if (hservice == NULL) {
448 CloseServiceHandle(hsrvmanager);
449 log_error_message("CreateService failed");
451 _("The Bacula service could not be installed"),
452 szAppName, MB_ICONEXCLAMATION | MB_OK);
456 set_service_description(hsrvmanager,hservice,
457 _("Provides file backup and restore services. Bacula -- the network backup solution."));
459 CloseServiceHandle(hsrvmanager);
460 CloseServiceHandle(hservice);
462 // Everything went fine
465 _("The Bacula File service was successfully installed.\n"
466 "The service may be started from the Control Panel and will\n"
467 "automatically be run the next time this machine is rebooted."),
469 MB_ICONINFORMATION | MB_OK);
473 log_error_message("Unknown Windows System version");
475 _("Unknown Windows operating system.\n"
476 "Cannot install Bacula service.\n"),
477 szAppName, MB_ICONEXCLAMATION | MB_OK);
485 // SERVICE REMOVE ROUTINE
487 bacService::RemoveService(bool silent)
489 // How to remove the Bacula service depends upon the OS
490 switch (g_platform_id) {
493 case VER_PLATFORM_WIN32_WINDOWS:
494 // Locate the RunService registry entry
496 if (RegOpenKey(HKEY_LOCAL_MACHINE,
497 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
498 &runservices) != ERROR_SUCCESS) {
500 _("Could not find registry entry.\nService probably not registerd - the Bacula service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
502 // Attempt to delete the Bacula key
503 if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS) {
504 RegCloseKey(runservices);
505 MessageBox(NULL, _("Could not delete Registry key.\nThe Bacula service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
508 RegCloseKey(runservices);
512 // Try to kill any running copy of Bacula
513 if (!KillRunningCopy()) {
515 _("Bacula could not be contacted, probably not running"),
516 szAppName, MB_ICONEXCLAMATION | MB_OK);
520 // We have successfully removed the service!
522 MessageBox(NULL, _("The Bacula service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
526 // Windows NT, Win2K, WinXP
527 case VER_PLATFORM_WIN32_NT:
529 SC_HANDLE hsrvmanager;
532 hsrvmanager = OpenSCManager(
533 NULL, // machine (NULL == local)
534 NULL, // database (NULL == default)
535 SC_MANAGER_ALL_ACCESS // access required
538 hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
539 if (hservice != NULL) {
540 SERVICE_STATUS status;
542 // Try to stop the Bacula service
543 if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
544 while(QueryServiceStatus(hservice, &status)) {
545 if (status.dwCurrentState == SERVICE_STOP_PENDING) {
552 if (status.dwCurrentState != SERVICE_STOPPED) {
553 MessageBox(NULL, _("The Bacula service could not be stopped"), szAppName, MB_ICONEXCLAMATION | MB_OK);
557 // Now remove the service from the SCM
558 if (DeleteService(hservice)) {
560 MessageBox(NULL, _("The Bacula service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
563 MessageBox(NULL, _("The Bacula service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
566 CloseServiceHandle(hservice);
568 MessageBox(NULL, _("The Bacula service could not be found"), szAppName, MB_ICONEXCLAMATION | MB_OK);
571 CloseServiceHandle(hsrvmanager);
573 MessageBox(NULL, _("The SCM could not be contacted - the Bacula service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
580 // USEFUL SERVICE SUPPORT ROUTINES
582 // Service control routine
583 void WINAPI ServiceCtrl(DWORD ctrlcode)
585 // What control code have we been sent?
587 case SERVICE_CONTROL_STOP:
588 // STOP : The service must stop
589 g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
593 case SERVICE_CONTROL_INTERROGATE:
594 // QUERY : Service control manager just wants to know our state
598 // Control code not recognised
602 // Tell the control manager what we're up to.
603 ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
606 // Service manager status reporting
607 BOOL ReportStatus(DWORD state,
611 static DWORD checkpoint = 1;
614 // If we're in the start state then we don't want the control manager
615 // sending us control messages because they'll confuse us.
616 if (state == SERVICE_START_PENDING) {
617 g_srvstatus.dwControlsAccepted = 0;
619 g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
622 // Save the new status we've been given
623 g_srvstatus.dwCurrentState = state;
624 g_srvstatus.dwWin32ExitCode = exitcode;
625 g_srvstatus.dwWaitHint = waithint;
627 // Update the checkpoint variable to let the SCM know that we
628 // haven't died if requests take a long time
629 if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
630 g_srvstatus.dwCheckPoint = 0;
632 g_srvstatus.dwCheckPoint = checkpoint++;
635 // Tell the SCM our new status
636 if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
637 log_error_message(_("SetServiceStatus failed"));
644 void LogErrorMsg(char *message, char *fname, int lineno)
651 // Get the error code
652 g_error = GetLastError();
653 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
654 FORMAT_MESSAGE_FROM_SYSTEM,
662 // Use event logging to log the error
663 heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
665 sprintf(msgbuff, _("\n\n%s error: %ld at %s:%d"),
666 BAC_SERVICENAME, g_error, fname, lineno);
667 strings[0] = msgbuff;
668 strings[1] = message;
671 if (heventsrc != NULL) {
675 heventsrc, // handle of event source
676 EVENTLOG_ERROR_TYPE, // event type
679 NULL, // current user's SID
680 3, // strings in 'strings'
681 0, // no bytes of raw data
682 (const char **)strings, // array of error strings
683 NULL); // no raw data
685 DeregisterEventSource(heventsrc);
689 typedef BOOL (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
691 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
695 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
696 SERVICE_DESCRIPTION sdBuf;
698 WinAPI ChangeServiceDescription;
700 HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
704 ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
705 "ChangeServiceConfig2A");
707 if (!ChangeServiceDescription) {
711 // Need to acquire database lock before reconfiguring.
712 sclLock = LockServiceDatabase(hSCManager);
714 // If the database cannot be locked, report the details.
715 if (sclLock == NULL) {
716 // Exit if the database is not locked by another process.
717 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
718 log_error_message("LockServiceDatabase");
722 // Allocate a buffer to get details about the lock.
723 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc(
724 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
725 if (lpqslsBuf == NULL) {
726 log_error_message("LocalAlloc");
730 // Get and print the lock status information.
731 if (!QueryServiceLockStatus(
734 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
736 log_error_message("QueryServiceLockStatus");
739 if (lpqslsBuf->fIsLocked) {
740 printf(_("Locked by: %s, duration: %ld seconds\n"),
741 lpqslsBuf->lpLockOwner,
742 lpqslsBuf->dwLockDuration);
744 printf(_("No longer locked\n"));
747 LocalFree(lpqslsBuf);
748 log_error_message(_("Could not lock database"));
752 // The database is locked, so it is safe to make changes.
754 sdBuf.lpDescription = lpDesc;
756 if (!ChangeServiceDescription(
757 hService, // handle to service
758 SERVICE_CONFIG_DESCRIPTION, // change: description
759 &sdBuf) ) { // value: new description
760 log_error_message("ChangeServiceConfig2");
763 // Release the database lock.
764 UnlockServiceDatabase(sclLock);