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 bool opt_debug;
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 About dialog for a currently-running
104 // copy of Bacula, (usually a servicified version.)
107 bacService::ShowAboutBox()
109 // Post to the Bacula menu window
110 if (!PostToBacula(MENU_ABOUTBOX_SHOW, 0, 0)) {
111 MessageBox(NULL, _("No existing instance of Bacula File service could be contacted"), szAppName, MB_ICONEXCLAMATION | MB_OK);
117 // Static routine to show the Status dialog for a currently-running
118 // copy of Bacula, (usually a servicified version.)
121 bacService::ShowStatus()
123 // Post to the Bacula menu window
124 if (!PostToBacula(MENU_STATUS_SHOW, 0, 0)) {
125 MessageBox(NULL, _("No existing instance of Bacula File service could be contacted"), szAppName, MB_ICONEXCLAMATION | MB_OK);
131 // SERVICE-MODE ROUTINES
133 // Service-mode defines:
135 // Internal service name
136 #define BAC_SERVICENAME "Bacula-fd"
138 // Displayed service name
139 #define BAC_SERVICEDISPLAYNAME "Bacula File Server"
141 // List other required serves
142 #define BAC_DEPENDENCIES __TEXT("tcpip\0afd\0")
145 // Internal service state
146 SERVICE_STATUS g_srvstatus; // current status of the service
147 SERVICE_STATUS_HANDLE g_hstatus;
149 DWORD g_servicethread = 0;
150 char* g_errortext[256];
153 // Forward defines of internal service functions
154 void WINAPI ServiceMain(DWORD argc, char **argv);
155 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
157 void WINAPI ServiceCtrl(DWORD ctrlcode);
158 bool WINAPI CtrlHandler (DWORD ctrltype);
159 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
161 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
163 BOOL g_servicemode = FALSE;
166 bacService::RunningAsService()
168 return g_servicemode;
172 bacService::KillRunningCopy()
174 //while (PostToBacula(WM_CLOSE, 0, 0)) {
180 // SERVICE MAIN ROUTINE
182 bacService::BaculaServiceMain()
184 // Mark that we are a service
185 g_servicemode = TRUE;
187 // How to run as a service depends upon the OS being used
188 switch (g_platform_id) {
191 case VER_PLATFORM_WIN32_WINDOWS:
193 // Obtain a handle to the kernel library
194 HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
195 if (kerneldll == NULL) {
196 MessageBox(NULL, _("KERNEL32.DLL not found: Bacula service not started"),
197 "Bacula Service", MB_OK);
201 // And find the RegisterServiceProcess function
202 DWORD (WINAPI *RegisterService)(DWORD, DWORD);
203 RegisterService = (DWORD (WINAPI *)(DWORD, DWORD))
204 GetProcAddress(kerneldll, "RegisterServiceProcess");
205 if (RegisterService == NULL) {
206 MessageBox(NULL, _("Registry service not found: Bacula service not started"),
207 "Bacula Service", MB_OK);
208 log_error_message(_("Registry service not found"));
212 // Register this process with the OS as a service!
213 RegisterService(0, 1);
215 // Run the main program as a service
218 // Then remove the service from the system service table
219 RegisterService(0, 0);
221 // Free the kernel library
222 FreeLibrary(kerneldll);
227 // Windows NT, Win2K, WinXP
228 case VER_PLATFORM_WIN32_NT:
230 // Create a service entry table
231 SERVICE_TABLE_ENTRY dispatchTable[] = {
232 {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
236 // Call the service control dispatcher with our entry table
237 if (!StartServiceCtrlDispatcher(dispatchTable)) {
238 log_error_message(_("StartServiceCtrlDispatcher failed."));
246 // SERVICE MAIN ROUTINE - NT ONLY !!!
247 // NT/Win2K/WinXP ONLY !!!
248 void WINAPI ServiceMain(DWORD argc, char **argv)
252 // Register the service control handler
253 g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
255 if (g_hstatus == 0) {
256 log_error_message(_("RegisterServiceCtlHandler failed"));
257 MessageBox(NULL, _("Contact Register Service Handler failure"),
258 "Bacula service", MB_OK);
262 // Set up some standard service state values
263 g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
264 g_srvstatus.dwServiceSpecificExitCode = 0;
266 // Give this status to the SCM
268 SERVICE_START_PENDING, // Service state
269 NO_ERROR, // Exit code type
270 45000)) { // Hint as to how long Bacula should have hung before you assume error
272 ReportStatus(SERVICE_STOPPED, g_error, 0);
273 log_error_message(_("ReportStatus STOPPED failed 1"));
277 // Now start the service for real
278 (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
282 // SERVICE START ROUTINE - thread that calls BaculaAppMain
284 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
287 // Save the current thread identifier
288 g_servicethread = GetCurrentThreadId();
290 // report the status to the service control manager.
293 SERVICE_RUNNING, // service state
294 NO_ERROR, // exit code
296 MessageBox(NULL, _("Report Service failure"), "Bacula Service", MB_OK);
297 log_error_message("ReportStatus RUNNING failed");
301 /* Call Bacula main code */
304 /* Mark that we're no longer running */
307 /* Tell the service manager that we've stopped */
308 ReportStatus(SERVICE_STOPPED, g_error, 0);
313 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
316 // Post a quit message to the main service thread
317 if (g_servicethread != 0) {
318 PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
322 // SERVICE INSTALL ROUTINE
324 bacService::InstallService(const char *pszCmdLine)
326 const int pathlength = 2048;
327 char path[pathlength];
328 char servicecmd[pathlength];
330 // Get the filename of this executable
331 if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
332 MessageBox(NULL, _("Unable to install Bacula File service"), szAppName, MB_ICONEXCLAMATION | MB_OK);
336 // Append the service-start flag to the end of the path:
337 if ((int)strlen(path) + 20 + (int)strlen(BaculaRunService) + (int)strlen(pszCmdLine) < pathlength) {
338 sprintf(servicecmd, "\"%s\" %s %s", path, BaculaRunService, pszCmdLine);
340 log_error_message(_("Service command length too long"));
341 MessageBox(NULL, _("Service command length too long. Service not registered."),
342 szAppName, MB_ICONEXCLAMATION | MB_OK);
346 // How to add the Bacula service depends upon the OS
347 switch (g_platform_id) {
350 case VER_PLATFORM_WIN32_WINDOWS:
351 // Locate the RunService registry entry
353 if (RegCreateKey(HKEY_LOCAL_MACHINE,
354 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
355 &runservices) != ERROR_SUCCESS) {
356 log_error_message(_("Cannot write System Registry"));
357 MessageBox(NULL, _("The System Registry could not be updated - the Bacula service was not installed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
361 // Attempt to add a Bacula key
362 if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS) {
363 RegCloseKey(runservices);
364 log_error_message(_("Cannot add Bacula key to System Registry"));
365 MessageBox(NULL, _("The Bacula service could not be installed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
369 RegCloseKey(runservices);
371 // We have successfully installed the service!
374 _("The Bacula File service was successfully installed.\n"
375 "The service may be started by double clicking on the\n"
376 "Bacula \"Start\" icon and will be automatically\n"
377 "be run the next time this machine is rebooted. "),
379 MB_ICONINFORMATION | MB_OK);
383 // Windows NT, Win2K, WinXP
384 case VER_PLATFORM_WIN32_NT:
386 SC_HANDLE hsrvmanager;
388 // Open the default, local Service Control Manager database
389 hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
390 if (hsrvmanager == NULL) {
391 log_error_message("OpenSCManager failed");
393 _("The Service Control Manager could not be contacted - the Bacula service was not installed"),
394 szAppName, MB_ICONEXCLAMATION | MB_OK);
398 // Create an entry for the Bacula service
399 hservice = CreateService(
400 hsrvmanager, // SCManager database
401 BAC_SERVICENAME, // name of service
402 BAC_SERVICEDISPLAYNAME, // name to display
403 SERVICE_ALL_ACCESS, // desired access
404 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
406 SERVICE_AUTO_START, // start type
407 SERVICE_ERROR_NORMAL, // error control type
408 servicecmd, // service's binary
409 NULL, // no load ordering group
410 NULL, // no tag identifier
411 BAC_DEPENDENCIES, // dependencies
412 NULL, // LocalSystem account
413 NULL); // no password
414 if (hservice == NULL) {
415 CloseServiceHandle(hsrvmanager);
416 log_error_message("CreateService failed");
418 _("The Bacula service could not be installed"),
419 szAppName, MB_ICONEXCLAMATION | MB_OK);
423 set_service_description(hsrvmanager,hservice,
424 _("Provides file backup and restore services. Bacula -- the network backup solution."));
426 CloseServiceHandle(hsrvmanager);
427 CloseServiceHandle(hservice);
429 // Everything went fine
432 _("The Bacula File service was successfully installed.\n"
433 "The service may be started from the Control Panel and will\n"
434 "automatically be run the next time this machine is rebooted."),
436 MB_ICONINFORMATION | MB_OK);
440 log_error_message("Unknown Windows System version");
442 _("Unknown Windows operating system.\n"
443 "Cannot install Bacula service.\n"),
444 szAppName, MB_ICONEXCLAMATION | MB_OK);
452 // SERVICE REMOVE ROUTINE
454 bacService::RemoveService()
456 // How to remove the Bacula service depends upon the OS
457 switch (g_platform_id) {
460 case VER_PLATFORM_WIN32_WINDOWS:
461 // Locate the RunService registry entry
463 if (RegOpenKey(HKEY_LOCAL_MACHINE,
464 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
465 &runservices) != ERROR_SUCCESS) {
467 _("Could not find registry entry.\nService probably not registerd - the Bacula service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
469 // Attempt to delete the Bacula key
470 if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS) {
471 RegCloseKey(runservices);
472 MessageBox(NULL, _("Could not delete Registry key.\nThe Bacula service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
475 RegCloseKey(runservices);
479 // Try to kill any running copy of Bacula
480 if (!KillRunningCopy()) {
482 _("Bacula could not be contacted, probably not running"),
483 szAppName, MB_ICONEXCLAMATION | MB_OK);
487 // We have successfully removed the service!
489 MessageBox(NULL, _("The Bacula service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
493 // Windows NT, Win2K, WinXP
494 case VER_PLATFORM_WIN32_NT:
496 SC_HANDLE hsrvmanager;
499 hsrvmanager = OpenSCManager(
500 NULL, // machine (NULL == local)
501 NULL, // database (NULL == default)
502 SC_MANAGER_ALL_ACCESS // access required
505 hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
506 if (hservice != NULL) {
507 SERVICE_STATUS status;
509 // Try to stop the Bacula service
510 if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
511 while(QueryServiceStatus(hservice, &status)) {
512 if (status.dwCurrentState == SERVICE_STOP_PENDING) {
519 if (status.dwCurrentState != SERVICE_STOPPED) {
520 MessageBox(NULL, _("The Bacula file service could not be stopped"), szAppName, MB_ICONEXCLAMATION | MB_OK);
524 // Now remove the service from the SCM
525 if (DeleteService(hservice)) {
527 MessageBox(NULL, _("The Bacula file service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
530 MessageBox(NULL, _("The Bacula file service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
533 CloseServiceHandle(hservice);
535 MessageBox(NULL, _("The Bacula file service could not be found"), szAppName, MB_ICONEXCLAMATION | MB_OK);
538 CloseServiceHandle(hsrvmanager);
540 MessageBox(NULL, _("The SCM could not be contacted - the Bacula file service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
547 // USEFUL SERVICE SUPPORT ROUTINES
549 // Service control routine
550 void WINAPI ServiceCtrl(DWORD ctrlcode)
552 // What control code have we been sent?
554 case SERVICE_CONTROL_STOP:
555 // STOP : The service must stop
556 g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
560 case SERVICE_CONTROL_INTERROGATE:
561 // QUERY : Service control manager just wants to know our state
565 // Control code not recognised
569 // Tell the control manager what we're up to.
570 ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
573 // Service manager status reporting
574 BOOL ReportStatus(DWORD state,
578 static DWORD checkpoint = 1;
581 // If we're in the start state then we don't want the control manager
582 // sending us control messages because they'll confuse us.
583 if (state == SERVICE_START_PENDING) {
584 g_srvstatus.dwControlsAccepted = 0;
586 g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
589 // Save the new status we've been given
590 g_srvstatus.dwCurrentState = state;
591 g_srvstatus.dwWin32ExitCode = exitcode;
592 g_srvstatus.dwWaitHint = waithint;
594 // Update the checkpoint variable to let the SCM know that we
595 // haven't died if requests take a long time
596 if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
597 g_srvstatus.dwCheckPoint = 0;
599 g_srvstatus.dwCheckPoint = checkpoint++;
602 // Tell the SCM our new status
603 if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
604 log_error_message(_("SetServiceStatus failed"));
611 void LogErrorMsg(char *message, char *fname, int lineno)
618 // Get the error code
619 g_error = GetLastError();
620 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
621 FORMAT_MESSAGE_FROM_SYSTEM,
629 // Use event logging to log the error
630 heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
632 sprintf(msgbuff, _("\n\n%s error: %ld at %s:%d"),
633 BAC_SERVICENAME, g_error, fname, lineno);
634 strings[0] = msgbuff;
635 strings[1] = message;
638 if (heventsrc != NULL) {
640 heventsrc, // handle of event source
641 EVENTLOG_ERROR_TYPE, // event type
644 NULL, // current user's SID
645 3, // strings in 'strings'
646 0, // no bytes of raw data
647 (const char **)strings, // array of error strings
648 NULL); // no raw data
650 DeregisterEventSource(heventsrc);
655 typedef BOOL (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
657 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
661 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
662 SERVICE_DESCRIPTION sdBuf;
664 WinAPI ChangeServiceDescription;
666 HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
670 ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
671 "ChangeServiceConfig2A");
673 if (!ChangeServiceDescription) {
677 // Need to acquire database lock before reconfiguring.
678 sclLock = LockServiceDatabase(hSCManager);
680 // If the database cannot be locked, report the details.
681 if (sclLock == NULL) {
682 // Exit if the database is not locked by another process.
683 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
684 log_error_message("LockServiceDatabase");
688 // Allocate a buffer to get details about the lock.
689 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc(
690 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
691 if (lpqslsBuf == NULL) {
692 log_error_message("LocalAlloc");
696 // Get and print the lock status information.
697 if (!QueryServiceLockStatus(
700 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
702 log_error_message("QueryServiceLockStatus");
705 if (lpqslsBuf->fIsLocked) {
706 printf(_("Locked by: %s, duration: %ld seconds\n"),
707 lpqslsBuf->lpLockOwner,
708 lpqslsBuf->dwLockDuration);
710 printf(_("No longer locked\n"));
713 LocalFree(lpqslsBuf);
714 log_error_message(_("Could not lock database"));
718 // The database is locked, so it is safe to make changes.
720 sdBuf.lpDescription = lpDesc;
722 if (!ChangeServiceDescription(
723 hService, // handle to service
724 SERVICE_CONFIG_DESCRIPTION, // change: description
725 &sdBuf) ) { // value: new description
726 log_error_message("ChangeServiceConfig2");
729 // Release the database lock.
730 UnlockServiceDatabase(sclLock);