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,
41 #include "winbacula.h"
42 #include "winservice.h"
43 //#include "wintray.h"
46 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
49 // OS-SPECIFIC ROUTINES
51 // Create an instance of the bacService class to cause the static fields to be
52 // 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)
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);
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()
130 // Post to the Bacula menu window
131 if (!PostToBacula(MENU_ABOUTBOX_SHOW, 0, 0)) {
132 MessageBox(NULL, _("No existing instance of Bacula could be contacted"), szAppName, MB_ICONEXCLAMATION | MB_OK);
139 // Static routine to show the Status dialog for a currently-running
140 // copy of Bacula, (usually a servicified version.)
143 bacService::ShowStatus()
146 // Post to the Bacula menu window
147 if (!PostToBacula(MENU_STATUS_SHOW, 0, 0)) {
148 MessageBox(NULL, _("No existing instance of Bacula could be contacted"), szAppName, MB_ICONEXCLAMATION | MB_OK);
155 // SERVICE-MODE ROUTINES
157 // Service-mode defines:
160 #define BAC_APPNAME "baculadir"
162 // Internal service name
163 #define BAC_SERVICENAME "Baculadir"
165 // Displayed service name
166 #define BAC_SERVICEDISPLAYNAME "Bacula Director"
168 // List other required services
169 #define BAC_DEPENDENCIES __TEXT("tcpip\0afd\0")
172 // Internal service state
173 SERVICE_STATUS g_srvstatus; // current status of the service
174 SERVICE_STATUS_HANDLE g_hstatus;
176 DWORD g_servicethread = 0;
177 char* g_errortext[256];
180 // Forward defines of internal service functions
181 void WINAPI ServiceMain(DWORD argc, char **argv);
182 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
184 void WINAPI ServiceCtrl(DWORD ctrlcode);
185 bool WINAPI CtrlHandler (DWORD ctrltype);
186 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
188 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
190 BOOL g_servicemode = FALSE;
193 bacService::RunningAsService()
195 return g_servicemode;
199 bacService::KillRunningCopy()
201 while (PostToBacula(WM_CLOSE, 0, 0))
206 // SERVICE MAIN ROUTINE
208 bacService::BaculaServiceMain()
210 // Mark that we are a service
211 g_servicemode = TRUE;
213 // How to run as a service depends upon the OS being used
214 switch (g_platform_id) {
217 case VER_PLATFORM_WIN32_WINDOWS:
219 // Obtain a handle to the kernel library
220 HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
221 if (kerneldll == NULL) {
222 MessageBox(NULL, _("KERNEL32.DLL not found: Bacula service not started"),
223 "Bacula Service", MB_OK);
227 // And find the RegisterServiceProcess function
228 DWORD (WINAPI *RegisterService)(DWORD, DWORD);
229 RegisterService = (DWORD (WINAPI *)(DWORD, DWORD))
230 GetProcAddress(kerneldll, "RegisterServiceProcess");
231 if (RegisterService == NULL) {
232 MessageBox(NULL, _("Registry service not found: Bacula service not started"),
233 "Bacula Service", MB_OK);
234 log_error_message(_("Registry service not found"));
238 // Register this process with the OS as a service!
239 RegisterService(0, 1);
241 // Run the main program as a service
244 // Then remove the service from the system service table
245 RegisterService(0, 0);
247 // Free the kernel library
248 FreeLibrary(kerneldll);
253 // Windows NT, Win2K, WinXP
254 case VER_PLATFORM_WIN32_NT:
256 // Create a service entry table
257 SERVICE_TABLE_ENTRY dispatchTable[] = {
258 {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
262 // Call the service control dispatcher with our entry table
263 if (!StartServiceCtrlDispatcher(dispatchTable)) {
264 log_error_message(_("StartServiceCtrlDispatcher failed."));
272 // SERVICE MAIN ROUTINE - NT ONLY !!!
273 // NT/Win2K/WinXP ONLY !!!
274 void WINAPI ServiceMain(DWORD argc, char **argv)
278 // Register the service control handler
279 g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
281 if (g_hstatus == 0) {
282 log_error_message(_("RegisterServiceCtlHandler failed"));
283 MessageBox(NULL, _("Contact Register Service Handler failure"),
284 "Bacula service", MB_OK);
288 // Set up some standard service state values
289 g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
290 g_srvstatus.dwServiceSpecificExitCode = 0;
292 // Give this status to the SCM
294 SERVICE_START_PENDING, // Service state
295 NO_ERROR, // Exit code type
296 45000)) { // Hint as to how long Bacula should have hung before you assume error
298 ReportStatus(SERVICE_STOPPED, g_error, 0);
299 log_error_message(_("ReportStatus STOPPED failed 1"));
303 // Now start the service for real
304 (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
308 // SERVICE START ROUTINE - thread that calls BaculaAppMain
310 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
313 // Save the current thread identifier
314 g_servicethread = GetCurrentThreadId();
316 // report the status to the service control manager.
319 SERVICE_RUNNING, // service state
320 NO_ERROR, // exit code
322 MessageBox(NULL, _("Report Service failure"), "Bacula Service", MB_OK);
323 log_error_message("ReportStatus RUNNING failed");
327 /* Call Bacula main code */
330 /* Mark that we're no longer running */
333 /* Tell the service manager that we've stopped */
334 ReportStatus(SERVICE_STOPPED, g_error, 0);
339 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
342 // Post a quit message to the main service thread
343 if (g_servicethread != 0) {
344 PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
348 // SERVICE INSTALL ROUTINE
350 bacService::InstallService()
352 const int pathlength = 2048;
353 char path[pathlength];
354 char servicecmd[pathlength];
357 // Get the filename of this executable
358 if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
359 MessageBox(NULL, _("Unable to install Bacula service"), szAppName, MB_ICONEXCLAMATION | MB_OK);
363 // Append the service-start flag to the end of the path:
364 if ((int)strlen(path) + 20 + (int)strlen(BaculaRunService) < pathlength) {
365 sprintf(servicecmd, "\"%s\" %s -c \"%s", path, BaculaRunService, path);
366 len = strlen(servicecmd) - 1;
367 for ( ; len > 0; len--) {
368 if (servicecmd[len] == '\\') {
369 servicecmd[len+1] = 0;
374 strcat(servicecmd, "bacula-dir.conf\"");
377 log_error_message(_("Service command length too long"));
378 MessageBox(NULL, _("Service command length too long. Service not registered."),
379 szAppName, MB_ICONEXCLAMATION | MB_OK);
383 // How to add the Bacula service depends upon the OS
384 switch (g_platform_id) {
387 case VER_PLATFORM_WIN32_WINDOWS:
388 // Locate the RunService registry entry
390 if (RegCreateKey(HKEY_LOCAL_MACHINE,
391 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
392 &runservices) != ERROR_SUCCESS) {
393 log_error_message(_("Cannot write System Registry"));
394 MessageBox(NULL, _("The System Registry could not be updated - the Bacula service was not installed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
398 // Attempt to add a Bacula key
399 if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS) {
400 RegCloseKey(runservices);
401 log_error_message(_("Cannot add Bacula key to System Registry"));
402 MessageBox(NULL, _("The Bacula service could not be installed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
406 RegCloseKey(runservices);
408 // We have successfully installed the service!
410 _("The Bacula File service was successfully installed.\n"
411 "The service may be started by double clicking on the\n"
412 "Bacula \"Start\" icon and will be automatically\n"
413 "be run the next time this machine is rebooted. "),
415 MB_ICONINFORMATION | MB_OK);
418 // Windows NT, Win2K, WinXP
419 case VER_PLATFORM_WIN32_NT:
421 SC_HANDLE hsrvmanager;
423 // Open the default, local Service Control Manager database
424 hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
425 if (hsrvmanager == NULL) {
426 log_error_message("OpenSCManager failed");
428 _("The Service Control Manager could not be contacted - the Bacula service was not installed"),
429 szAppName, MB_ICONEXCLAMATION | MB_OK);
433 // Create an entry for the Bacula service
434 hservice = CreateService(
435 hsrvmanager, // SCManager database
436 BAC_SERVICENAME, // name of service
437 BAC_SERVICEDISPLAYNAME, // name to display
438 SERVICE_ALL_ACCESS, // desired access
439 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
441 SERVICE_AUTO_START, // start type
442 SERVICE_ERROR_NORMAL, // error control type
443 servicecmd, // service's binary
444 NULL, // no load ordering group
445 NULL, // no tag identifier
446 BAC_DEPENDENCIES, // dependencies
447 NULL, // LocalSystem account
448 NULL); // no password
449 if (hservice == NULL) {
450 CloseServiceHandle(hsrvmanager);
451 log_error_message("CreateService failed");
453 _("The Bacula service could not be installed"),
454 szAppName, MB_ICONEXCLAMATION | MB_OK);
458 set_service_description(hsrvmanager,hservice,
459 _("Provides file backup and restore services. Bacula -- the network backup solution."));
461 CloseServiceHandle(hsrvmanager);
462 CloseServiceHandle(hservice);
464 // Everything went fine
466 _("The Bacula File service was successfully installed.\n"
467 "The service may be started from the Control Panel and will\n"
468 "automatically be run the next time this machine is rebooted."),
470 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()
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!
521 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)) {
557 MessageBox(NULL, _("The Bacula service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
559 MessageBox(NULL, _("The Bacula service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
562 CloseServiceHandle(hservice);
564 MessageBox(NULL, _("The Bacula service could not be found"), szAppName, MB_ICONEXCLAMATION | MB_OK);
567 CloseServiceHandle(hsrvmanager);
569 MessageBox(NULL, _("The SCM could not be contacted - the Bacula service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
576 // USEFUL SERVICE SUPPORT ROUTINES
578 // Service control routine
579 void WINAPI ServiceCtrl(DWORD ctrlcode)
581 // What control code have we been sent?
583 case SERVICE_CONTROL_STOP:
584 // STOP : The service must stop
585 g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
589 case SERVICE_CONTROL_INTERROGATE:
590 // QUERY : Service control manager just wants to know our state
594 // Control code not recognised
598 // Tell the control manager what we're up to.
599 ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
602 // Service manager status reporting
603 BOOL ReportStatus(DWORD state,
607 static DWORD checkpoint = 1;
610 // If we're in the start state then we don't want the control manager
611 // sending us control messages because they'll confuse us.
612 if (state == SERVICE_START_PENDING) {
613 g_srvstatus.dwControlsAccepted = 0;
615 g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
618 // Save the new status we've been given
619 g_srvstatus.dwCurrentState = state;
620 g_srvstatus.dwWin32ExitCode = exitcode;
621 g_srvstatus.dwWaitHint = waithint;
623 // Update the checkpoint variable to let the SCM know that we
624 // haven't died if requests take a long time
625 if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
626 g_srvstatus.dwCheckPoint = 0;
628 g_srvstatus.dwCheckPoint = checkpoint++;
631 // Tell the SCM our new status
632 if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
633 log_error_message(_("SetServiceStatus failed"));
640 void LogErrorMsg(char *message, char *fname, int lineno)
647 // Get the error code
648 g_error = GetLastError();
649 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
650 FORMAT_MESSAGE_FROM_SYSTEM,
658 // Use event logging to log the error
659 heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
661 sprintf(msgbuff, _("\n\n%s error: %ld at %s:%d"),
662 BAC_SERVICENAME, g_error, fname, lineno);
663 strings[0] = msgbuff;
664 strings[1] = message;
667 if (heventsrc != NULL) {
671 heventsrc, // handle of event source
672 EVENTLOG_ERROR_TYPE, // event type
675 NULL, // current user's SID
676 3, // strings in 'strings'
677 0, // no bytes of raw data
678 (const char **)strings, // array of error strings
679 NULL); // no raw data
681 DeregisterEventSource(heventsrc);
685 typedef BOOL (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
687 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
691 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
692 SERVICE_DESCRIPTION sdBuf;
694 WinAPI ChangeServiceDescription;
696 HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
700 ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
701 "ChangeServiceConfig2A");
703 if (!ChangeServiceDescription) {
707 // Need to acquire database lock before reconfiguring.
708 sclLock = LockServiceDatabase(hSCManager);
710 // If the database cannot be locked, report the details.
711 if (sclLock == NULL) {
712 // Exit if the database is not locked by another process.
713 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
714 log_error_message("LockServiceDatabase");
718 // Allocate a buffer to get details about the lock.
719 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc(
720 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
721 if (lpqslsBuf == NULL) {
722 log_error_message("LocalAlloc");
726 // Get and print the lock status information.
727 if (!QueryServiceLockStatus(
730 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
732 log_error_message("QueryServiceLockStatus");
735 if (lpqslsBuf->fIsLocked) {
736 printf(_("Locked by: %s, duration: %ld seconds\n"),
737 lpqslsBuf->lpLockOwner,
738 lpqslsBuf->dwLockDuration);
740 printf(_("No longer locked\n"));
743 LocalFree(lpqslsBuf);
744 log_error_message(_("Could not lock database"));
748 // The database is locked, so it is safe to make changes.
750 sdBuf.lpDescription = lpDesc;
752 if (!ChangeServiceDescription(
753 hService, // handle to service
754 SERVICE_CONFIG_DESCRIPTION, // change: description
755 &sdBuf) ) { // value: new description
756 log_error_message("ChangeServiceConfig2");
759 // Release the database lock.
760 UnlockServiceDatabase(sclLock);