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 Free Software Foundataion Europe e.V.
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()
63 PostToBacula(UINT message, WPARAM wParam, LPARAM lParam)
65 // Locate the hidden Bacula menu window
66 HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
67 if (hservwnd == NULL) {
71 // Post the message to Bacula
72 PostMessage(hservwnd, message, wParam, lParam);
77 // Static routine to show the About dialog for a currently-running
78 // copy of Bacula, (usually a servicified version.)
81 bacService::ShowAboutBox()
83 // Post to the Bacula menu window
84 if (!PostToBacula(MENU_ABOUTBOX_SHOW, 0, 0)) {
85 MessageBox(NULL, _("No existing instance of Bacula storage service could be contacted"), szAppName, MB_ICONEXCLAMATION | MB_OK);
91 // Static routine to show the Status dialog for a currently-running
92 // copy of Bacula, (usually a servicified version.)
95 bacService::ShowStatus()
97 // Post to the Bacula menu window
98 if (!PostToBacula(MENU_STATUS_SHOW, 0, 0)) {
99 MessageBox(NULL, _("No existing instance of Bacula storage service could be contacted"), szAppName, MB_ICONEXCLAMATION | MB_OK);
105 // SERVICE-MODE ROUTINES
107 // Service-mode defines:
109 // Internal service name
110 #define BAC_SERVICENAME "Bacula-sd"
112 // Displayed service name
113 #define BAC_SERVICEDISPLAYNAME "Bacula Storage Server"
115 // List other required services
116 #define BAC_DEPENDENCIES __TEXT("tcpip\0afd\0")
119 // Internal service state
120 SERVICE_STATUS g_srvstatus; // current status of the service
121 SERVICE_STATUS_HANDLE g_hstatus;
123 DWORD g_servicethread = 0;
124 char* g_errortext[256];
127 // Forward defines of internal service functions
128 void WINAPI ServiceMain(DWORD argc, char **argv);
129 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
131 void WINAPI ServiceCtrl(DWORD ctrlcode);
132 bool WINAPI CtrlHandler (DWORD ctrltype);
133 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
135 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
137 BOOL g_servicemode = FALSE;
140 bacService::RunningAsService()
142 return g_servicemode;
146 bacService::KillRunningCopy()
148 while (PostToBacula(WM_CLOSE, 0, 0)) {
154 // SERVICE MAIN ROUTINE
156 bacService::BaculaServiceMain()
158 // Mark that we are a service
159 g_servicemode = TRUE;
161 // Create a service entry table
162 SERVICE_TABLE_ENTRY dispatchTable[] = {
163 {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
167 // Call the service control dispatcher with our entry table
168 if (!StartServiceCtrlDispatcher(dispatchTable)) {
169 log_error_message(_("StartServiceCtrlDispatcher failed."));
175 // SERVICE MAIN ROUTINE - NT ONLY !!!
176 // NT/Win2K/WinXP ONLY !!!
177 void WINAPI ServiceMain(DWORD argc, char **argv)
181 // Register the service control handler
182 g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
184 if (g_hstatus == 0) {
185 log_error_message(_("RegisterServiceCtlHandler failed"));
186 MessageBox(NULL, _("Contact Register Service Handler failure"),
187 "Bacula service", MB_OK);
191 // Set up some standard service state values
192 g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
193 g_srvstatus.dwServiceSpecificExitCode = 0;
195 // Give this status to the SCM
197 SERVICE_START_PENDING, // Service state
198 NO_ERROR, // Exit code type
199 45000)) { // Hint as to how long Bacula should have hung before you assume error
201 ReportStatus(SERVICE_STOPPED, g_error, 0);
202 log_error_message(_("ReportStatus STOPPED failed 1"));
206 // Now start the service for real
207 (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
211 // SERVICE START ROUTINE - thread that calls BaculaAppMain
213 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
216 // Save the current thread identifier
217 g_servicethread = GetCurrentThreadId();
219 // report the status to the service control manager.
222 SERVICE_RUNNING, // service state
223 NO_ERROR, // exit code
225 MessageBox(NULL, _("Report Service failure"), "Bacula Service", MB_OK);
226 log_error_message("ReportStatus RUNNING failed");
230 /* Call Bacula main code */
233 /* Mark that we're no longer running */
236 /* Tell the service manager that we've stopped */
237 ReportStatus(SERVICE_STOPPED, g_error, 0);
242 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
245 // Post a quit message to the main service thread
246 if (g_servicethread != 0) {
247 PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
251 // SERVICE INSTALL ROUTINE
253 bacService::InstallService(const char *pszCmdLine)
255 const int pathlength = 2048;
256 char path[pathlength];
257 char servicecmd[pathlength];
259 // Get the filename of this executable
260 if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
261 MessageBox(NULL, _("Unable to install Bacula Storage service"), szAppName, MB_ICONEXCLAMATION | MB_OK);
265 // Append the service-start flag to the end of the path:
266 if ((int)strlen(path) + 5 + (int)strlen(BaculaRunService) + (int)strlen(pszCmdLine) < pathlength) {
267 sprintf(servicecmd, "\"%s\" %s %s", path, BaculaRunService, pszCmdLine);
269 log_error_message(_("Service command length too long"));
270 MessageBox(NULL, _("Service command length too long. Service not registered."),
271 szAppName, MB_ICONEXCLAMATION | MB_OK);
276 SC_HANDLE hsrvmanager;
278 // Open the default, local Service Control Manager database
279 hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
280 if (hsrvmanager == NULL) {
281 log_error_message("OpenSCManager failed");
283 _("The Service Control Manager could not be contacted - the Bacula Storage service was not installed"),
284 szAppName, MB_ICONEXCLAMATION | MB_OK);
288 // Create an entry for the Bacula service
289 hservice = CreateService(
290 hsrvmanager, // SCManager database
291 BAC_SERVICENAME, // name of service
292 BAC_SERVICEDISPLAYNAME, // name to display
293 SERVICE_ALL_ACCESS, // desired access
294 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
296 SERVICE_AUTO_START, // start type
297 SERVICE_ERROR_NORMAL, // error control type
298 servicecmd, // service's binary
299 NULL, // no load ordering group
300 NULL, // no tag identifier
301 BAC_DEPENDENCIES, // dependencies
302 NULL, // LocalSystem account
303 NULL); // no password
304 if (hservice == NULL) {
305 CloseServiceHandle(hsrvmanager);
306 log_error_message("CreateService failed");
308 _("The Bacula Storage service could not be installed"),
309 szAppName, MB_ICONEXCLAMATION | MB_OK);
313 set_service_description(hsrvmanager,hservice,
314 _("Provides storage services. Bacula -- the network backup solution."));
316 CloseServiceHandle(hsrvmanager);
317 CloseServiceHandle(hservice);
319 // Everything went fine
322 _("The Bacula Storage service was successfully installed.\n"
323 "The service may be started from the Control Panel and will\n"
324 "automatically be run the next time this machine is rebooted."),
326 MB_ICONINFORMATION | MB_OK);
332 // SERVICE REMOVE ROUTINE
334 bacService::RemoveService()
337 SC_HANDLE hsrvmanager;
340 hsrvmanager = OpenSCManager(
341 NULL, // machine (NULL == local)
342 NULL, // database (NULL == default)
343 SC_MANAGER_ALL_ACCESS // access required
346 hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
347 if (hservice != NULL) {
348 SERVICE_STATUS status;
350 // Try to stop the Bacula service
351 if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
352 while(QueryServiceStatus(hservice, &status)) {
353 if (status.dwCurrentState == SERVICE_STOP_PENDING) {
360 if (status.dwCurrentState != SERVICE_STOPPED) {
361 MessageBox(NULL, _("The Bacula Storage service could not be stopped"), szAppName, MB_ICONEXCLAMATION | MB_OK);
365 // Now remove the service from the SCM
366 if(DeleteService(hservice)) {
368 MessageBox(NULL, _("The Bacula Storage service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
371 MessageBox(NULL, _("The Bacula Storage service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
374 CloseServiceHandle(hservice);
376 MessageBox(NULL, _("The Bacula Storage service could not be found"), szAppName, MB_ICONEXCLAMATION | MB_OK);
379 CloseServiceHandle(hsrvmanager);
381 MessageBox(NULL, _("The SCM could not be contacted - the Bacula Storage service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
386 // USEFUL SERVICE SUPPORT ROUTINES
388 // Service control routine
389 void WINAPI ServiceCtrl(DWORD ctrlcode)
391 // What control code have we been sent?
393 case SERVICE_CONTROL_STOP:
394 // STOP : The service must stop
395 g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
399 case SERVICE_CONTROL_INTERROGATE:
400 // QUERY : Service control manager just wants to know our state
404 // Control code not recognised
408 // Tell the control manager what we're up to.
409 ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
412 // Service manager status reporting
413 BOOL ReportStatus(DWORD state,
417 static DWORD checkpoint = 1;
420 // If we're in the start state then we don't want the control manager
421 // sending us control messages because they'll confuse us.
422 if (state == SERVICE_START_PENDING) {
423 g_srvstatus.dwControlsAccepted = 0;
425 g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
428 // Save the new status we've been given
429 g_srvstatus.dwCurrentState = state;
430 g_srvstatus.dwWin32ExitCode = exitcode;
431 g_srvstatus.dwWaitHint = waithint;
433 // Update the checkpoint variable to let the SCM know that we
434 // haven't died if requests take a long time
435 if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
436 g_srvstatus.dwCheckPoint = 0;
438 g_srvstatus.dwCheckPoint = checkpoint++;
441 // Tell the SCM our new status
442 if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
443 log_error_message(_("SetServiceStatus failed"));
450 void LogErrorMsg(char *message, char *fname, int lineno)
457 // Get the error code
458 g_error = GetLastError();
459 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
460 FORMAT_MESSAGE_FROM_SYSTEM,
468 // Use event logging to log the error
469 heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
471 sprintf(msgbuff, _("\n\n%s error: %ld at %s:%d"),
472 BAC_SERVICENAME, g_error, fname, lineno);
473 strings[0] = msgbuff;
474 strings[1] = message;
477 if (heventsrc != NULL) {
481 heventsrc, // handle of event source
482 EVENTLOG_ERROR_TYPE, // event type
485 NULL, // current user's SID
486 3, // strings in 'strings'
487 0, // no bytes of raw data
488 (const char **)strings, // array of error strings
489 NULL); // no raw data
491 DeregisterEventSource(heventsrc);
495 typedef BOOL (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
497 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
501 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
502 SERVICE_DESCRIPTION sdBuf;
504 WinAPI ChangeServiceDescription;
506 HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
510 ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
511 "ChangeServiceConfig2A");
513 if (!ChangeServiceDescription) {
517 // Need to acquire database lock before reconfiguring.
518 sclLock = LockServiceDatabase(hSCManager);
520 // If the database cannot be locked, report the details.
521 if (sclLock == NULL) {
522 // Exit if the database is not locked by another process.
523 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
524 log_error_message("LockServiceDatabase");
528 // Allocate a buffer to get details about the lock.
529 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc(
530 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
531 if (lpqslsBuf == NULL) {
532 log_error_message("LocalAlloc");
536 // Get and print the lock status information.
537 if (!QueryServiceLockStatus(
540 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
542 log_error_message("QueryServiceLockStatus");
545 if (lpqslsBuf->fIsLocked) {
546 printf(_("Locked by: %s, duration: %ld seconds\n"),
547 lpqslsBuf->lpLockOwner,
548 lpqslsBuf->dwLockDuration);
550 printf(_("No longer locked\n"));
553 LocalFree(lpqslsBuf);
554 log_error_message(_("Could not lock database"));
558 // The database is locked, so it is safe to make changes.
560 sdBuf.lpDescription = lpDesc;
562 if (!ChangeServiceDescription(
563 hService, // handle to service
564 SERVICE_CONFIG_DESCRIPTION, // change: description
565 &sdBuf) ) { // value: new description
566 log_error_message("ChangeServiceConfig2");
569 // Release the database lock.
570 UnlockServiceDatabase(sclLock);