1 // Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
3 // This file is 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"
45 // Error message logging
46 void LogErrorMsg(char *message);
48 void SetServicePrivileges();
51 // OS-SPECIFIC ROUTINES
53 // Create an instance of the bacService class to cause the static fields to be
54 // initialised properly
56 extern int NoGetFileAttributesEx; /* set if function no avail -- Win95 */
61 BOOL g_impersonating_user = 0;
63 bacService::bacService()
65 OSVERSIONINFO osversioninfo;
66 osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
68 // Get the current OS version
69 if (!GetVersionEx(&osversioninfo)) {
72 g_platform_id = osversioninfo.dwPlatformId;
74 if (osversioninfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
75 osversioninfo.dwMinorVersion == 0) {
76 /* Running Win95 so no GetFileAttributesEx available */
77 NoGetFileAttributesEx = 1;
82 // IsWin95 - returns a BOOL indicating whether the current OS is Win95
86 return (g_platform_id == VER_PLATFORM_WIN32_WINDOWS);
89 // IsWinNT - returns a bool indicating whether the current OS is WinNT
93 return (g_platform_id == VER_PLATFORM_WIN32_NT);
96 // Internal routine to find the Bacula menu class window and
97 // post a message to it!
100 PostToBacula(UINT message, WPARAM wParam, LPARAM lParam)
102 // Locate the hidden Bacula menu window
103 HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
104 if (hservwnd == NULL) {
108 // Post the message to Bacula
109 PostMessage(hservwnd, message, wParam, lParam);
114 // Static routine to show the Properties dialog for a currently-running
115 // copy of Bacula, (usually a servicified version.)
118 bacService::ShowProperties()
120 #ifdef properties_implemented
121 // Post to the Bacula menu window
122 if (!PostToBacula(MENU_PROPERTIES_SHOW, 0, 0)) {
123 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
130 // Static routine to show the Default Properties dialog for a currently-running
131 // copy of Bacula, (usually a servicified version.)
134 bacService::ShowDefaultProperties()
136 #ifdef properties_implemented
137 // Post to the Bacula menu window
138 if (!PostToBacula(MENU_DEFAULT_PROPERTIES_SHOW, 0, 0)) {
139 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
147 // Static routine to show the About dialog for a currently-running
148 // copy of Bacula, (usually a servicified version.)
151 bacService::ShowAboutBox()
153 // Post to the Bacula menu window
154 if (!PostToBacula(MENU_ABOUTBOX_SHOW, 0, 0)) {
155 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
161 // Static routine to show the Status dialog for a currently-running
162 // copy of Bacula, (usually a servicified version.)
165 bacService::ShowStatus()
167 // Post to the Bacula menu window
168 if (!PostToBacula(MENU_STATUS_SHOW, 0, 0)) {
169 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
175 // Static routine to show the Events dialog for a currently-running
176 // copy of Bacula, (usually a servicified version.)
179 bacService::ShowEvents()
181 // Post to the Bacula menu window
182 if (!PostToBacula(MENU_EVENTS_SHOW, 0, 0)) {
183 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
190 // Static routine to tell a locally-running instance of the server
191 // to connect out to a new client
194 bacService::PostAddNewClient(unsigned long ipaddress)
196 // Post to the Bacula menu window
197 if (!PostToBacula(MENU_ADD_CLIENT_MSG, 0, ipaddress)) {
198 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
205 // SERVICE-MODE ROUTINES
207 // Service-mode defines:
210 #define BAC_APPNAME "bacula"
212 // Internal service name
213 #define BAC_SERVICENAME "Bacula"
215 // Displayed service name
216 #define BAC_SERVICEDISPLAYNAME "Bacula File Server"
218 // List other required serves
219 #define BAC_DEPENDENCIES ""
222 // Internal service state
223 SERVICE_STATUS g_srvstatus; // current status of the service
224 SERVICE_STATUS_HANDLE g_hstatus;
226 DWORD g_servicethread = 0;
227 char* g_errortext[256];
230 // Forward defines of internal service functions
231 void WINAPI ServiceMain(DWORD argc, char **argv);
232 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
234 void WINAPI ServiceCtrl(DWORD ctrlcode);
235 bool WINAPI CtrlHandler (DWORD ctrltype);
236 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
238 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
240 BOOL g_servicemode = FALSE;
243 bacService::RunningAsService()
245 return g_servicemode;
249 bacService::KillRunningCopy()
251 while (PostToBacula(WM_CLOSE, 0, 0))
257 // ROUTINE TO POST THE HANDLE OF THE CURRENT USER TO THE RUNNING Bacula, IN ORDER
258 // THAT IT CAN LOAD THE APPROPRIATE SETTINGS. THIS IS USED ONLY BY THE SVCHELPER
259 // OPTION, WHEN RUNNING UNDER NT
261 bacService::PostUserHelperMessage()
263 // - Check the platform type
268 // - Get the current process ID
269 DWORD processId = GetCurrentProcessId();
271 // - Post it to the existing Bacula
272 if (!PostToBacula(MENU_SERVICEHELPER_MSG, 0, (LPARAM)processId)) {
276 // - Wait until it's been used
280 // ROUTINE TO PROCESS AN INCOMING INSTANCE OF THE ABOVE MESSAGE
282 bacService::ProcessUserHelperMessage(WPARAM wParam, LPARAM lParam) {
283 // - Check the platform type
284 if (!IsWinNT() || !bacService::RunningAsService()) {
288 // - Close the HKEY_CURRENT_USER key, to force NT to reload it for the new user
289 // NB: Note that this is _really_ dodgy if ANY other thread is accessing the key!
290 if (RegCloseKey(HKEY_CURRENT_USER) != ERROR_SUCCESS) {
294 // - Revert to our own identity
296 g_impersonating_user = FALSE;
298 // - Open the specified process
299 HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)lParam);
300 if (processHandle == NULL) {
304 // - Get the token for the given process
305 HANDLE userToken = NULL;
306 if (!OpenProcessToken(processHandle, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, &userToken)) {
307 CloseHandle(processHandle);
310 CloseHandle(processHandle);
312 // - Set this thread to impersonate them
313 if (!ImpersonateLoggedOnUser(userToken)) {
314 CloseHandle(userToken);
317 CloseHandle(userToken);
319 g_impersonating_user = TRUE;
323 // SERVICE MAIN ROUTINE
325 bacService::BaculaServiceMain()
327 // Mark that we are a service
328 g_servicemode = TRUE;
330 // How to run as a service depends upon the OS being used
331 switch (g_platform_id) {
334 case VER_PLATFORM_WIN32_WINDOWS:
336 // Obtain a handle to the kernel library
337 HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
338 if (kerneldll == NULL) {
339 MessageBox(NULL, "KERNEL32.DLL not found: Bacula service not started",
340 "Bacula Service", MB_OK);
344 // And find the RegisterServiceProcess function
345 DWORD (*RegisterService)(DWORD, DWORD);
346 RegisterService = (DWORD (*)(DWORD, DWORD))
347 GetProcAddress(kerneldll, "RegisterServiceProcess");
348 if (RegisterService == NULL) {
349 MessageBox(NULL, "Registry service not found: Bacula service not started",
350 "Bacula Service", MB_OK);
351 LogErrorMsg("Registry service not found");
355 // Register this process with the OS as a service!
356 RegisterService(0, 1);
358 // Run the main program as a service
361 // Then remove the service from the system service table
362 RegisterService(0, 0);
364 // Free the kernel library
365 FreeLibrary(kerneldll);
371 case VER_PLATFORM_WIN32_NT:
373 // Create a service entry table
374 SERVICE_TABLE_ENTRY dispatchTable[] = {
375 {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
379 // Call the service control dispatcher with our entry table
380 if (!StartServiceCtrlDispatcher(dispatchTable)) {
381 LogErrorMsg("StartServiceCtrlDispatcher failed.");
389 // SERVICE MAIN ROUTINE - NT ONLY !!!
390 // NT/Win2K/WinXP ONLY !!!
391 void WINAPI ServiceMain(DWORD argc, char **argv)
395 // Register the service control handler
396 g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
398 if (g_hstatus == 0) {
399 LogErrorMsg("RegisterServiceCtlHandler failed");
400 MessageBox(NULL, "Contact Register Service Handler failure",
401 "Bacula service", MB_OK);
405 // Set up some standard service state values
406 g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
407 g_srvstatus.dwServiceSpecificExitCode = 0;
409 // Give this status to the SCM
411 SERVICE_START_PENDING, // Service state
412 NO_ERROR, // Exit code type
413 45000)) { // Hint as to how long Bacula should have hung before you assume error
415 ReportStatus(SERVICE_STOPPED, g_error, 0);
416 LogErrorMsg("ReportStatus failed 1");
420 // Now start the service for real
421 (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
425 // SERVICE START ROUTINE - thread that calls BaculaAppMain
427 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
430 // Save the current thread identifier
431 g_servicethread = GetCurrentThreadId();
433 // report the status to the service control manager.
436 SERVICE_RUNNING, // service state
437 NO_ERROR, // exit code
439 MessageBox(NULL, "Report Service failure", "Bacula Service", MB_OK);
440 LogErrorMsg("ReportStatus failed 2");
444 /* Call Bacula main code */
447 /* Mark that we're no longer running */
450 /* Tell the service manager that we've stopped */
451 ReportStatus(SERVICE_STOPPED, g_error, 0);
457 * Setup privileges we think we will need. We probably do not need
458 * the SE_SECURITY_NAME, but since nothing seems to be working,
459 * we get it hoping to fix the problems.
461 void SetServicePrivileges()
464 TOKEN_PRIVILEGES tkp;
465 // Get a token for this process.
466 if (!OpenProcessToken(GetCurrentProcess(),
467 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
468 /* Forge on anyway */
471 // Get the LUID for the security privilege.
472 LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tkp.Privileges[0].Luid);
474 tkp.PrivilegeCount = 1; // one privilege to set
475 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
477 // Get the security privilege for this process.
478 AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES),
479 (PTOKEN_PRIVILEGES)NULL, (PDWORD)0);
481 // Cannot test the return value of AdjustTokenPrivileges.
482 if (GetLastError() != ERROR_SUCCESS) {
483 // MessageBox(NULL, "Get security priv failed: AdjustTokePrivileges", "backup", MB_OK);
486 // Get the LUID for the backup privilege.
487 LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &tkp.Privileges[0].Luid);
489 tkp.PrivilegeCount = 1; // one privilege to set
490 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
492 // Get the backup privilege for this process.
493 AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES),
494 (PTOKEN_PRIVILEGES)NULL, (PDWORD)0);
496 // Cannot test the return value of AdjustTokenPrivileges.
497 if (GetLastError() != ERROR_SUCCESS) {
498 // MessageBox(NULL, "Get backup priv failed: AdjustTokePrivileges", "backup", MB_OK);
501 // Get the LUID for the restore privilege.
502 LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &tkp.Privileges[0].Luid);
504 tkp.PrivilegeCount = 1; // one privilege to set
505 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
507 // Get the restore privilege for this process.
508 AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES),
509 (PTOKEN_PRIVILEGES)NULL, (PDWORD)0);
511 // Cannot test the return value of AdjustTokenPrivileges.
512 if (GetLastError() != ERROR_SUCCESS) {
513 // MessageBox(NULL, "Get restore priv failed: AdjustTokePrivileges", "restore", MB_OK);
521 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
524 // Post a quit message to the main service thread
525 if (g_servicethread != 0) {
526 PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
530 // SERVICE INSTALL ROUTINE
532 bacService::InstallService()
534 const int pathlength = 2048;
535 char path[pathlength];
536 char servicecmd[pathlength];
539 // Get the filename of this executable
540 if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
541 MessageBox(NULL, "Unable to install Bacula service", szAppName, MB_ICONEXCLAMATION | MB_OK);
545 // Append the service-start flag to the end of the path:
546 if ((int)strlen(path) + 20 + (int)strlen(BaculaRunService) < pathlength) {
547 sprintf(servicecmd, "\"%s\" %s -c %s", path, BaculaRunService, path);
548 len = strlen(servicecmd) - 1;
549 for ( ; len > 0; len--) {
550 if (servicecmd[len] == '\\') {
556 strcat(servicecmd, "\\bacula-fd.conf");
559 LogErrorMsg("Service commend length too long");
560 MessageBox(NULL, "Service command length too long. Service not registered.",
561 szAppName, MB_ICONEXCLAMATION | MB_OK);
565 // How to add the Bacula service depends upon the OS
566 switch (g_platform_id) {
569 case VER_PLATFORM_WIN32_WINDOWS:
570 // Locate the RunService registry entry
572 if (RegCreateKey(HKEY_LOCAL_MACHINE,
573 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
574 &runservices) != ERROR_SUCCESS) {
575 LogErrorMsg("Cannot write System Registry");
576 MessageBox(NULL, "The System Registry could not be updated - the Bacula service was not installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
580 // Attempt to add a Bacula key
581 if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS) {
582 RegCloseKey(runservices);
583 LogErrorMsg("Cannot add Bacula key to System Registry");
584 MessageBox(NULL, "The Bacula service could not be installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
588 RegCloseKey(runservices);
590 // We have successfully installed the service!
592 "The Bacula File service was successfully installed.\n"
593 "The service may be started by double clicking on the\n"
594 "Bacula \"Start\" icon and will be automatically\n"
595 "be run the next time this machine is rebooted. ",
597 MB_ICONINFORMATION | MB_OK);
600 // Run the service...
604 si.lpReserved = NULL;
605 si.lpReserved2 = NULL;
608 PROCESS_INFORMATION pi;
609 if (!CreateProcess(NULL, servicecmd, // Program name & path
610 NULL, NULL, // Security attributes
611 FALSE, // Inherit handles?
612 NORMAL_PRIORITY_CLASS, // Extra startup flags
613 NULL, // Environment table
614 NULL, // Current directory
618 MessageBox(NULL, "CreateProcess: the Bacula service failed to start", szAppName, MB_ICONSTOP | MB_OK);
625 case VER_PLATFORM_WIN32_NT:
627 SC_HANDLE hsrvmanager;
629 // Open the default, local Service Control Manager database
630 hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
631 if (hsrvmanager == NULL) {
632 LogErrorMsg("OpenSCManager failed");
634 "The Service Control Manager could not be contacted - the Bacula service was not installed",
635 szAppName, MB_ICONEXCLAMATION | MB_OK);
639 // Create an entry for the Bacula service
640 hservice = CreateService(
641 hsrvmanager, // SCManager database
642 BAC_SERVICENAME, // name of service
643 BAC_SERVICEDISPLAYNAME, // name to display
644 SERVICE_ALL_ACCESS, // desired access
645 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
647 SERVICE_AUTO_START, // start type
648 SERVICE_ERROR_NORMAL, // error control type
649 servicecmd, // service's binary
650 NULL, // no load ordering group
651 NULL, // no tag identifier
652 BAC_DEPENDENCIES, // dependencies
653 NULL, // LocalSystem account
654 NULL); // no password
655 if (hservice == NULL) {
656 CloseServiceHandle(hsrvmanager);
657 LogErrorMsg("CreateService failed");
659 "The Bacula service could not be installed",
660 szAppName, MB_ICONEXCLAMATION | MB_OK);
664 /*****FIXME***** add code to set Description */
666 CloseServiceHandle(hsrvmanager);
667 CloseServiceHandle(hservice);
669 // Now install the servicehelper registry setting...
670 // Locate the RunService registry entry
672 if (RegCreateKey(HKEY_LOCAL_MACHINE,
673 "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
674 &runapps) != ERROR_SUCCESS) {
675 MessageBox(NULL, "WARNING: Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded",
676 szAppName, MB_ICONEXCLAMATION | MB_OK);
678 char servicehelpercmd[pathlength];
680 // Append the service-helper-start flag to the end of the path:
681 if ((int)strlen(path) + 4 + (int)strlen(BaculaRunServiceHelper) < pathlength) {
682 sprintf(servicehelpercmd, "\"%s\" %s", path, BaculaRunServiceHelper);
684 // Add the Bacula Service Helper entry
685 if (RegSetValueEx(runapps, szAppName, 0, REG_SZ,
686 (unsigned char *)servicehelpercmd, strlen(servicehelpercmd)+1) != ERROR_SUCCESS) {
687 MessageBox(NULL, "WARNING:Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded", szAppName, MB_ICONEXCLAMATION | MB_OK);
689 RegCloseKey(runapps);
693 // Everything went fine
695 "The Bacula File service was successfully installed.\n"
696 "The service may be started from the Control Panel and will\n"
697 "automatically be run the next time this machine is rebooted.",
699 MB_ICONINFORMATION | MB_OK);
702 LogErrorMsg("Unknow Windows OP Sys");
704 "Unknown Windows operating system.\n"
705 "Cannot install Bacula service.\n",
706 szAppName, MB_ICONEXCLAMATION | MB_OK);
714 // SERVICE REMOVE ROUTINE
716 bacService::RemoveService()
718 // How to remove the Bacula service depends upon the OS
719 switch (g_platform_id) {
722 case VER_PLATFORM_WIN32_WINDOWS:
723 // Locate the RunService registry entry
725 if (RegOpenKey(HKEY_LOCAL_MACHINE,
726 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
727 &runservices) != ERROR_SUCCESS) {
729 "Could not find registry entry.\nService probably not registerd - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
731 // Attempt to delete the Bacula key
732 if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS) {
733 RegCloseKey(runservices);
734 MessageBox(NULL, "Could not delete Registry key.\nThe Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
737 RegCloseKey(runservices);
741 // Try to kill any running copy of Bacula
742 if (!KillRunningCopy()) {
744 "Bacula could not be contacted, probably not running",
745 szAppName, MB_ICONEXCLAMATION | MB_OK);
749 // We have successfully removed the service!
750 MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
754 case VER_PLATFORM_WIN32_NT:
756 SC_HANDLE hsrvmanager;
758 // Attempt to remove the service-helper hook
760 if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
761 &runapps) == ERROR_SUCCESS) {
762 // Attempt to delete the Bacula key
763 if (RegDeleteValue(runapps, szAppName) != ERROR_SUCCESS) {
764 MessageBox(NULL, "WARNING:The ServiceHelper hook entry could not be removed from the registry", szAppName, MB_ICONEXCLAMATION | MB_OK);
766 RegCloseKey(runapps);
770 hsrvmanager = OpenSCManager(
771 NULL, // machine (NULL == local)
772 NULL, // database (NULL == default)
773 SC_MANAGER_ALL_ACCESS // access required
776 hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
777 if (hservice != NULL) {
778 SERVICE_STATUS status;
780 // Try to stop the Bacula service
781 if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
782 while(QueryServiceStatus(hservice, &status)) {
783 if (status.dwCurrentState == SERVICE_STOP_PENDING) {
790 if (status.dwCurrentState != SERVICE_STOPPED) {
791 MessageBox(NULL, "The Bacula service could not be stopped", szAppName, MB_ICONEXCLAMATION | MB_OK);
795 // Now remove the service from the SCM
796 if(DeleteService(hservice)) {
797 MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
799 MessageBox(NULL, "The Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
802 CloseServiceHandle(hservice);
804 MessageBox(NULL, "The Bacula service could not be found", szAppName, MB_ICONEXCLAMATION | MB_OK);
807 CloseServiceHandle(hsrvmanager);
809 MessageBox(NULL, "The SCM could not be contacted - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
816 // USEFUL SERVICE SUPPORT ROUTINES
818 // Service control routine
819 void WINAPI ServiceCtrl(DWORD ctrlcode)
821 // What control code have we been sent?
823 case SERVICE_CONTROL_STOP:
824 // STOP : The service must stop
825 g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
829 case SERVICE_CONTROL_INTERROGATE:
830 // QUERY : Service control manager just wants to know our state
834 // Control code not recognised
839 // Tell the control manager what we're up to.
840 ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
843 // Service manager status reporting
844 BOOL ReportStatus(DWORD state,
848 static DWORD checkpoint = 1;
851 // If we're in the start state then we don't want the control manager
852 // sending us control messages because they'll confuse us.
853 if (state == SERVICE_START_PENDING) {
854 g_srvstatus.dwControlsAccepted = 0;
856 g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
859 // Save the new status we've been given
860 g_srvstatus.dwCurrentState = state;
861 g_srvstatus.dwWin32ExitCode = exitcode;
862 g_srvstatus.dwWaitHint = waithint;
864 // Update the checkpoint variable to let the SCM know that we
865 // haven't died if requests take a long time
866 if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
867 g_srvstatus.dwCheckPoint = 0;
869 g_srvstatus.dwCheckPoint = checkpoint++;
872 // Tell the SCM our new status
873 if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
874 LogErrorMsg("SetServiceStatus failed");
881 void LogErrorMsg(char *message)
888 // Get the error code
889 g_error = GetLastError();
890 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
891 FORMAT_MESSAGE_FROM_SYSTEM,
899 // Use event logging to log the error
900 heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
902 sprintf(msgbuff, "%s error: %ld", BAC_SERVICENAME, g_error);
903 strings[0] = msgbuff;
904 strings[1] = message;
907 if (heventsrc != NULL) {
911 heventsrc, // handle of event source
912 EVENTLOG_ERROR_TYPE, // event type
915 NULL, // current user's SID
916 3, // strings in 'strings'
917 0, // no bytes of raw data
918 (const char **)strings, // array of error strings
919 NULL); // no raw data
921 DeregisterEventSource(heventsrc);
926 /* ================== Not yet implemented ===================== */
928 VOID ReconfigureSampleService(BOOL fDisable, LPSTR lpDesc)
931 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
932 SERVICE_DESCRIPTION sdBuf;
933 DWORD dwBytesNeeded, dwStartType;
935 // Need to acquire database lock before reconfiguring.
937 sclLock = LockServiceDatabase(schSCManager);
939 // If the database cannot be locked, report the details.
943 // Exit if the database is not locked by another process.
945 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED)
946 MyErrorExit("LockServiceDatabase");
948 // Allocate a buffer to get details about the lock.
950 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS) LocalAlloc(
951 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
952 if (lpqslsBuf == NULL)
953 MyErrorExit("LocalAlloc");
955 // Get and print the lock status information.
957 if (!QueryServiceLockStatus(
960 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
962 MyErrorExit("QueryServiceLockStatus");
964 if (lpqslsBuf->fIsLocked)
965 printf("Locked by: %s, duration: %d seconds\n",
966 lpqslsBuf->lpLockOwner,
967 lpqslsBuf->dwLockDuration);
969 printf("No longer locked\n");
971 LocalFree(lpqslsBuf);
972 MyErrorExit("Could not lock database");
975 // The database is locked, so it is safe to make changes.
977 // Open a handle to the service.
979 schService = OpenService(
980 schSCManager, // SCManager database
981 "Sample_Srv", // name of service
982 SERVICE_CHANGE_CONFIG); // need CHANGE access
983 if (schService == NULL)
984 MyErrorExit("OpenService");
986 dwStartType = (fDisable) ? SERVICE_DISABLED :
987 SERVICE_DEMAND_START;
991 if (! ChangeServiceConfig(
992 schService, // handle of service
993 SERVICE_NO_CHANGE, // service type: no change
994 dwStartType, // change service start type
995 SERVICE_NO_CHANGE, // error control: no change
996 NULL, // binary path: no change
997 NULL, // load order group: no change
998 NULL, // tag ID: no change
999 NULL, // dependencies: no change
1000 NULL, // account name: no change
1001 NULL, // password: no change
1002 NULL) ) // display name: no change
1004 MyErrorExit("ChangeServiceConfig");
1007 printf("ChangeServiceConfig SUCCESS\n");
1009 sdBuf.lpDescription = lpDesc;
1011 if( !ChangeServiceConfig2(
1012 schService, // handle to service
1013 SERVICE_CONFIG_DESCRIPTION // change: description
1014 &sdBuf) ) // value: new description
1016 MyErrorExit("ChangeServiceConfig2");
1019 printf("ChangeServiceConfig2 SUCCESS\n");
1021 // Release the database lock.
1023 UnlockServiceDatabase(sclLock);
1025 // Close the handle to the service.
1027 CloseServiceHandle(schService);