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,
41 #include "winbacula.h"
42 #include "winservice.h"
45 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
48 // OS-SPECIFIC ROUTINES
50 // Create an instance of the bacService class to cause the static fields to be
51 // initialised properly
57 bacService::bacService()
59 OSVERSIONINFO osversioninfo;
60 osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
62 // Get the current OS version
63 if (!GetVersionEx(&osversioninfo)) {
66 g_platform_id = osversioninfo.dwPlatformId;
71 // IsWin95 - returns a BOOL indicating whether the current OS is Win95
75 return (g_platform_id == VER_PLATFORM_WIN32_WINDOWS);
78 // IsWinNT - returns a bool indicating whether the current OS is WinNT
82 return (g_platform_id == VER_PLATFORM_WIN32_NT);
85 // Internal routine to find the Bacula menu class window and
86 // post a message to it!
89 PostToBacula(UINT message, WPARAM wParam, LPARAM lParam)
91 // Locate the hidden Bacula menu window
92 HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
93 if (hservwnd == NULL) {
97 // Post the message to Bacula
98 PostMessage(hservwnd, message, wParam, lParam);
103 // Static routine to show the Properties dialog for a currently-running
104 // copy of Bacula, (usually a servicified version.)
107 bacService::ShowProperties()
112 // Static routine to show the Default Properties dialog for a currently-running
113 // copy of Bacula, (usually a servicified version.)
116 bacService::ShowDefaultProperties()
121 // Static routine to show the About dialog for a currently-running
122 // copy of Bacula, (usually a servicified version.)
125 bacService::ShowAboutBox()
127 // Post to the Bacula menu window
128 if (!PostToBacula(MENU_ABOUTBOX_SHOW, 0, 0)) {
129 MessageBox(NULL, _("No existing instance of Bacula could be contacted"), szAppName, MB_ICONEXCLAMATION | MB_OK);
135 // Static routine to show the Status dialog for a currently-running
136 // copy of Bacula, (usually a servicified version.)
139 bacService::ShowStatus()
141 // Post to the Bacula menu window
142 if (!PostToBacula(MENU_STATUS_SHOW, 0, 0)) {
143 MessageBox(NULL, _("No existing instance of Bacula could be contacted"), szAppName, MB_ICONEXCLAMATION | MB_OK);
149 // SERVICE-MODE ROUTINES
151 // Service-mode defines:
154 #define BAC_APPNAME "baculafd"
156 // Internal service name
157 #define BAC_SERVICENAME "Baculafd"
159 // Displayed service name
160 #define BAC_SERVICEDISPLAYNAME "Bacula File Server"
162 // List other required serves
163 #define BAC_DEPENDENCIES __TEXT("tcpip\0afd\0")
166 // Internal service state
167 SERVICE_STATUS g_srvstatus; // current status of the service
168 SERVICE_STATUS_HANDLE g_hstatus;
170 DWORD g_servicethread = 0;
171 char* g_errortext[256];
174 // Forward defines of internal service functions
175 void WINAPI ServiceMain(DWORD argc, char **argv);
176 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
178 void WINAPI ServiceCtrl(DWORD ctrlcode);
179 bool WINAPI CtrlHandler (DWORD ctrltype);
180 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
182 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
184 BOOL g_servicemode = FALSE;
187 bacService::RunningAsService()
189 return g_servicemode;
193 bacService::KillRunningCopy()
195 while (PostToBacula(WM_CLOSE, 0, 0))
200 // SERVICE MAIN ROUTINE
202 bacService::BaculaServiceMain()
204 // Mark that we are a service
205 g_servicemode = TRUE;
207 // How to run as a service depends upon the OS being used
208 switch (g_platform_id) {
211 case VER_PLATFORM_WIN32_WINDOWS:
213 // Obtain a handle to the kernel library
214 HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
215 if (kerneldll == NULL) {
216 MessageBox(NULL, _("KERNEL32.DLL not found: Bacula service not started"),
217 "Bacula Service", MB_OK);
221 // And find the RegisterServiceProcess function
222 DWORD (WINAPI *RegisterService)(DWORD, DWORD);
223 RegisterService = (DWORD (WINAPI *)(DWORD, DWORD))
224 GetProcAddress(kerneldll, "RegisterServiceProcess");
225 if (RegisterService == NULL) {
226 MessageBox(NULL, _("Registry service not found: Bacula service not started"),
227 "Bacula Service", MB_OK);
228 log_error_message(_("Registry service not found"));
232 // Register this process with the OS as a service!
233 RegisterService(0, 1);
235 // Run the main program as a service
238 // Then remove the service from the system service table
239 RegisterService(0, 0);
241 // Free the kernel library
242 FreeLibrary(kerneldll);
247 // Windows NT, Win2K, WinXP
248 case VER_PLATFORM_WIN32_NT:
250 // Create a service entry table
251 SERVICE_TABLE_ENTRY dispatchTable[] = {
252 {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
256 // Call the service control dispatcher with our entry table
257 if (!StartServiceCtrlDispatcher(dispatchTable)) {
258 log_error_message(_("StartServiceCtrlDispatcher failed."));
266 // SERVICE MAIN ROUTINE - NT ONLY !!!
267 // NT/Win2K/WinXP ONLY !!!
268 void WINAPI ServiceMain(DWORD argc, char **argv)
272 // Register the service control handler
273 g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
275 if (g_hstatus == 0) {
276 log_error_message(_("RegisterServiceCtlHandler failed"));
277 MessageBox(NULL, _("Contact Register Service Handler failure"),
278 "Bacula service", MB_OK);
282 // Set up some standard service state values
283 g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
284 g_srvstatus.dwServiceSpecificExitCode = 0;
286 // Give this status to the SCM
288 SERVICE_START_PENDING, // Service state
289 NO_ERROR, // Exit code type
290 45000)) { // Hint as to how long Bacula should have hung before you assume error
292 ReportStatus(SERVICE_STOPPED, g_error, 0);
293 log_error_message(_("ReportStatus STOPPED failed 1"));
297 // Now start the service for real
298 (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
302 // SERVICE START ROUTINE - thread that calls BaculaAppMain
304 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
307 // Save the current thread identifier
308 g_servicethread = GetCurrentThreadId();
310 // report the status to the service control manager.
313 SERVICE_RUNNING, // service state
314 NO_ERROR, // exit code
316 MessageBox(NULL, _("Report Service failure"), "Bacula Service", MB_OK);
317 log_error_message("ReportStatus RUNNING failed");
321 /* Call Bacula main code */
324 /* Mark that we're no longer running */
327 /* Tell the service manager that we've stopped */
328 ReportStatus(SERVICE_STOPPED, g_error, 0);
333 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
336 // Post a quit message to the main service thread
337 if (g_servicethread != 0) {
338 PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
342 // SERVICE INSTALL ROUTINE
344 bacService::InstallService()
346 const int pathlength = 2048;
347 char path[pathlength];
348 char servicecmd[pathlength];
351 // Get the filename of this executable
352 if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
353 MessageBox(NULL, _("Unable to install Bacula service"), szAppName, MB_ICONEXCLAMATION | MB_OK);
357 // Append the service-start flag to the end of the path:
358 if ((int)strlen(path) + 20 + (int)strlen(BaculaRunService) < pathlength) {
359 sprintf(servicecmd, "\"%s\" %s -c \"%s\"", path, BaculaRunService, path);
360 len = strlen(servicecmd) - 1;
361 for ( ; len > 0; len--) {
362 if (servicecmd[len] == '\\') {
368 strcat(servicecmd, "\\bacula-fd.conf");
371 log_error_message(_("Service command length too long"));
372 MessageBox(NULL, _("Service command length too long. Service not registered."),
373 szAppName, MB_ICONEXCLAMATION | MB_OK);
377 // How to add the Bacula service depends upon the OS
378 switch (g_platform_id) {
381 case VER_PLATFORM_WIN32_WINDOWS:
382 // Locate the RunService registry entry
384 if (RegCreateKey(HKEY_LOCAL_MACHINE,
385 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
386 &runservices) != ERROR_SUCCESS) {
387 log_error_message(_("Cannot write System Registry"));
388 MessageBox(NULL, _("The System Registry could not be updated - the Bacula service was not installed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
392 // Attempt to add a Bacula key
393 if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS) {
394 RegCloseKey(runservices);
395 log_error_message(_("Cannot add Bacula key to System Registry"));
396 MessageBox(NULL, _("The Bacula service could not be installed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
400 RegCloseKey(runservices);
402 // We have successfully installed the service!
405 _("The Bacula File service was successfully installed.\n"
406 "The service may be started by double clicking on the\n"
407 "Bacula \"Start\" icon and will be automatically\n"
408 "be run the next time this machine is rebooted. "),
410 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);
460 // Everything went fine
463 _("The Bacula File service was successfully installed.\n"
464 "The service may be started from the Control Panel and will\n"
465 "automatically be run the next time this machine is rebooted."),
467 MB_ICONINFORMATION | MB_OK);
471 log_error_message("Unknown Windows System version");
473 _("Unknown Windows operating system.\n"
474 "Cannot install Bacula service.\n"),
475 szAppName, MB_ICONEXCLAMATION | MB_OK);
483 // SERVICE REMOVE ROUTINE
485 bacService::RemoveService()
487 // How to remove the Bacula service depends upon the OS
488 switch (g_platform_id) {
491 case VER_PLATFORM_WIN32_WINDOWS:
492 // Locate the RunService registry entry
494 if (RegOpenKey(HKEY_LOCAL_MACHINE,
495 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
496 &runservices) != ERROR_SUCCESS) {
498 _("Could not find registry entry.\nService probably not registerd - the Bacula service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
500 // Attempt to delete the Bacula key
501 if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS) {
502 RegCloseKey(runservices);
503 MessageBox(NULL, _("Could not delete Registry key.\nThe Bacula service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
506 RegCloseKey(runservices);
510 // Try to kill any running copy of Bacula
511 if (!KillRunningCopy()) {
513 _("Bacula could not be contacted, probably not running"),
514 szAppName, MB_ICONEXCLAMATION | MB_OK);
518 // We have successfully removed the service!
520 MessageBox(NULL, _("The Bacula service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
524 // Windows NT, Win2K, WinXP
525 case VER_PLATFORM_WIN32_NT:
527 SC_HANDLE hsrvmanager;
530 hsrvmanager = OpenSCManager(
531 NULL, // machine (NULL == local)
532 NULL, // database (NULL == default)
533 SC_MANAGER_ALL_ACCESS // access required
536 hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
537 if (hservice != NULL) {
538 SERVICE_STATUS status;
540 // Try to stop the Bacula service
541 if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
542 while(QueryServiceStatus(hservice, &status)) {
543 if (status.dwCurrentState == SERVICE_STOP_PENDING) {
550 if (status.dwCurrentState != SERVICE_STOPPED) {
551 MessageBox(NULL, _("The Bacula service could not be stopped"), szAppName, MB_ICONEXCLAMATION | MB_OK);
555 // Now remove the service from the SCM
556 if (DeleteService(hservice)) {
558 MessageBox(NULL, _("The Bacula service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
561 MessageBox(NULL, _("The Bacula service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
564 CloseServiceHandle(hservice);
566 MessageBox(NULL, _("The Bacula service could not be found"), szAppName, MB_ICONEXCLAMATION | MB_OK);
569 CloseServiceHandle(hsrvmanager);
571 MessageBox(NULL, _("The SCM could not be contacted - the Bacula service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
578 // USEFUL SERVICE SUPPORT ROUTINES
580 // Service control routine
581 void WINAPI ServiceCtrl(DWORD ctrlcode)
583 // What control code have we been sent?
585 case SERVICE_CONTROL_STOP:
586 // STOP : The service must stop
587 g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
591 case SERVICE_CONTROL_INTERROGATE:
592 // QUERY : Service control manager just wants to know our state
596 // Control code not recognised
600 // Tell the control manager what we're up to.
601 ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
604 // Service manager status reporting
605 BOOL ReportStatus(DWORD state,
609 static DWORD checkpoint = 1;
612 // If we're in the start state then we don't want the control manager
613 // sending us control messages because they'll confuse us.
614 if (state == SERVICE_START_PENDING) {
615 g_srvstatus.dwControlsAccepted = 0;
617 g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
620 // Save the new status we've been given
621 g_srvstatus.dwCurrentState = state;
622 g_srvstatus.dwWin32ExitCode = exitcode;
623 g_srvstatus.dwWaitHint = waithint;
625 // Update the checkpoint variable to let the SCM know that we
626 // haven't died if requests take a long time
627 if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
628 g_srvstatus.dwCheckPoint = 0;
630 g_srvstatus.dwCheckPoint = checkpoint++;
633 // Tell the SCM our new status
634 if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
635 log_error_message(_("SetServiceStatus failed"));
642 void LogErrorMsg(char *message, char *fname, int lineno)
649 // Get the error code
650 g_error = GetLastError();
651 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
652 FORMAT_MESSAGE_FROM_SYSTEM,
660 // Use event logging to log the error
661 heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
663 sprintf(msgbuff, _("\n\n%s error: %ld at %s:%d"),
664 BAC_SERVICENAME, g_error, fname, lineno);
665 strings[0] = msgbuff;
666 strings[1] = message;
669 if (heventsrc != NULL) {
673 heventsrc, // handle of event source
674 EVENTLOG_ERROR_TYPE, // event type
677 NULL, // current user's SID
678 3, // strings in 'strings'
679 0, // no bytes of raw data
680 (const char **)strings, // array of error strings
681 NULL); // no raw data
683 DeregisterEventSource(heventsrc);
687 typedef BOOL (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
689 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
693 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
694 SERVICE_DESCRIPTION sdBuf;
696 WinAPI ChangeServiceDescription;
698 HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
702 ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
703 "ChangeServiceConfig2A");
705 if (!ChangeServiceDescription) {
709 // Need to acquire database lock before reconfiguring.
710 sclLock = LockServiceDatabase(hSCManager);
712 // If the database cannot be locked, report the details.
713 if (sclLock == NULL) {
714 // Exit if the database is not locked by another process.
715 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
716 log_error_message("LockServiceDatabase");
720 // Allocate a buffer to get details about the lock.
721 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc(
722 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
723 if (lpqslsBuf == NULL) {
724 log_error_message("LocalAlloc");
728 // Get and print the lock status information.
729 if (!QueryServiceLockStatus(
732 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
734 log_error_message("QueryServiceLockStatus");
737 if (lpqslsBuf->fIsLocked) {
738 printf(_("Locked by: %s, duration: %ld seconds\n"),
739 lpqslsBuf->lpLockOwner,
740 lpqslsBuf->dwLockDuration);
742 printf(_("No longer locked\n"));
745 LocalFree(lpqslsBuf);
746 log_error_message(_("Could not lock database"));
750 // The database is locked, so it is safe to make changes.
752 sdBuf.lpDescription = lpDesc;
754 if (!ChangeServiceDescription(
755 hService, // handle to service
756 SERVICE_CONFIG_DESCRIPTION, // change: description
757 &sdBuf) ) { // value: new description
758 log_error_message("ChangeServiceConfig2");
761 // Release the database lock.
762 UnlockServiceDatabase(sclLock);