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 (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"
46 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
49 // OS-SPECIFIC ROUTINES
51 // Create an instance of the bacService class to cause the static fields to be
52 // initialised properly
57 BOOL g_impersonating_user = 0;
59 bacService::bacService()
61 OSVERSIONINFO osversioninfo;
62 osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
64 // Get the current OS version
65 if (!GetVersionEx(&osversioninfo)) {
68 g_platform_id = osversioninfo.dwPlatformId;
71 /* Rewritten to lookup entry point */
72 if (osversioninfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
73 osversioninfo.dwMinorVersion == 0) {
74 /* Running Win95 so no GetFileAttributesEx available */
75 NoGetFileAttributesEx = 1;
81 // IsWin95 - returns a BOOL indicating whether the current OS is Win95
85 return (g_platform_id == VER_PLATFORM_WIN32_WINDOWS);
88 // IsWinNT - returns a bool indicating whether the current OS is WinNT
92 return (g_platform_id == VER_PLATFORM_WIN32_NT);
95 // Internal routine to find the Bacula menu class window and
96 // post a message to it!
99 PostToBacula(UINT message, WPARAM wParam, LPARAM lParam)
101 // Locate the hidden Bacula menu window
102 HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
103 if (hservwnd == NULL) {
107 // Post the message to Bacula
108 PostMessage(hservwnd, message, wParam, lParam);
113 // Static routine to show the Properties dialog for a currently-running
114 // copy of Bacula, (usually a servicified version.)
117 bacService::ShowProperties()
122 // Static routine to show the Default Properties dialog for a currently-running
123 // copy of Bacula, (usually a servicified version.)
126 bacService::ShowDefaultProperties()
131 // Static routine to show the About dialog for a currently-running
132 // copy of Bacula, (usually a servicified version.)
135 bacService::ShowAboutBox()
137 // Post to the Bacula menu window
138 if (!PostToBacula(MENU_ABOUTBOX_SHOW, 0, 0)) {
139 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
145 // Static routine to show the Status dialog for a currently-running
146 // copy of Bacula, (usually a servicified version.)
149 bacService::ShowStatus()
151 // Post to the Bacula menu window
152 if (!PostToBacula(MENU_STATUS_SHOW, 0, 0)) {
153 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
159 // Static routine to show the Events dialog for a currently-running
160 // copy of Bacula, (usually a servicified version.)
163 bacService::ShowEvents()
165 // Post to the Bacula menu window
166 if (!PostToBacula(MENU_EVENTS_SHOW, 0, 0)) {
167 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
174 // Static routine to tell a locally-running instance of the server
175 // to connect out to a new client
178 bacService::PostAddNewClient(unsigned long ipaddress)
180 // Post to the Bacula menu window
181 if (!PostToBacula(MENU_ADD_CLIENT_MSG, 0, ipaddress)) {
182 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
189 // SERVICE-MODE ROUTINES
191 // Service-mode defines:
194 #define BAC_APPNAME "bacula"
196 // Internal service name
197 #define BAC_SERVICENAME "Bacula"
199 // Displayed service name
200 #define BAC_SERVICEDISPLAYNAME "Bacula File Server"
202 // List other required serves
203 #define BAC_DEPENDENCIES ""
206 // Internal service state
207 SERVICE_STATUS g_srvstatus; // current status of the service
208 SERVICE_STATUS_HANDLE g_hstatus;
210 DWORD g_servicethread = 0;
211 char* g_errortext[256];
214 // Forward defines of internal service functions
215 void WINAPI ServiceMain(DWORD argc, char **argv);
216 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
218 void WINAPI ServiceCtrl(DWORD ctrlcode);
219 bool WINAPI CtrlHandler (DWORD ctrltype);
220 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
222 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
224 BOOL g_servicemode = FALSE;
227 bacService::RunningAsService()
229 return g_servicemode;
233 bacService::KillRunningCopy()
235 while (PostToBacula(WM_CLOSE, 0, 0))
241 // ROUTINE TO POST THE HANDLE OF THE CURRENT USER TO THE RUNNING Bacula, IN ORDER
242 // THAT IT CAN LOAD THE APPROPRIATE SETTINGS. THIS IS USED ONLY BY THE SVCHELPER
243 // OPTION, WHEN RUNNING UNDER NT
245 bacService::PostUserHelperMessage()
247 // - Check the platform type
252 // - Get the current process ID
253 DWORD processId = GetCurrentProcessId();
255 // - Post it to the existing Bacula
256 if (!PostToBacula(MENU_SERVICEHELPER_MSG, 0, (LPARAM)processId)) {
260 // - Wait until it's been used
264 // ROUTINE TO PROCESS AN INCOMING INSTANCE OF THE ABOVE MESSAGE
266 bacService::ProcessUserHelperMessage(WPARAM wParam, LPARAM lParam) {
270 // SERVICE MAIN ROUTINE
272 bacService::BaculaServiceMain()
274 // Mark that we are a service
275 g_servicemode = TRUE;
277 // How to run as a service depends upon the OS being used
278 switch (g_platform_id) {
281 case VER_PLATFORM_WIN32_WINDOWS:
283 // Obtain a handle to the kernel library
284 HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
285 if (kerneldll == NULL) {
286 MessageBox(NULL, "KERNEL32.DLL not found: Bacula service not started",
287 "Bacula Service", MB_OK);
291 // And find the RegisterServiceProcess function
292 DWORD WINAPI (*RegisterService)(DWORD, DWORD);
293 RegisterService = (DWORD (*)(DWORD, DWORD))
294 GetProcAddress(kerneldll, "RegisterServiceProcess");
295 if (RegisterService == NULL) {
296 MessageBox(NULL, "Registry service not found: Bacula service not started",
297 "Bacula Service", MB_OK);
298 log_error_message("Registry service not found");
302 // Register this process with the OS as a service!
303 RegisterService(0, 1);
305 // Run the main program as a service
308 // Then remove the service from the system service table
309 RegisterService(0, 0);
311 // Free the kernel library
312 FreeLibrary(kerneldll);
317 // Windows NT, Win2K, WinXP
318 case VER_PLATFORM_WIN32_NT:
320 // Create a service entry table
321 SERVICE_TABLE_ENTRY dispatchTable[] = {
322 {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
326 // Call the service control dispatcher with our entry table
327 if (!StartServiceCtrlDispatcher(dispatchTable)) {
328 log_error_message("StartServiceCtrlDispatcher failed.");
336 // SERVICE MAIN ROUTINE - NT ONLY !!!
337 // NT/Win2K/WinXP ONLY !!!
338 void WINAPI ServiceMain(DWORD argc, char **argv)
342 // Register the service control handler
343 g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
345 if (g_hstatus == 0) {
346 log_error_message("RegisterServiceCtlHandler failed");
347 MessageBox(NULL, "Contact Register Service Handler failure",
348 "Bacula service", MB_OK);
352 // Set up some standard service state values
353 g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
354 g_srvstatus.dwServiceSpecificExitCode = 0;
356 // Give this status to the SCM
358 SERVICE_START_PENDING, // Service state
359 NO_ERROR, // Exit code type
360 45000)) { // Hint as to how long Bacula should have hung before you assume error
362 ReportStatus(SERVICE_STOPPED, g_error, 0);
363 log_error_message("ReportStatus STOPPED failed 1");
367 // Now start the service for real
368 (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
372 // SERVICE START ROUTINE - thread that calls BaculaAppMain
374 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
377 // Save the current thread identifier
378 g_servicethread = GetCurrentThreadId();
380 // report the status to the service control manager.
383 SERVICE_RUNNING, // service state
384 NO_ERROR, // exit code
386 MessageBox(NULL, "Report Service failure", "Bacula Service", MB_OK);
387 log_error_message("ReportStatus RUNNING failed");
391 /* Call Bacula main code */
394 /* Mark that we're no longer running */
397 /* Tell the service manager that we've stopped */
398 ReportStatus(SERVICE_STOPPED, g_error, 0);
403 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
406 // Post a quit message to the main service thread
407 if (g_servicethread != 0) {
408 PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
412 // SERVICE INSTALL ROUTINE
414 bacService::InstallService()
416 const int pathlength = 2048;
417 char path[pathlength];
418 char servicecmd[pathlength];
421 // Get the filename of this executable
422 if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
423 MessageBox(NULL, "Unable to install Bacula service", szAppName, MB_ICONEXCLAMATION | MB_OK);
427 // Append the service-start flag to the end of the path:
428 if ((int)strlen(path) + 20 + (int)strlen(BaculaRunService) < pathlength) {
429 sprintf(servicecmd, "\"%s\" %s -c %s", path, BaculaRunService, path);
430 len = strlen(servicecmd) - 1;
431 for ( ; len > 0; len--) {
432 if (servicecmd[len] == '\\') {
438 strcat(servicecmd, "\\bacula-fd.conf");
441 log_error_message("Service command length too long");
442 MessageBox(NULL, "Service command length too long. Service not registered.",
443 szAppName, MB_ICONEXCLAMATION | MB_OK);
447 // How to add the Bacula service depends upon the OS
448 switch (g_platform_id) {
451 case VER_PLATFORM_WIN32_WINDOWS:
452 // Locate the RunService registry entry
454 if (RegCreateKey(HKEY_LOCAL_MACHINE,
455 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
456 &runservices) != ERROR_SUCCESS) {
457 log_error_message("Cannot write System Registry");
458 MessageBox(NULL, "The System Registry could not be updated - the Bacula service was not installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
462 // Attempt to add a Bacula key
463 if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS) {
464 RegCloseKey(runservices);
465 log_error_message("Cannot add Bacula key to System Registry");
466 MessageBox(NULL, "The Bacula service could not be installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
470 RegCloseKey(runservices);
472 // We have successfully installed the service!
474 "The Bacula File service was successfully installed.\n"
475 "The service may be started by double clicking on the\n"
476 "Bacula \"Start\" icon and will be automatically\n"
477 "be run the next time this machine is rebooted. ",
479 MB_ICONINFORMATION | MB_OK);
482 // Windows NT, Win2K, WinXP
483 case VER_PLATFORM_WIN32_NT:
485 SC_HANDLE hsrvmanager;
487 // Open the default, local Service Control Manager database
488 hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
489 if (hsrvmanager == NULL) {
490 log_error_message("OpenSCManager failed");
492 "The Service Control Manager could not be contacted - the Bacula service was not installed",
493 szAppName, MB_ICONEXCLAMATION | MB_OK);
497 // Create an entry for the Bacula service
498 hservice = CreateService(
499 hsrvmanager, // SCManager database
500 BAC_SERVICENAME, // name of service
501 BAC_SERVICEDISPLAYNAME, // name to display
502 SERVICE_ALL_ACCESS, // desired access
503 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
505 SERVICE_AUTO_START, // start type
506 SERVICE_ERROR_NORMAL, // error control type
507 servicecmd, // service's binary
508 NULL, // no load ordering group
509 NULL, // no tag identifier
510 BAC_DEPENDENCIES, // dependencies
511 NULL, // LocalSystem account
512 NULL); // no password
513 if (hservice == NULL) {
514 CloseServiceHandle(hsrvmanager);
515 log_error_message("CreateService failed");
517 "The Bacula service could not be installed",
518 szAppName, MB_ICONEXCLAMATION | MB_OK);
522 set_service_description(hsrvmanager,hservice,
523 "Provides file backup and restore services. Bacula -- the network backup solution.");
525 CloseServiceHandle(hsrvmanager);
526 CloseServiceHandle(hservice);
528 // Now install the servicehelper registry setting...
529 // Locate the RunService registry entry
531 if (RegCreateKey(HKEY_LOCAL_MACHINE,
532 "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
533 &runapps) != ERROR_SUCCESS) {
534 MessageBox(NULL, "WARNING: Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded",
535 szAppName, MB_ICONEXCLAMATION | MB_OK);
537 char servicehelpercmd[pathlength];
539 // Append the service-helper-start flag to the end of the path:
540 if ((int)strlen(path) + 4 + (int)strlen(BaculaRunServiceHelper) < pathlength) {
541 sprintf(servicehelpercmd, "\"%s\" %s", path, BaculaRunServiceHelper);
543 // Add the Bacula Service Helper entry
544 if (RegSetValueEx(runapps, szAppName, 0, REG_SZ,
545 (unsigned char *)servicehelpercmd, strlen(servicehelpercmd)+1) != ERROR_SUCCESS) {
546 MessageBox(NULL, "WARNING:Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded", szAppName, MB_ICONEXCLAMATION | MB_OK);
548 RegCloseKey(runapps);
552 // Everything went fine
554 "The Bacula File service was successfully installed.\n"
555 "The service may be started from the Control Panel and will\n"
556 "automatically be run the next time this machine is rebooted.",
558 MB_ICONINFORMATION | MB_OK);
561 log_error_message("Unknown Windows System version");
563 "Unknown Windows operating system.\n"
564 "Cannot install Bacula service.\n",
565 szAppName, MB_ICONEXCLAMATION | MB_OK);
573 // SERVICE REMOVE ROUTINE
575 bacService::RemoveService()
577 // How to remove the Bacula service depends upon the OS
578 switch (g_platform_id) {
581 case VER_PLATFORM_WIN32_WINDOWS:
582 // Locate the RunService registry entry
584 if (RegOpenKey(HKEY_LOCAL_MACHINE,
585 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
586 &runservices) != ERROR_SUCCESS) {
588 "Could not find registry entry.\nService probably not registerd - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
590 // Attempt to delete the Bacula key
591 if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS) {
592 RegCloseKey(runservices);
593 MessageBox(NULL, "Could not delete Registry key.\nThe Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
596 RegCloseKey(runservices);
600 // Try to kill any running copy of Bacula
601 if (!KillRunningCopy()) {
603 "Bacula could not be contacted, probably not running",
604 szAppName, MB_ICONEXCLAMATION | MB_OK);
608 // We have successfully removed the service!
609 MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
612 // Windows NT, Win2K, WinXP
613 case VER_PLATFORM_WIN32_NT:
615 SC_HANDLE hsrvmanager;
617 // Attempt to remove the service-helper hook
619 if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
620 &runapps) == ERROR_SUCCESS) {
621 // Attempt to delete the Bacula key
622 if (RegDeleteValue(runapps, szAppName) != ERROR_SUCCESS) {
623 MessageBox(NULL, "WARNING:The ServiceHelper hook entry could not be removed from the registry", szAppName, MB_ICONEXCLAMATION | MB_OK);
625 RegCloseKey(runapps);
629 hsrvmanager = OpenSCManager(
630 NULL, // machine (NULL == local)
631 NULL, // database (NULL == default)
632 SC_MANAGER_ALL_ACCESS // access required
635 hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
636 if (hservice != NULL) {
637 SERVICE_STATUS status;
639 // Try to stop the Bacula service
640 if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
641 while(QueryServiceStatus(hservice, &status)) {
642 if (status.dwCurrentState == SERVICE_STOP_PENDING) {
649 if (status.dwCurrentState != SERVICE_STOPPED) {
650 MessageBox(NULL, "The Bacula service could not be stopped", szAppName, MB_ICONEXCLAMATION | MB_OK);
654 // Now remove the service from the SCM
655 if(DeleteService(hservice)) {
656 MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
658 MessageBox(NULL, "The Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
661 CloseServiceHandle(hservice);
663 MessageBox(NULL, "The Bacula service could not be found", szAppName, MB_ICONEXCLAMATION | MB_OK);
666 CloseServiceHandle(hsrvmanager);
668 MessageBox(NULL, "The SCM could not be contacted - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
675 // USEFUL SERVICE SUPPORT ROUTINES
677 // Service control routine
678 void WINAPI ServiceCtrl(DWORD ctrlcode)
680 // What control code have we been sent?
682 case SERVICE_CONTROL_STOP:
683 // STOP : The service must stop
684 g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
688 case SERVICE_CONTROL_INTERROGATE:
689 // QUERY : Service control manager just wants to know our state
693 // Control code not recognised
697 // Tell the control manager what we're up to.
698 ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
701 // Service manager status reporting
702 BOOL ReportStatus(DWORD state,
706 static DWORD checkpoint = 1;
709 // If we're in the start state then we don't want the control manager
710 // sending us control messages because they'll confuse us.
711 if (state == SERVICE_START_PENDING) {
712 g_srvstatus.dwControlsAccepted = 0;
714 g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
717 // Save the new status we've been given
718 g_srvstatus.dwCurrentState = state;
719 g_srvstatus.dwWin32ExitCode = exitcode;
720 g_srvstatus.dwWaitHint = waithint;
722 // Update the checkpoint variable to let the SCM know that we
723 // haven't died if requests take a long time
724 if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
725 g_srvstatus.dwCheckPoint = 0;
727 g_srvstatus.dwCheckPoint = checkpoint++;
730 // Tell the SCM our new status
731 if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
732 log_error_message("SetServiceStatus failed");
739 void LogErrorMsg(char *message, char *fname, int lineno)
746 // Get the error code
747 g_error = GetLastError();
748 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
749 FORMAT_MESSAGE_FROM_SYSTEM,
757 // Use event logging to log the error
758 heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
760 sprintf(msgbuff, "\n\n%s error: %ld at %s:%d",
761 BAC_SERVICENAME, g_error, fname, lineno);
762 strings[0] = msgbuff;
763 strings[1] = message;
766 if (heventsrc != NULL) {
770 heventsrc, // handle of event source
771 EVENTLOG_ERROR_TYPE, // event type
774 NULL, // current user's SID
775 3, // strings in 'strings'
776 0, // no bytes of raw data
777 (const char **)strings, // array of error strings
778 NULL); // no raw data
780 DeregisterEventSource(heventsrc);
784 typedef BOOL WINAPI (*WinAPI)(SC_HANDLE, DWORD, LPVOID);
786 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
790 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
791 SERVICE_DESCRIPTION sdBuf;
793 WinAPI ChangeServiceDescription;
795 HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
799 ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
800 "ChangeServiceConfig2A");
802 if (!ChangeServiceDescription) {
806 // Need to acquire database lock before reconfiguring.
807 sclLock = LockServiceDatabase(hSCManager);
809 // If the database cannot be locked, report the details.
810 if (sclLock == NULL) {
811 // Exit if the database is not locked by another process.
812 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
813 log_error_message("LockServiceDatabase");
817 // Allocate a buffer to get details about the lock.
818 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc(
819 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
820 if (lpqslsBuf == NULL) {
821 log_error_message("LocalAlloc");
825 // Get and print the lock status information.
826 if (!QueryServiceLockStatus(
829 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
831 log_error_message("QueryServiceLockStatus");
834 if (lpqslsBuf->fIsLocked) {
835 printf("Locked by: %s, duration: %ld seconds\n",
836 lpqslsBuf->lpLockOwner,
837 lpqslsBuf->dwLockDuration);
839 printf("No longer locked\n");
842 LocalFree(lpqslsBuf);
843 log_error_message("Could not lock database");
847 // The database is locked, so it is safe to make changes.
849 sdBuf.lpDescription = lpDesc;
851 if(!ChangeServiceDescription(
852 hService, // handle to service
853 SERVICE_CONFIG_DESCRIPTION, // change: description
854 &sdBuf) ) { // value: new description
855 log_error_message("ChangeServiceConfig2");
857 printf("ChangeServiceConfig2 SUCCESS\n");
860 // Release the database lock.
861 UnlockServiceDatabase(sclLock);