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
55 extern DWORD g_platform_id;
58 bacService::bacService()
60 OSVERSIONINFO osversioninfo;
61 osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
63 // Get the current OS version
64 if (!GetVersionEx(&osversioninfo)) {
67 g_platform_id = osversioninfo.dwPlatformId;
72 // IsWin95 - returns a BOOL indicating whether the current OS is Win95
76 return (g_platform_id == VER_PLATFORM_WIN32_WINDOWS);
79 // IsWinNT - returns a bool indicating whether the current OS is WinNT
83 return (g_platform_id == VER_PLATFORM_WIN32_NT);
86 // Internal routine to find the Bacula menu class window and
87 // post a message to it!
90 PostToBacula(UINT message, WPARAM wParam, LPARAM lParam)
92 // Locate the hidden Bacula menu window
93 HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
94 if (hservwnd == NULL) {
98 // Post the message to Bacula
99 PostMessage(hservwnd, message, wParam, lParam);
104 // Static routine to show the Properties dialog for a currently-running
105 // copy of Bacula, (usually a servicified version.)
108 bacService::ShowProperties()
113 // Static routine to show the Default Properties dialog for a currently-running
114 // copy of Bacula, (usually a servicified version.)
117 bacService::ShowDefaultProperties()
122 // Static routine to show the About dialog for a currently-running
123 // copy of Bacula, (usually a servicified version.)
126 bacService::ShowAboutBox()
128 // Post to the Bacula menu window
129 if (!PostToBacula(MENU_ABOUTBOX_SHOW, 0, 0)) {
130 MessageBox(NULL, _("No existing instance of Bacula could be contacted"), szAppName, MB_ICONEXCLAMATION | MB_OK);
136 // Static routine to show the Status dialog for a currently-running
137 // copy of Bacula, (usually a servicified version.)
140 bacService::ShowStatus()
142 // Post to the Bacula menu window
143 if (!PostToBacula(MENU_STATUS_SHOW, 0, 0)) {
144 MessageBox(NULL, _("No existing instance of Bacula could be contacted"), szAppName, MB_ICONEXCLAMATION | MB_OK);
150 // SERVICE-MODE ROUTINES
152 // Service-mode defines:
155 #define BAC_APPNAME "bacula"
157 // Internal service name
158 #define BAC_SERVICENAME "Bacula"
160 // Displayed service name
161 #define BAC_SERVICEDISPLAYNAME "Bacula File Server"
163 // List other required serves
164 #define BAC_DEPENDENCIES __TEXT("tcpip\0afd\0+File System\0")
167 // Internal service state
168 SERVICE_STATUS g_srvstatus; // current status of the service
169 SERVICE_STATUS_HANDLE g_hstatus;
171 DWORD g_servicethread = 0;
172 char* g_errortext[256];
175 // Forward defines of internal service functions
176 void WINAPI ServiceMain(DWORD argc, char **argv);
177 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
179 void WINAPI ServiceCtrl(DWORD ctrlcode);
180 bool WINAPI CtrlHandler (DWORD ctrltype);
181 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
183 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
185 BOOL g_servicemode = FALSE;
188 bacService::RunningAsService()
190 return g_servicemode;
194 bacService::KillRunningCopy()
196 while (PostToBacula(WM_CLOSE, 0, 0))
201 // SERVICE MAIN ROUTINE
203 bacService::BaculaServiceMain()
205 // Mark that we are a service
206 g_servicemode = TRUE;
208 // How to run as a service depends upon the OS being used
209 switch (g_platform_id) {
212 case VER_PLATFORM_WIN32_WINDOWS:
214 // Obtain a handle to the kernel library
215 HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
216 if (kerneldll == NULL) {
217 MessageBox(NULL, _("KERNEL32.DLL not found: Bacula service not started"),
218 "Bacula Service", MB_OK);
222 // And find the RegisterServiceProcess function
223 DWORD (WINAPI *RegisterService)(DWORD, DWORD);
224 RegisterService = (DWORD (WINAPI *)(DWORD, DWORD))
225 GetProcAddress(kerneldll, "RegisterServiceProcess");
226 if (RegisterService == NULL) {
227 MessageBox(NULL, _("Registry service not found: Bacula service not started"),
228 "Bacula Service", MB_OK);
229 log_error_message(_("Registry service not found"));
233 // Register this process with the OS as a service!
234 RegisterService(0, 1);
236 // Run the main program as a service
239 // Then remove the service from the system service table
240 RegisterService(0, 0);
242 // Free the kernel library
243 FreeLibrary(kerneldll);
248 // Windows NT, Win2K, WinXP
249 case VER_PLATFORM_WIN32_NT:
251 // Create a service entry table
252 SERVICE_TABLE_ENTRY dispatchTable[] = {
253 {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
257 // Call the service control dispatcher with our entry table
258 if (!StartServiceCtrlDispatcher(dispatchTable)) {
259 log_error_message(_("StartServiceCtrlDispatcher failed."));
267 // SERVICE MAIN ROUTINE - NT ONLY !!!
268 // NT/Win2K/WinXP ONLY !!!
269 void WINAPI ServiceMain(DWORD argc, char **argv)
273 // Register the service control handler
274 g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
276 if (g_hstatus == 0) {
277 log_error_message(_("RegisterServiceCtlHandler failed"));
278 MessageBox(NULL, _("Contact Register Service Handler failure"),
279 "Bacula service", MB_OK);
283 // Set up some standard service state values
284 g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
285 g_srvstatus.dwServiceSpecificExitCode = 0;
287 // Give this status to the SCM
289 SERVICE_START_PENDING, // Service state
290 NO_ERROR, // Exit code type
291 45000)) { // Hint as to how long Bacula should have hung before you assume error
293 ReportStatus(SERVICE_STOPPED, g_error, 0);
294 log_error_message(_("ReportStatus STOPPED failed 1"));
298 // Now start the service for real
299 (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
303 // SERVICE START ROUTINE - thread that calls BaculaAppMain
305 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
308 // Save the current thread identifier
309 g_servicethread = GetCurrentThreadId();
311 // report the status to the service control manager.
314 SERVICE_RUNNING, // service state
315 NO_ERROR, // exit code
317 MessageBox(NULL, _("Report Service failure"), "Bacula Service", MB_OK);
318 log_error_message("ReportStatus RUNNING failed");
322 /* Call Bacula main code */
325 /* Mark that we're no longer running */
328 /* Tell the service manager that we've stopped */
329 ReportStatus(SERVICE_STOPPED, g_error, 0);
334 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
337 // Post a quit message to the main service thread
338 if (g_servicethread != 0) {
339 PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
343 // SERVICE INSTALL ROUTINE
345 bacService::InstallService()
347 const int pathlength = 2048;
348 char path[pathlength];
349 char servicecmd[pathlength];
352 // Get the filename of this executable
353 if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
354 MessageBox(NULL, _("Unable to install Bacula service"), szAppName, MB_ICONEXCLAMATION | MB_OK);
358 // Append the service-start flag to the end of the path:
359 if ((int)strlen(path) + 20 + (int)strlen(BaculaRunService) < pathlength) {
360 sprintf(servicecmd, "\"%s\" %s -c \"%s\"", path, BaculaRunService, path);
361 len = strlen(servicecmd) - 1;
362 for ( ; len > 0; len--) {
363 if (servicecmd[len] == '\\') {
369 strcat(servicecmd, "\\bacula-fd.conf");
372 log_error_message(_("Service command length too long"));
373 MessageBox(NULL, _("Service command length too long. Service not registered."),
374 szAppName, MB_ICONEXCLAMATION | MB_OK);
378 // How to add the Bacula service depends upon the OS
379 switch (g_platform_id) {
382 case VER_PLATFORM_WIN32_WINDOWS:
383 // Locate the RunService registry entry
385 if (RegCreateKey(HKEY_LOCAL_MACHINE,
386 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
387 &runservices) != ERROR_SUCCESS) {
388 log_error_message(_("Cannot write System Registry"));
389 MessageBox(NULL, _("The System Registry could not be updated - the Bacula service was not installed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
393 // Attempt to add a Bacula key
394 if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS) {
395 RegCloseKey(runservices);
396 log_error_message(_("Cannot add Bacula key to System Registry"));
397 MessageBox(NULL, _("The Bacula service could not be installed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
401 RegCloseKey(runservices);
403 // 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);
415 // Windows NT, Win2K, WinXP
416 case VER_PLATFORM_WIN32_NT:
418 SC_HANDLE hsrvmanager;
420 // Open the default, local Service Control Manager database
421 hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
422 if (hsrvmanager == NULL) {
423 log_error_message("OpenSCManager failed");
425 _("The Service Control Manager could not be contacted - the Bacula service was not installed"),
426 szAppName, MB_ICONEXCLAMATION | MB_OK);
430 // Create an entry for the Bacula service
431 hservice = CreateService(
432 hsrvmanager, // SCManager database
433 BAC_SERVICENAME, // name of service
434 BAC_SERVICEDISPLAYNAME, // name to display
435 SERVICE_ALL_ACCESS, // desired access
436 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
438 SERVICE_AUTO_START, // start type
439 SERVICE_ERROR_NORMAL, // error control type
440 servicecmd, // service's binary
441 NULL, // no load ordering group
442 NULL, // no tag identifier
443 BAC_DEPENDENCIES, // dependencies
444 NULL, // LocalSystem account
445 NULL); // no password
446 if (hservice == NULL) {
447 CloseServiceHandle(hsrvmanager);
448 log_error_message("CreateService failed");
450 _("The Bacula service could not be installed"),
451 szAppName, MB_ICONEXCLAMATION | MB_OK);
455 set_service_description(hsrvmanager,hservice,
456 _("Provides file backup and restore services. Bacula -- the network backup solution."));
458 CloseServiceHandle(hsrvmanager);
459 CloseServiceHandle(hservice);
461 // Everything went fine
464 _("The Bacula File service was successfully installed.\n"
465 "The service may be started from the Control Panel and will\n"
466 "automatically be run the next time this machine is rebooted."),
468 MB_ICONINFORMATION | MB_OK);
472 log_error_message("Unknown Windows System version");
474 _("Unknown Windows operating system.\n"
475 "Cannot install Bacula service.\n"),
476 szAppName, MB_ICONEXCLAMATION | MB_OK);
484 // SERVICE REMOVE ROUTINE
486 bacService::RemoveService()
488 // How to remove the Bacula service depends upon the OS
489 switch (g_platform_id) {
492 case VER_PLATFORM_WIN32_WINDOWS:
493 // Locate the RunService registry entry
495 if (RegOpenKey(HKEY_LOCAL_MACHINE,
496 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
497 &runservices) != ERROR_SUCCESS) {
499 _("Could not find registry entry.\nService probably not registerd - the Bacula service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
501 // Attempt to delete the Bacula key
502 if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS) {
503 RegCloseKey(runservices);
504 MessageBox(NULL, _("Could not delete Registry key.\nThe Bacula service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
507 RegCloseKey(runservices);
511 // Try to kill any running copy of Bacula
512 if (!KillRunningCopy()) {
514 _("Bacula could not be contacted, probably not running"),
515 szAppName, MB_ICONEXCLAMATION | MB_OK);
519 // We have successfully removed the service!
521 MessageBox(NULL, _("The Bacula service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
525 // Windows NT, Win2K, WinXP
526 case VER_PLATFORM_WIN32_NT:
528 SC_HANDLE hsrvmanager;
531 hsrvmanager = OpenSCManager(
532 NULL, // machine (NULL == local)
533 NULL, // database (NULL == default)
534 SC_MANAGER_ALL_ACCESS // access required
537 hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
538 if (hservice != NULL) {
539 SERVICE_STATUS status;
541 // Try to stop the Bacula service
542 if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
543 while(QueryServiceStatus(hservice, &status)) {
544 if (status.dwCurrentState == SERVICE_STOP_PENDING) {
551 if (status.dwCurrentState != SERVICE_STOPPED) {
552 MessageBox(NULL, _("The Bacula service could not be stopped"), szAppName, MB_ICONEXCLAMATION | MB_OK);
556 // Now remove the service from the SCM
557 if (DeleteService(hservice)) {
559 MessageBox(NULL, _("The Bacula service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
562 MessageBox(NULL, _("The Bacula service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
565 CloseServiceHandle(hservice);
567 MessageBox(NULL, _("The Bacula service could not be found"), szAppName, MB_ICONEXCLAMATION | MB_OK);
570 CloseServiceHandle(hsrvmanager);
572 MessageBox(NULL, _("The SCM could not be contacted - the Bacula service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
579 // USEFUL SERVICE SUPPORT ROUTINES
581 // Service control routine
582 void WINAPI ServiceCtrl(DWORD ctrlcode)
584 // What control code have we been sent?
586 case SERVICE_CONTROL_STOP:
587 // STOP : The service must stop
588 g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
592 case SERVICE_CONTROL_INTERROGATE:
593 // QUERY : Service control manager just wants to know our state
597 // Control code not recognised
601 // Tell the control manager what we're up to.
602 ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
605 // Service manager status reporting
606 BOOL ReportStatus(DWORD state,
610 static DWORD checkpoint = 1;
613 // If we're in the start state then we don't want the control manager
614 // sending us control messages because they'll confuse us.
615 if (state == SERVICE_START_PENDING) {
616 g_srvstatus.dwControlsAccepted = 0;
618 g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
621 // Save the new status we've been given
622 g_srvstatus.dwCurrentState = state;
623 g_srvstatus.dwWin32ExitCode = exitcode;
624 g_srvstatus.dwWaitHint = waithint;
626 // Update the checkpoint variable to let the SCM know that we
627 // haven't died if requests take a long time
628 if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
629 g_srvstatus.dwCheckPoint = 0;
631 g_srvstatus.dwCheckPoint = checkpoint++;
634 // Tell the SCM our new status
635 if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
636 log_error_message(_("SetServiceStatus failed"));
643 void LogErrorMsg(char *message, char *fname, int lineno)
650 // Get the error code
651 g_error = GetLastError();
652 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
653 FORMAT_MESSAGE_FROM_SYSTEM,
661 // Use event logging to log the error
662 heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
664 sprintf(msgbuff, _("\n\n%s error: %ld at %s:%d"),
665 BAC_SERVICENAME, g_error, fname, lineno);
666 strings[0] = msgbuff;
667 strings[1] = message;
670 if (heventsrc != NULL) {
674 heventsrc, // handle of event source
675 EVENTLOG_ERROR_TYPE, // event type
678 NULL, // current user's SID
679 3, // strings in 'strings'
680 0, // no bytes of raw data
681 (const char **)strings, // array of error strings
682 NULL); // no raw data
684 DeregisterEventSource(heventsrc);
688 typedef BOOL (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
690 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
694 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
695 SERVICE_DESCRIPTION sdBuf;
697 WinAPI ChangeServiceDescription;
699 HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
703 ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
704 "ChangeServiceConfig2A");
706 if (!ChangeServiceDescription) {
710 // Need to acquire database lock before reconfiguring.
711 sclLock = LockServiceDatabase(hSCManager);
713 // If the database cannot be locked, report the details.
714 if (sclLock == NULL) {
715 // Exit if the database is not locked by another process.
716 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
717 log_error_message("LockServiceDatabase");
721 // Allocate a buffer to get details about the lock.
722 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc(
723 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
724 if (lpqslsBuf == NULL) {
725 log_error_message("LocalAlloc");
729 // Get and print the lock status information.
730 if (!QueryServiceLockStatus(
733 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
735 log_error_message("QueryServiceLockStatus");
738 if (lpqslsBuf->fIsLocked) {
739 printf(_("Locked by: %s, duration: %ld seconds\n"),
740 lpqslsBuf->lpLockOwner,
741 lpqslsBuf->dwLockDuration);
743 printf(_("No longer locked\n"));
746 LocalFree(lpqslsBuf);
747 log_error_message(_("Could not lock database"));
751 // The database is locked, so it is safe to make changes.
753 sdBuf.lpDescription = lpDesc;
755 if (!ChangeServiceDescription(
756 hService, // handle to service
757 SERVICE_CONFIG_DESCRIPTION, // change: description
758 &sdBuf) ) { // value: new description
759 log_error_message("ChangeServiceConfig2");
762 // Release the database lock.
763 UnlockServiceDatabase(sclLock);