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;
77 // IsWin95 - returns a BOOL indicating whether the current OS is Win95
81 return (g_platform_id == VER_PLATFORM_WIN32_WINDOWS);
84 // IsWinNT - returns a bool indicating whether the current OS is WinNT
88 return (g_platform_id == VER_PLATFORM_WIN32_NT);
91 // Internal routine to find the Bacula menu class window and
92 // post a message to it!
95 PostToBacula(UINT message, WPARAM wParam, LPARAM lParam)
97 // Locate the hidden Bacula menu window
98 HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
99 if (hservwnd == NULL) {
103 // Post the message to Bacula
104 PostMessage(hservwnd, message, wParam, lParam);
109 // Static routine to show the Properties dialog for a currently-running
110 // copy of Bacula, (usually a servicified version.)
113 bacService::ShowProperties()
115 #ifdef properties_implemented
116 // Post to the Bacula menu window
117 if (!PostToBacula(MENU_PROPERTIES_SHOW, 0, 0)) {
118 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
125 // Static routine to show the Default Properties dialog for a currently-running
126 // copy of Bacula, (usually a servicified version.)
129 bacService::ShowDefaultProperties()
131 #ifdef properties_implemented
132 // Post to the Bacula menu window
133 if (!PostToBacula(MENU_DEFAULT_PROPERTIES_SHOW, 0, 0)) {
134 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
142 // Static routine to show the About dialog for a currently-running
143 // copy of Bacula, (usually a servicified version.)
146 bacService::ShowAboutBox()
148 // Post to the Bacula menu window
149 if (!PostToBacula(MENU_ABOUTBOX_SHOW, 0, 0)) {
150 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
156 // Static routine to show the Status dialog for a currently-running
157 // copy of Bacula, (usually a servicified version.)
160 bacService::ShowStatus()
162 // Post to the Bacula menu window
163 if (!PostToBacula(MENU_STATUS_SHOW, 0, 0)) {
164 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
170 // Static routine to show the Events dialog for a currently-running
171 // copy of Bacula, (usually a servicified version.)
174 bacService::ShowEvents()
176 // Post to the Bacula menu window
177 if (!PostToBacula(MENU_EVENTS_SHOW, 0, 0)) {
178 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
185 // Static routine to tell a locally-running instance of the server
186 // to connect out to a new client
189 bacService::PostAddNewClient(unsigned long ipaddress)
191 // Post to the Bacula menu window
192 if (!PostToBacula(MENU_ADD_CLIENT_MSG, 0, ipaddress)) {
193 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
200 // SERVICE-MODE ROUTINES
202 // Service-mode defines:
205 #define BAC_APPNAME "bacula"
207 // Internal service name
208 #define BAC_SERVICENAME "Bacula"
210 // Displayed service name
211 #define BAC_SERVICEDISPLAYNAME "Bacula File Server"
213 // List other required serves
214 #define BAC_DEPENDENCIES ""
217 // Internal service state
218 SERVICE_STATUS g_srvstatus; // current status of the service
219 SERVICE_STATUS_HANDLE g_hstatus;
221 DWORD g_servicethread = 0;
222 char* g_errortext[256];
225 // Forward defines of internal service functions
226 void WINAPI ServiceMain(DWORD argc, char **argv);
227 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
229 void WINAPI ServiceCtrl(DWORD ctrlcode);
230 bool WINAPI CtrlHandler (DWORD ctrltype);
231 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
233 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
235 BOOL g_servicemode = FALSE;
238 bacService::RunningAsService()
240 return g_servicemode;
244 bacService::KillRunningCopy()
246 while (PostToBacula(WM_CLOSE, 0, 0))
252 // ROUTINE TO POST THE HANDLE OF THE CURRENT USER TO THE RUNNING Bacula, IN ORDER
253 // THAT IT CAN LOAD THE APPROPRIATE SETTINGS. THIS IS USED ONLY BY THE SVCHELPER
254 // OPTION, WHEN RUNNING UNDER NT
256 bacService::PostUserHelperMessage()
258 // - Check the platform type
263 // - Get the current process ID
264 DWORD processId = GetCurrentProcessId();
266 // - Post it to the existing Bacula
267 if (!PostToBacula(MENU_SERVICEHELPER_MSG, 0, (LPARAM)processId)) {
271 // - Wait until it's been used
275 // ROUTINE TO PROCESS AN INCOMING INSTANCE OF THE ABOVE MESSAGE
277 bacService::ProcessUserHelperMessage(WPARAM wParam, LPARAM lParam) {
278 // - Check the platform type
279 if (!IsWinNT() || !bacService::RunningAsService()) {
283 // - Close the HKEY_CURRENT_USER key, to force NT to reload it for the new user
284 // NB: Note that this is _really_ dodgy if ANY other thread is accessing the key!
285 if (RegCloseKey(HKEY_CURRENT_USER) != ERROR_SUCCESS) {
289 // - Revert to our own identity
291 g_impersonating_user = FALSE;
293 // - Open the specified process
294 HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)lParam);
295 if (processHandle == NULL) {
299 // - Get the token for the given process
300 HANDLE userToken = NULL;
301 if (!OpenProcessToken(processHandle, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, &userToken)) {
302 CloseHandle(processHandle);
305 CloseHandle(processHandle);
307 // - Set this thread to impersonate them
308 if (!ImpersonateLoggedOnUser(userToken)) {
309 CloseHandle(userToken);
312 CloseHandle(userToken);
314 g_impersonating_user = TRUE;
318 // SERVICE MAIN ROUTINE
320 bacService::BaculaServiceMain()
322 // Mark that we are a service
323 g_servicemode = TRUE;
325 // How to run as a service depends upon the OS being used
326 switch (g_platform_id) {
329 case VER_PLATFORM_WIN32_WINDOWS:
331 // Obtain a handle to the kernel library
332 HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
333 if (kerneldll == NULL) {
334 MessageBox(NULL, "KERNEL32.DLL not found: Bacula service not started",
335 "Bacula Service", MB_OK);
339 /* Test for GetFileAttributesEx which is not in Win95 */
340 if (GetProcAddress(kerneldll, "GetFileAttributesEx") == NULL) {
341 NoGetFileAttributesEx = 1;
342 /*****FIXME***** remove after testing */
343 MessageBox(NULL, "winserv NoGetFileAttributesEx", "Bacula", MB_OK);
345 MessageBox(NULL, "winserv Got GetFileAttributesEx", "Bacula", MB_OK);
348 // And find the RegisterServiceProcess function
349 DWORD (*RegisterService)(DWORD, DWORD);
350 RegisterService = (DWORD (*)(DWORD, DWORD))
351 GetProcAddress(kerneldll, "RegisterServiceProcess");
352 if (RegisterService == NULL) {
353 MessageBox(NULL, "Registry service not found: Bacula service not started",
354 "Bacula Service", MB_OK);
355 LogErrorMsg("Registry service not found");
359 // Register this process with the OS as a service!
360 RegisterService(0, 1);
362 // Run the main program as a service
365 // Then remove the service from the system service table
366 RegisterService(0, 0);
368 // Free the kernel library
369 FreeLibrary(kerneldll);
375 case VER_PLATFORM_WIN32_NT:
377 // Create a service entry table
378 SERVICE_TABLE_ENTRY dispatchTable[] = {
379 {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
383 // Call the service control dispatcher with our entry table
384 if (!StartServiceCtrlDispatcher(dispatchTable)) {
385 LogErrorMsg("StartServiceCtrlDispatcher failed.");
393 // SERVICE MAIN ROUTINE - NT ONLY !!!
394 // NT/Win2K/WinXP ONLY !!!
395 void WINAPI ServiceMain(DWORD argc, char **argv)
399 // Register the service control handler
400 g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
402 if (g_hstatus == 0) {
403 LogErrorMsg("RegisterServiceCtlHandler failed");
404 MessageBox(NULL, "Contact Register Service Handler failure",
405 "Bacula service", MB_OK);
409 // Set up some standard service state values
410 g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
411 g_srvstatus.dwServiceSpecificExitCode = 0;
413 // Give this status to the SCM
415 SERVICE_START_PENDING, // Service state
416 NO_ERROR, // Exit code type
417 45000)) { // Hint as to how long Bacula should have hung before you assume error
419 ReportStatus(SERVICE_STOPPED, g_error, 0);
420 LogErrorMsg("ReportStatus failed 1");
424 // Now start the service for real
425 (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
429 // SERVICE START ROUTINE - thread that calls BaculaAppMain
431 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
434 // Save the current thread identifier
435 g_servicethread = GetCurrentThreadId();
437 // report the status to the service control manager.
440 SERVICE_RUNNING, // service state
441 NO_ERROR, // exit code
443 MessageBox(NULL, "Report Service failure", "Bacula Service", MB_OK);
444 LogErrorMsg("ReportStatus failed 2");
448 /* Call Bacula main code */
451 /* Mark that we're no longer running */
454 /* Tell the service manager that we've stopped */
455 ReportStatus(SERVICE_STOPPED, g_error, 0);
461 * Setup privileges we think we will need. We probably do not need
462 * the SE_SECURITY_NAME, but since nothing seems to be working,
463 * we get it hoping to fix the problems.
465 void SetServicePrivileges()
468 TOKEN_PRIVILEGES tkp;
469 // Get a token for this process.
470 if (!OpenProcessToken(GetCurrentProcess(),
471 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
472 /* Forge on anyway */
475 // Get the LUID for the security privilege.
476 LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tkp.Privileges[0].Luid);
478 tkp.PrivilegeCount = 1; // one privilege to set
479 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
481 // Get the security privilege for this process.
482 AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES),
483 (PTOKEN_PRIVILEGES)NULL, (PDWORD)0);
485 // Cannot test the return value of AdjustTokenPrivileges.
486 if (GetLastError() != ERROR_SUCCESS) {
487 // MessageBox(NULL, "Get security priv failed: AdjustTokePrivileges", "backup", MB_OK);
490 // Get the LUID for the backup privilege.
491 LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &tkp.Privileges[0].Luid);
493 tkp.PrivilegeCount = 1; // one privilege to set
494 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
496 // Get the backup privilege for this process.
497 AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES),
498 (PTOKEN_PRIVILEGES)NULL, (PDWORD)0);
500 // Cannot test the return value of AdjustTokenPrivileges.
501 if (GetLastError() != ERROR_SUCCESS) {
502 // MessageBox(NULL, "Get backup priv failed: AdjustTokePrivileges", "backup", MB_OK);
505 // Get the LUID for the restore privilege.
506 LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &tkp.Privileges[0].Luid);
508 tkp.PrivilegeCount = 1; // one privilege to set
509 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
511 // Get the restore privilege for this process.
512 AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES),
513 (PTOKEN_PRIVILEGES)NULL, (PDWORD)0);
515 // Cannot test the return value of AdjustTokenPrivileges.
516 if (GetLastError() != ERROR_SUCCESS) {
517 // MessageBox(NULL, "Get restore priv failed: AdjustTokePrivileges", "restore", MB_OK);
525 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
528 // Post a quit message to the main service thread
529 if (g_servicethread != 0) {
530 PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
534 // SERVICE INSTALL ROUTINE
536 bacService::InstallService()
538 const int pathlength = 2048;
539 char path[pathlength];
540 char servicecmd[pathlength];
543 // Get the filename of this executable
544 if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
545 MessageBox(NULL, "Unable to install Bacula service", szAppName, MB_ICONEXCLAMATION | MB_OK);
549 // Append the service-start flag to the end of the path:
550 if ((int)strlen(path) + 20 + (int)strlen(BaculaRunService) < pathlength) {
551 sprintf(servicecmd, "\"%s\" %s -c %s", path, BaculaRunService, path);
552 len = strlen(servicecmd) - 1;
553 for ( ; len > 0; len--) {
554 if (servicecmd[len] == '\\') {
560 strcat(servicecmd, "\\bacula-fd.conf");
563 LogErrorMsg("Service commend length too long");
564 MessageBox(NULL, "Service command length too long. Service not registered.",
565 szAppName, MB_ICONEXCLAMATION | MB_OK);
569 // How to add the Bacula service depends upon the OS
570 switch (g_platform_id) {
573 case VER_PLATFORM_WIN32_WINDOWS:
574 // Locate the RunService registry entry
576 if (RegCreateKey(HKEY_LOCAL_MACHINE,
577 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
578 &runservices) != ERROR_SUCCESS) {
579 LogErrorMsg("Cannot write System Registry");
580 MessageBox(NULL, "The System Registry could not be updated - the Bacula service was not installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
584 // Attempt to add a Bacula key
585 if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS) {
586 RegCloseKey(runservices);
587 LogErrorMsg("Cannot add Bacula key to System Registry");
588 MessageBox(NULL, "The Bacula service could not be installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
592 RegCloseKey(runservices);
594 // We have successfully installed the service!
596 "The Bacula File service was successfully installed.\n"
597 "The service may be started by double clicking on the\n"
598 "Bacula \"Start\" icon and will be automatically\n"
599 "be run the next time this machine is rebooted. ",
601 MB_ICONINFORMATION | MB_OK);
604 // Run the service...
608 si.lpReserved = NULL;
609 si.lpReserved2 = NULL;
612 PROCESS_INFORMATION pi;
613 if (!CreateProcess(NULL, servicecmd, // Program name & path
614 NULL, NULL, // Security attributes
615 FALSE, // Inherit handles?
616 NORMAL_PRIORITY_CLASS, // Extra startup flags
617 NULL, // Environment table
618 NULL, // Current directory
622 MessageBox(NULL, "CreateProcess: the Bacula service failed to start", szAppName, MB_ICONSTOP | MB_OK);
629 case VER_PLATFORM_WIN32_NT:
631 SC_HANDLE hsrvmanager;
633 // Open the default, local Service Control Manager database
634 hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
635 if (hsrvmanager == NULL) {
636 LogErrorMsg("OpenSCManager failed");
638 "The Service Control Manager could not be contacted - the Bacula service was not installed",
639 szAppName, MB_ICONEXCLAMATION | MB_OK);
643 // Create an entry for the Bacula service
644 hservice = CreateService(
645 hsrvmanager, // SCManager database
646 BAC_SERVICENAME, // name of service
647 BAC_SERVICEDISPLAYNAME, // name to display
648 SERVICE_ALL_ACCESS, // desired access
649 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
651 SERVICE_AUTO_START, // start type
652 SERVICE_ERROR_NORMAL, // error control type
653 servicecmd, // service's binary
654 NULL, // no load ordering group
655 NULL, // no tag identifier
656 BAC_DEPENDENCIES, // dependencies
657 NULL, // LocalSystem account
658 NULL); // no password
659 if (hservice == NULL) {
660 CloseServiceHandle(hsrvmanager);
661 LogErrorMsg("CreateService failed");
663 "The Bacula service could not be installed",
664 szAppName, MB_ICONEXCLAMATION | MB_OK);
668 /*****FIXME***** add code to set Description */
670 CloseServiceHandle(hsrvmanager);
671 CloseServiceHandle(hservice);
673 // Now install the servicehelper registry setting...
674 // Locate the RunService registry entry
676 if (RegCreateKey(HKEY_LOCAL_MACHINE,
677 "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
678 &runapps) != ERROR_SUCCESS) {
679 MessageBox(NULL, "WARNING: Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded",
680 szAppName, MB_ICONEXCLAMATION | MB_OK);
682 char servicehelpercmd[pathlength];
684 // Append the service-helper-start flag to the end of the path:
685 if ((int)strlen(path) + 4 + (int)strlen(BaculaRunServiceHelper) < pathlength) {
686 sprintf(servicehelpercmd, "\"%s\" %s", path, BaculaRunServiceHelper);
688 // Add the Bacula Service Helper entry
689 if (RegSetValueEx(runapps, szAppName, 0, REG_SZ,
690 (unsigned char *)servicehelpercmd, strlen(servicehelpercmd)+1) != ERROR_SUCCESS) {
691 MessageBox(NULL, "WARNING:Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded", szAppName, MB_ICONEXCLAMATION | MB_OK);
693 RegCloseKey(runapps);
697 // Everything went fine
699 "The Bacula File service was successfully installed.\n"
700 "The service may be started from the Control Panel and will\n"
701 "automatically be run the next time this machine is rebooted.",
703 MB_ICONINFORMATION | MB_OK);
706 LogErrorMsg("Unknow Windows OP Sys");
708 "Unknown Windows operating system.\n"
709 "Cannot install Bacula service.\n",
710 szAppName, MB_ICONEXCLAMATION | MB_OK);
718 // SERVICE REMOVE ROUTINE
720 bacService::RemoveService()
722 // How to remove the Bacula service depends upon the OS
723 switch (g_platform_id) {
726 case VER_PLATFORM_WIN32_WINDOWS:
727 // Locate the RunService registry entry
729 if (RegOpenKey(HKEY_LOCAL_MACHINE,
730 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
731 &runservices) != ERROR_SUCCESS) {
733 "Could not find registry entry.\nService probably not registerd - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
735 // Attempt to delete the Bacula key
736 if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS) {
737 RegCloseKey(runservices);
738 MessageBox(NULL, "Could not delete Registry key.\nThe Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
741 RegCloseKey(runservices);
745 // Try to kill any running copy of Bacula
746 if (!KillRunningCopy()) {
748 "Bacula could not be contacted, probably not running",
749 szAppName, MB_ICONEXCLAMATION | MB_OK);
753 // We have successfully removed the service!
754 MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
758 case VER_PLATFORM_WIN32_NT:
760 SC_HANDLE hsrvmanager;
762 // Attempt to remove the service-helper hook
764 if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
765 &runapps) == ERROR_SUCCESS) {
766 // Attempt to delete the Bacula key
767 if (RegDeleteValue(runapps, szAppName) != ERROR_SUCCESS) {
768 MessageBox(NULL, "WARNING:The ServiceHelper hook entry could not be removed from the registry", szAppName, MB_ICONEXCLAMATION | MB_OK);
770 RegCloseKey(runapps);
774 hsrvmanager = OpenSCManager(
775 NULL, // machine (NULL == local)
776 NULL, // database (NULL == default)
777 SC_MANAGER_ALL_ACCESS // access required
780 hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
781 if (hservice != NULL) {
782 SERVICE_STATUS status;
784 // Try to stop the Bacula service
785 if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
786 while(QueryServiceStatus(hservice, &status)) {
787 if (status.dwCurrentState == SERVICE_STOP_PENDING) {
794 if (status.dwCurrentState != SERVICE_STOPPED) {
795 MessageBox(NULL, "The Bacula service could not be stopped", szAppName, MB_ICONEXCLAMATION | MB_OK);
799 // Now remove the service from the SCM
800 if(DeleteService(hservice)) {
801 MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
803 MessageBox(NULL, "The Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
806 CloseServiceHandle(hservice);
808 MessageBox(NULL, "The Bacula service could not be found", szAppName, MB_ICONEXCLAMATION | MB_OK);
811 CloseServiceHandle(hsrvmanager);
813 MessageBox(NULL, "The SCM could not be contacted - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
820 // USEFUL SERVICE SUPPORT ROUTINES
822 // Service control routine
823 void WINAPI ServiceCtrl(DWORD ctrlcode)
825 // What control code have we been sent?
827 case SERVICE_CONTROL_STOP:
828 // STOP : The service must stop
829 g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
833 case SERVICE_CONTROL_INTERROGATE:
834 // QUERY : Service control manager just wants to know our state
838 // Control code not recognised
843 // Tell the control manager what we're up to.
844 ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
847 // Service manager status reporting
848 BOOL ReportStatus(DWORD state,
852 static DWORD checkpoint = 1;
855 // If we're in the start state then we don't want the control manager
856 // sending us control messages because they'll confuse us.
857 if (state == SERVICE_START_PENDING) {
858 g_srvstatus.dwControlsAccepted = 0;
860 g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
863 // Save the new status we've been given
864 g_srvstatus.dwCurrentState = state;
865 g_srvstatus.dwWin32ExitCode = exitcode;
866 g_srvstatus.dwWaitHint = waithint;
868 // Update the checkpoint variable to let the SCM know that we
869 // haven't died if requests take a long time
870 if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
871 g_srvstatus.dwCheckPoint = 0;
873 g_srvstatus.dwCheckPoint = checkpoint++;
876 // Tell the SCM our new status
877 if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
878 LogErrorMsg("SetServiceStatus failed");
885 void LogErrorMsg(char *message)
892 // Get the error code
893 g_error = GetLastError();
894 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
895 FORMAT_MESSAGE_FROM_SYSTEM,
903 // Use event logging to log the error
904 heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
906 sprintf(msgbuff, "%s error: %ld", BAC_SERVICENAME, g_error);
907 strings[0] = msgbuff;
908 strings[1] = message;
911 if (heventsrc != NULL) {
915 heventsrc, // handle of event source
916 EVENTLOG_ERROR_TYPE, // event type
919 NULL, // current user's SID
920 3, // strings in 'strings'
921 0, // no bytes of raw data
922 (const char **)strings, // array of error strings
923 NULL); // no raw data
925 DeregisterEventSource(heventsrc);
930 /* ================== Not yet implemented ===================== */
932 VOID ReconfigureSampleService(BOOL fDisable, LPSTR lpDesc)
935 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
936 SERVICE_DESCRIPTION sdBuf;
937 DWORD dwBytesNeeded, dwStartType;
939 // Need to acquire database lock before reconfiguring.
941 sclLock = LockServiceDatabase(schSCManager);
943 // If the database cannot be locked, report the details.
947 // Exit if the database is not locked by another process.
949 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED)
950 MyErrorExit("LockServiceDatabase");
952 // Allocate a buffer to get details about the lock.
954 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS) LocalAlloc(
955 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
956 if (lpqslsBuf == NULL)
957 MyErrorExit("LocalAlloc");
959 // Get and print the lock status information.
961 if (!QueryServiceLockStatus(
964 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
966 MyErrorExit("QueryServiceLockStatus");
968 if (lpqslsBuf->fIsLocked)
969 printf("Locked by: %s, duration: %d seconds\n",
970 lpqslsBuf->lpLockOwner,
971 lpqslsBuf->dwLockDuration);
973 printf("No longer locked\n");
975 LocalFree(lpqslsBuf);
976 MyErrorExit("Could not lock database");
979 // The database is locked, so it is safe to make changes.
981 // Open a handle to the service.
983 schService = OpenService(
984 schSCManager, // SCManager database
985 "Sample_Srv", // name of service
986 SERVICE_CHANGE_CONFIG); // need CHANGE access
987 if (schService == NULL)
988 MyErrorExit("OpenService");
990 dwStartType = (fDisable) ? SERVICE_DISABLED :
991 SERVICE_DEMAND_START;
995 if (! ChangeServiceConfig(
996 schService, // handle of service
997 SERVICE_NO_CHANGE, // service type: no change
998 dwStartType, // change service start type
999 SERVICE_NO_CHANGE, // error control: no change
1000 NULL, // binary path: no change
1001 NULL, // load order group: no change
1002 NULL, // tag ID: no change
1003 NULL, // dependencies: no change
1004 NULL, // account name: no change
1005 NULL, // password: no change
1006 NULL) ) // display name: no change
1008 MyErrorExit("ChangeServiceConfig");
1011 printf("ChangeServiceConfig SUCCESS\n");
1013 sdBuf.lpDescription = lpDesc;
1015 if( !ChangeServiceConfig2(
1016 schService, // handle to service
1017 SERVICE_CONFIG_DESCRIPTION // change: description
1018 &sdBuf) ) // value: new description
1020 MyErrorExit("ChangeServiceConfig2");
1023 printf("ChangeServiceConfig2 SUCCESS\n");
1025 // Release the database lock.
1027 UnlockServiceDatabase(sclLock);
1029 // Close the handle to the service.
1031 CloseServiceHandle(schService);