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"
44 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
47 // OS-SPECIFIC ROUTINES
49 // Create an instance of the bacService class to cause the static fields to be
50 // initialised properly
56 // SERVICE-MODE ROUTINES
58 // Service-mode defines:
60 // Internal service name
61 #define BAC_SERVICENAME "Bacula-dir"
63 // Displayed service name
64 #define BAC_SERVICEDISPLAYNAME "Bacula Director"
66 // List other required services
67 #define BAC_DEPENDENCIES __TEXT("tcpip\0afd\0")
70 // Internal service state
71 SERVICE_STATUS g_srvstatus; // current status of the service
72 SERVICE_STATUS_HANDLE g_hstatus;
74 DWORD g_servicethread = 0;
75 char* g_errortext[256];
78 // Forward defines of internal service functions
79 void WINAPI ServiceMain(DWORD argc, char **argv);
80 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
82 void WINAPI ServiceCtrl(DWORD ctrlcode);
83 bool WINAPI CtrlHandler (DWORD ctrltype);
84 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
86 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
88 BOOL g_servicemode = FALSE;
91 bacService::RunningAsService()
97 bacService::KillRunningCopy()
99 // while (PostToBacula(WM_CLOSE, 0, 0))
104 // SERVICE MAIN ROUTINE
106 bacService::BaculaServiceMain()
108 // Mark that we are a service
109 g_servicemode = TRUE;
111 // Create a service entry table
112 SERVICE_TABLE_ENTRY dispatchTable[] = {
113 {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
117 // Call the service control dispatcher with our entry table
118 if (!StartServiceCtrlDispatcher(dispatchTable)) {
119 log_error_message(_("StartServiceCtrlDispatcher failed."));
125 // SERVICE MAIN ROUTINE - NT ONLY !!!
126 // NT/Win2K/WinXP ONLY !!!
127 void WINAPI ServiceMain(DWORD argc, char **argv)
131 // Register the service control handler
132 g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
134 if (g_hstatus == 0) {
135 log_error_message(_("RegisterServiceCtlHandler failed"));
136 MessageBox(NULL, _("Contact Register Service Handler failure"),
137 "Bacula service", MB_OK);
141 // Set up some standard service state values
142 g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
143 g_srvstatus.dwServiceSpecificExitCode = 0;
145 // Give this status to the SCM
147 SERVICE_START_PENDING, // Service state
148 NO_ERROR, // Exit code type
149 45000)) { // Hint as to how long Bacula should have hung before you assume error
151 ReportStatus(SERVICE_STOPPED, g_error, 0);
152 log_error_message(_("ReportStatus STOPPED failed 1"));
156 // Now start the service for real
157 (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
161 // SERVICE START ROUTINE - thread that calls BaculaAppMain
163 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
166 // Save the current thread identifier
167 g_servicethread = GetCurrentThreadId();
169 // report the status to the service control manager.
172 SERVICE_RUNNING, // service state
173 NO_ERROR, // exit code
175 MessageBox(NULL, _("Report Service failure"), "Bacula Service", MB_OK);
176 log_error_message("ReportStatus RUNNING failed");
180 /* Call Bacula main code */
183 /* Mark that we're no longer running */
186 /* Tell the service manager that we've stopped */
187 ReportStatus(SERVICE_STOPPED, g_error, 0);
192 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
195 // Post a quit message to the main service thread
196 if (g_servicethread != 0) {
197 PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
201 // SERVICE INSTALL ROUTINE
203 bacService::InstallService(const char *pszCmdLine)
205 const int pathlength = 2048;
206 char path[pathlength];
207 char servicecmd[pathlength];
209 // Get the filename of this executable
210 if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
211 MessageBox(NULL, _("Unable to install Bacula Director service"), szAppName, MB_ICONEXCLAMATION | MB_OK);
215 // Append the service-start flag to the end of the path:
216 if ((int)strlen(path) + 5 + (int)strlen(BaculaRunService) + (int)strlen(pszCmdLine) < pathlength) {
217 sprintf(servicecmd, "\"%s\" %s %s", path, BaculaRunService, pszCmdLine);
219 log_error_message(_("Service command length too long"));
220 MessageBox(NULL, _("Service command length too long. Service not registered."),
221 szAppName, MB_ICONEXCLAMATION | MB_OK);
226 SC_HANDLE hsrvmanager;
228 // Open the default, local Service Control Manager database
229 hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
230 if (hsrvmanager == NULL) {
231 log_error_message("OpenSCManager failed");
233 _("The Service Control Manager could not be contacted - the Bacula Director service was not installed"),
234 szAppName, MB_ICONEXCLAMATION | MB_OK);
238 // Create an entry for the Bacula service
239 hservice = CreateService(
240 hsrvmanager, // SCManager database
241 BAC_SERVICENAME, // name of service
242 BAC_SERVICEDISPLAYNAME, // name to display
243 SERVICE_ALL_ACCESS, // desired access
244 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
246 SERVICE_AUTO_START, // start type
247 SERVICE_ERROR_NORMAL, // error control type
248 servicecmd, // service's binary
249 NULL, // no load ordering group
250 NULL, // no tag identifier
251 BAC_DEPENDENCIES, // dependencies
252 NULL, // LocalSystem account
253 NULL); // no password
254 if (hservice == NULL) {
255 CloseServiceHandle(hsrvmanager);
256 log_error_message("CreateService failed");
258 _("The Bacula Director service could not be installed"),
259 szAppName, MB_ICONEXCLAMATION | MB_OK);
263 set_service_description(hsrvmanager,hservice,
264 _("Provides director services. Bacula -- the network backup solution."));
266 CloseServiceHandle(hsrvmanager);
267 CloseServiceHandle(hservice);
269 // Everything went fine
272 _("The Bacula Director service was successfully installed.\n"
273 "The service may be started from the Control Panel and will\n"
274 "automatically be run the next time this machine is rebooted."),
276 MB_ICONINFORMATION | MB_OK);
282 // SERVICE REMOVE ROUTINE
284 bacService::RemoveService()
287 SC_HANDLE hsrvmanager;
290 hsrvmanager = OpenSCManager(
291 NULL, // machine (NULL == local)
292 NULL, // database (NULL == default)
293 SC_MANAGER_ALL_ACCESS // access required
296 hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
297 if (hservice != NULL) {
298 SERVICE_STATUS status;
300 // Try to stop the Bacula service
301 if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
302 while(QueryServiceStatus(hservice, &status)) {
303 if (status.dwCurrentState == SERVICE_STOP_PENDING) {
310 if (status.dwCurrentState != SERVICE_STOPPED) {
311 MessageBox(NULL, _("The Bacula Director service could not be stopped"), szAppName, MB_ICONEXCLAMATION | MB_OK);
315 // Now remove the service from the SCM
316 if(DeleteService(hservice)) {
318 MessageBox(NULL, _("The Bacula Director service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
321 MessageBox(NULL, _("The Bacula Director service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
324 CloseServiceHandle(hservice);
326 MessageBox(NULL, _("The Bacula Director service could not be found"), szAppName, MB_ICONEXCLAMATION | MB_OK);
329 CloseServiceHandle(hsrvmanager);
331 MessageBox(NULL, _("The SCM could not be contacted - the Bacula Director service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
336 // USEFUL SERVICE SUPPORT ROUTINES
338 // Service control routine
339 void WINAPI ServiceCtrl(DWORD ctrlcode)
341 // What control code have we been sent?
343 case SERVICE_CONTROL_STOP:
344 // STOP : The service must stop
345 g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
349 case SERVICE_CONTROL_INTERROGATE:
350 // QUERY : Service control manager just wants to know our state
354 // Control code not recognised
358 // Tell the control manager what we're up to.
359 ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
362 // Service manager status reporting
363 BOOL ReportStatus(DWORD state,
367 static DWORD checkpoint = 1;
370 // If we're in the start state then we don't want the control manager
371 // sending us control messages because they'll confuse us.
372 if (state == SERVICE_START_PENDING) {
373 g_srvstatus.dwControlsAccepted = 0;
375 g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
378 // Save the new status we've been given
379 g_srvstatus.dwCurrentState = state;
380 g_srvstatus.dwWin32ExitCode = exitcode;
381 g_srvstatus.dwWaitHint = waithint;
383 // Update the checkpoint variable to let the SCM know that we
384 // haven't died if requests take a long time
385 if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
386 g_srvstatus.dwCheckPoint = 0;
388 g_srvstatus.dwCheckPoint = checkpoint++;
391 // Tell the SCM our new status
392 if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
393 log_error_message(_("SetServiceStatus failed"));
400 void LogErrorMsg(char *message, char *fname, int lineno)
407 // Get the error code
408 g_error = GetLastError();
409 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
410 FORMAT_MESSAGE_FROM_SYSTEM,
418 // Use event logging to log the error
419 heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
421 sprintf(msgbuff, _("\n\n%s error: %ld at %s:%d"),
422 BAC_SERVICENAME, g_error, fname, lineno);
423 strings[0] = msgbuff;
424 strings[1] = message;
427 if (heventsrc != NULL) {
431 heventsrc, // handle of event source
432 EVENTLOG_ERROR_TYPE, // event type
435 NULL, // current user's SID
436 3, // strings in 'strings'
437 0, // no bytes of raw data
438 (const char **)strings, // array of error strings
439 NULL); // no raw data
441 DeregisterEventSource(heventsrc);
445 typedef BOOL (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
447 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
451 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
452 SERVICE_DESCRIPTION sdBuf;
454 WinAPI ChangeServiceDescription;
456 HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
460 ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
461 "ChangeServiceConfig2A");
463 if (!ChangeServiceDescription) {
467 // Need to acquire database lock before reconfiguring.
468 sclLock = LockServiceDatabase(hSCManager);
470 // If the database cannot be locked, report the details.
471 if (sclLock == NULL) {
472 // Exit if the database is not locked by another process.
473 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
474 log_error_message("LockServiceDatabase");
478 // Allocate a buffer to get details about the lock.
479 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc(
480 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
481 if (lpqslsBuf == NULL) {
482 log_error_message("LocalAlloc");
486 // Get and print the lock status information.
487 if (!QueryServiceLockStatus(
490 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
492 log_error_message("QueryServiceLockStatus");
495 if (lpqslsBuf->fIsLocked) {
496 printf(_("Locked by: %s, duration: %ld seconds\n"),
497 lpqslsBuf->lpLockOwner,
498 lpqslsBuf->dwLockDuration);
500 printf(_("No longer locked\n"));
503 LocalFree(lpqslsBuf);
504 log_error_message(_("Could not lock database"));
508 // The database is locked, so it is safe to make changes.
510 sdBuf.lpDescription = lpDesc;
512 if (!ChangeServiceDescription(
513 hService, // handle to service
514 SERVICE_CONFIG_DESCRIPTION, // change: description
515 &sdBuf) ) { // value: new description
516 log_error_message("ChangeServiceConfig2");
519 // Release the database lock.
520 UnlockServiceDatabase(sclLock);