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
58 bacService::bacService()
60 OSVERSIONINFO osversioninfo;
61 osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
63 // Get the current OS version
64 if (!GetVersionEx(&osversioninfo)) {
67 g_platform_id = osversioninfo.dwPlatformId;
72 // IsWin95 - returns a BOOL indicating whether the current OS is Win95
76 return (g_platform_id == VER_PLATFORM_WIN32_WINDOWS);
79 // IsWinNT - returns a bool indicating whether the current OS is WinNT
83 return (g_platform_id == VER_PLATFORM_WIN32_NT);
86 // Internal routine to find the Bacula menu class window and
87 // post a message to it!
90 PostToBacula(UINT message, WPARAM wParam, LPARAM lParam)
92 // Locate the hidden Bacula menu window
93 HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
94 if (hservwnd == NULL) {
98 // Post the message to Bacula
99 PostMessage(hservwnd, message, wParam, lParam);
104 // Static routine to show the Properties dialog for a currently-running
105 // copy of Bacula, (usually a servicified version.)
108 bacService::ShowProperties()
113 // Static routine to show the Default Properties dialog for a currently-running
114 // copy of Bacula, (usually a servicified version.)
117 bacService::ShowDefaultProperties()
122 // Static routine to show the About dialog for a currently-running
123 // copy of Bacula, (usually a servicified version.)
126 bacService::ShowAboutBox()
128 // Post to the Bacula menu window
129 if (!PostToBacula(MENU_ABOUTBOX_SHOW, 0, 0)) {
130 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
136 // Static routine to show the Status dialog for a currently-running
137 // copy of Bacula, (usually a servicified version.)
140 bacService::ShowStatus()
142 // Post to the Bacula menu window
143 if (!PostToBacula(MENU_STATUS_SHOW, 0, 0)) {
144 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
151 // Static routine to show the Events dialog for a currently-running
152 // copy of Bacula, (usually a servicified version.)
155 bacService::ShowEvents()
157 // Post to the Bacula menu window
158 if (!PostToBacula(MENU_EVENTS_SHOW, 0, 0)) {
159 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
166 // Static routine to tell a locally-running instance of the server
167 // to connect out to a new client
170 bacService::PostAddNewClient(unsigned long ipaddress)
172 // Post to the Bacula menu window
173 if (!PostToBacula(MENU_ADD_CLIENT_MSG, 0, ipaddress)) {
174 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
182 // SERVICE-MODE ROUTINES
184 // Service-mode defines:
187 #define BAC_APPNAME "bacula"
189 // Internal service name
190 #define BAC_SERVICENAME "Bacula"
192 // Displayed service name
193 #define BAC_SERVICEDISPLAYNAME "Bacula File Server"
195 // List other required serves
196 #define BAC_DEPENDENCIES ""
199 // Internal service state
200 SERVICE_STATUS g_srvstatus; // current status of the service
201 SERVICE_STATUS_HANDLE g_hstatus;
203 DWORD g_servicethread = 0;
204 char* g_errortext[256];
207 // Forward defines of internal service functions
208 void WINAPI ServiceMain(DWORD argc, char **argv);
209 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
211 void WINAPI ServiceCtrl(DWORD ctrlcode);
212 bool WINAPI CtrlHandler (DWORD ctrltype);
213 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
215 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
217 BOOL g_servicemode = FALSE;
220 bacService::RunningAsService()
222 return g_servicemode;
226 bacService::KillRunningCopy()
228 while (PostToBacula(WM_CLOSE, 0, 0))
235 // ROUTINE TO POST THE HANDLE OF THE CURRENT USER TO THE RUNNING Bacula, IN ORDER
236 // THAT IT CAN LOAD THE APPROPRIATE SETTINGS. THIS IS USED ONLY BY THE SVCHELPER
237 // OPTION, WHEN RUNNING UNDER NT
239 bacService::PostUserHelperMessage()
241 // - Check the platform type
246 // - Get the current process ID
247 DWORD processId = GetCurrentProcessId();
249 // - Post it to the existing Bacula
250 if (!PostToBacula(MENU_SERVICEHELPER_MSG, 0, (LPARAM)processId)) {
254 // - Wait until it's been used
258 // ROUTINE TO PROCESS AN INCOMING INSTANCE OF THE ABOVE MESSAGE
260 bacService::ProcessUserHelperMessage(WPARAM wParam, LPARAM lParam) {
266 // SERVICE MAIN ROUTINE
268 bacService::BaculaServiceMain()
270 // Mark that we are a service
271 g_servicemode = TRUE;
273 // How to run as a service depends upon the OS being used
274 switch (g_platform_id) {
277 case VER_PLATFORM_WIN32_WINDOWS:
279 // Obtain a handle to the kernel library
280 HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
281 if (kerneldll == NULL) {
282 MessageBox(NULL, "KERNEL32.DLL not found: Bacula service not started",
283 "Bacula Service", MB_OK);
287 // And find the RegisterServiceProcess function
288 DWORD (WINAPI *RegisterService)(DWORD, DWORD);
289 RegisterService = (DWORD (WINAPI *)(DWORD, DWORD))
290 GetProcAddress(kerneldll, "RegisterServiceProcess");
291 if (RegisterService == NULL) {
292 MessageBox(NULL, "Registry service not found: Bacula service not started",
293 "Bacula Service", MB_OK);
294 log_error_message("Registry service not found");
298 // Register this process with the OS as a service!
299 RegisterService(0, 1);
301 // Run the main program as a service
304 // Then remove the service from the system service table
305 RegisterService(0, 0);
307 // Free the kernel library
308 FreeLibrary(kerneldll);
313 // Windows NT, Win2K, WinXP
314 case VER_PLATFORM_WIN32_NT:
316 // Create a service entry table
317 SERVICE_TABLE_ENTRY dispatchTable[] = {
318 {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
322 // Call the service control dispatcher with our entry table
323 if (!StartServiceCtrlDispatcher(dispatchTable)) {
324 log_error_message("StartServiceCtrlDispatcher failed.");
332 // SERVICE MAIN ROUTINE - NT ONLY !!!
333 // NT/Win2K/WinXP ONLY !!!
334 void WINAPI ServiceMain(DWORD argc, char **argv)
338 // Register the service control handler
339 g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
341 if (g_hstatus == 0) {
342 log_error_message("RegisterServiceCtlHandler failed");
343 MessageBox(NULL, "Contact Register Service Handler failure",
344 "Bacula service", MB_OK);
348 // Set up some standard service state values
349 g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
350 g_srvstatus.dwServiceSpecificExitCode = 0;
352 // Give this status to the SCM
354 SERVICE_START_PENDING, // Service state
355 NO_ERROR, // Exit code type
356 45000)) { // Hint as to how long Bacula should have hung before you assume error
358 ReportStatus(SERVICE_STOPPED, g_error, 0);
359 log_error_message("ReportStatus STOPPED failed 1");
363 // Now start the service for real
364 (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
368 // SERVICE START ROUTINE - thread that calls BaculaAppMain
370 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
373 // Save the current thread identifier
374 g_servicethread = GetCurrentThreadId();
376 // report the status to the service control manager.
379 SERVICE_RUNNING, // service state
380 NO_ERROR, // exit code
382 MessageBox(NULL, "Report Service failure", "Bacula Service", MB_OK);
383 log_error_message("ReportStatus RUNNING failed");
387 /* Call Bacula main code */
390 /* Mark that we're no longer running */
393 /* Tell the service manager that we've stopped */
394 ReportStatus(SERVICE_STOPPED, g_error, 0);
399 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
402 // Post a quit message to the main service thread
403 if (g_servicethread != 0) {
404 PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
408 // SERVICE INSTALL ROUTINE
410 bacService::InstallService()
412 const int pathlength = 2048;
413 char path[pathlength];
414 char servicecmd[pathlength];
417 // Get the filename of this executable
418 if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
419 MessageBox(NULL, "Unable to install Bacula service", szAppName, MB_ICONEXCLAMATION | MB_OK);
423 // Append the service-start flag to the end of the path:
424 if ((int)strlen(path) + 20 + (int)strlen(BaculaRunService) < pathlength) {
425 sprintf(servicecmd, "\"%s\" %s -c \"%s\"", path, BaculaRunService, path);
426 len = strlen(servicecmd) - 1;
427 for ( ; len > 0; len--) {
428 if (servicecmd[len] == '\\') {
434 strcat(servicecmd, "\\bacula-fd.conf");
437 log_error_message("Service command length too long");
438 MessageBox(NULL, "Service command length too long. Service not registered.",
439 szAppName, MB_ICONEXCLAMATION | MB_OK);
443 // How to add the Bacula service depends upon the OS
444 switch (g_platform_id) {
447 case VER_PLATFORM_WIN32_WINDOWS:
448 // Locate the RunService registry entry
450 if (RegCreateKey(HKEY_LOCAL_MACHINE,
451 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
452 &runservices) != ERROR_SUCCESS) {
453 log_error_message("Cannot write System Registry");
454 MessageBox(NULL, "The System Registry could not be updated - the Bacula service was not installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
458 // Attempt to add a Bacula key
459 if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS) {
460 RegCloseKey(runservices);
461 log_error_message("Cannot add Bacula key to System Registry");
462 MessageBox(NULL, "The Bacula service could not be installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
466 RegCloseKey(runservices);
468 // We have successfully installed the service!
470 "The Bacula File service was successfully installed.\n"
471 "The service may be started by double clicking on the\n"
472 "Bacula \"Start\" icon and will be automatically\n"
473 "be run the next time this machine is rebooted. ",
475 MB_ICONINFORMATION | MB_OK);
478 // Windows NT, Win2K, WinXP
479 case VER_PLATFORM_WIN32_NT:
481 SC_HANDLE hsrvmanager;
483 // Open the default, local Service Control Manager database
484 hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
485 if (hsrvmanager == NULL) {
486 log_error_message("OpenSCManager failed");
488 "The Service Control Manager could not be contacted - the Bacula service was not installed",
489 szAppName, MB_ICONEXCLAMATION | MB_OK);
493 // Create an entry for the Bacula service
494 hservice = CreateService(
495 hsrvmanager, // SCManager database
496 BAC_SERVICENAME, // name of service
497 BAC_SERVICEDISPLAYNAME, // name to display
498 SERVICE_ALL_ACCESS, // desired access
499 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
501 SERVICE_AUTO_START, // start type
502 SERVICE_ERROR_NORMAL, // error control type
503 servicecmd, // service's binary
504 NULL, // no load ordering group
505 NULL, // no tag identifier
506 BAC_DEPENDENCIES, // dependencies
507 NULL, // LocalSystem account
508 NULL); // no password
509 if (hservice == NULL) {
510 CloseServiceHandle(hsrvmanager);
511 log_error_message("CreateService failed");
513 "The Bacula service could not be installed",
514 szAppName, MB_ICONEXCLAMATION | MB_OK);
518 set_service_description(hsrvmanager,hservice,
519 "Provides file backup and restore services. Bacula -- the network backup solution.");
521 CloseServiceHandle(hsrvmanager);
522 CloseServiceHandle(hservice);
524 // Now install the servicehelper registry setting...
525 // Locate the RunService registry entry
527 if (RegCreateKey(HKEY_LOCAL_MACHINE,
528 "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
529 &runapps) != ERROR_SUCCESS) {
530 MessageBox(NULL, "WARNING: Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded",
531 szAppName, MB_ICONEXCLAMATION | MB_OK);
533 char servicehelpercmd[pathlength];
535 // Append the service-helper-start flag to the end of the path:
536 if ((int)strlen(path) + 4 + (int)strlen(BaculaRunServiceHelper) < pathlength) {
537 sprintf(servicehelpercmd, "\"%s\" %s", path, BaculaRunServiceHelper);
539 // Add the Bacula Service Helper entry
540 if (RegSetValueEx(runapps, szAppName, 0, REG_SZ,
541 (unsigned char *)servicehelpercmd, strlen(servicehelpercmd)+1) != ERROR_SUCCESS) {
542 MessageBox(NULL, "WARNING:Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded", szAppName, MB_ICONEXCLAMATION | MB_OK);
544 RegCloseKey(runapps);
548 // Everything went fine
550 "The Bacula File service was successfully installed.\n"
551 "The service may be started from the Control Panel and will\n"
552 "automatically be run the next time this machine is rebooted.",
554 MB_ICONINFORMATION | MB_OK);
557 log_error_message("Unknown Windows System version");
559 "Unknown Windows operating system.\n"
560 "Cannot install Bacula service.\n",
561 szAppName, MB_ICONEXCLAMATION | MB_OK);
569 // SERVICE REMOVE ROUTINE
571 bacService::RemoveService()
573 // How to remove the Bacula service depends upon the OS
574 switch (g_platform_id) {
577 case VER_PLATFORM_WIN32_WINDOWS:
578 // Locate the RunService registry entry
580 if (RegOpenKey(HKEY_LOCAL_MACHINE,
581 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
582 &runservices) != ERROR_SUCCESS) {
584 "Could not find registry entry.\nService probably not registerd - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
586 // Attempt to delete the Bacula key
587 if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS) {
588 RegCloseKey(runservices);
589 MessageBox(NULL, "Could not delete Registry key.\nThe Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
592 RegCloseKey(runservices);
596 // Try to kill any running copy of Bacula
597 if (!KillRunningCopy()) {
599 "Bacula could not be contacted, probably not running",
600 szAppName, MB_ICONEXCLAMATION | MB_OK);
604 // We have successfully removed the service!
605 MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
608 // Windows NT, Win2K, WinXP
609 case VER_PLATFORM_WIN32_NT:
611 SC_HANDLE hsrvmanager;
613 // Attempt to remove the service-helper hook
615 if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
616 &runapps) == ERROR_SUCCESS) {
617 // Attempt to delete the Bacula key
618 if (RegDeleteValue(runapps, szAppName) != ERROR_SUCCESS) {
619 MessageBox(NULL, "WARNING:The ServiceHelper hook entry could not be removed from the registry", szAppName, MB_ICONEXCLAMATION | MB_OK);
621 RegCloseKey(runapps);
625 hsrvmanager = OpenSCManager(
626 NULL, // machine (NULL == local)
627 NULL, // database (NULL == default)
628 SC_MANAGER_ALL_ACCESS // access required
631 hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
632 if (hservice != NULL) {
633 SERVICE_STATUS status;
635 // Try to stop the Bacula service
636 if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
637 while(QueryServiceStatus(hservice, &status)) {
638 if (status.dwCurrentState == SERVICE_STOP_PENDING) {
645 if (status.dwCurrentState != SERVICE_STOPPED) {
646 MessageBox(NULL, "The Bacula service could not be stopped", szAppName, MB_ICONEXCLAMATION | MB_OK);
650 // Now remove the service from the SCM
651 if(DeleteService(hservice)) {
652 MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
654 MessageBox(NULL, "The Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
657 CloseServiceHandle(hservice);
659 MessageBox(NULL, "The Bacula service could not be found", szAppName, MB_ICONEXCLAMATION | MB_OK);
662 CloseServiceHandle(hsrvmanager);
664 MessageBox(NULL, "The SCM could not be contacted - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
671 // USEFUL SERVICE SUPPORT ROUTINES
673 // Service control routine
674 void WINAPI ServiceCtrl(DWORD ctrlcode)
676 // What control code have we been sent?
678 case SERVICE_CONTROL_STOP:
679 // STOP : The service must stop
680 g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
684 case SERVICE_CONTROL_INTERROGATE:
685 // QUERY : Service control manager just wants to know our state
689 // Control code not recognised
693 // Tell the control manager what we're up to.
694 ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
697 // Service manager status reporting
698 BOOL ReportStatus(DWORD state,
702 static DWORD checkpoint = 1;
705 // If we're in the start state then we don't want the control manager
706 // sending us control messages because they'll confuse us.
707 if (state == SERVICE_START_PENDING) {
708 g_srvstatus.dwControlsAccepted = 0;
710 g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
713 // Save the new status we've been given
714 g_srvstatus.dwCurrentState = state;
715 g_srvstatus.dwWin32ExitCode = exitcode;
716 g_srvstatus.dwWaitHint = waithint;
718 // Update the checkpoint variable to let the SCM know that we
719 // haven't died if requests take a long time
720 if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
721 g_srvstatus.dwCheckPoint = 0;
723 g_srvstatus.dwCheckPoint = checkpoint++;
726 // Tell the SCM our new status
727 if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
728 log_error_message("SetServiceStatus failed");
735 void LogErrorMsg(char *message, char *fname, int lineno)
742 // Get the error code
743 g_error = GetLastError();
744 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
745 FORMAT_MESSAGE_FROM_SYSTEM,
753 // Use event logging to log the error
754 heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
756 sprintf(msgbuff, "\n\n%s error: %ld at %s:%d",
757 BAC_SERVICENAME, g_error, fname, lineno);
758 strings[0] = msgbuff;
759 strings[1] = message;
762 if (heventsrc != NULL) {
766 heventsrc, // handle of event source
767 EVENTLOG_ERROR_TYPE, // event type
770 NULL, // current user's SID
771 3, // strings in 'strings'
772 0, // no bytes of raw data
773 (const char **)strings, // array of error strings
774 NULL); // no raw data
776 DeregisterEventSource(heventsrc);
780 typedef BOOL (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
782 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
786 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
787 SERVICE_DESCRIPTION sdBuf;
789 WinAPI ChangeServiceDescription;
791 HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
795 ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
796 "ChangeServiceConfig2A");
798 if (!ChangeServiceDescription) {
802 // Need to acquire database lock before reconfiguring.
803 sclLock = LockServiceDatabase(hSCManager);
805 // If the database cannot be locked, report the details.
806 if (sclLock == NULL) {
807 // Exit if the database is not locked by another process.
808 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
809 log_error_message("LockServiceDatabase");
813 // Allocate a buffer to get details about the lock.
814 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc(
815 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
816 if (lpqslsBuf == NULL) {
817 log_error_message("LocalAlloc");
821 // Get and print the lock status information.
822 if (!QueryServiceLockStatus(
825 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
827 log_error_message("QueryServiceLockStatus");
830 if (lpqslsBuf->fIsLocked) {
831 printf("Locked by: %s, duration: %ld seconds\n",
832 lpqslsBuf->lpLockOwner,
833 lpqslsBuf->dwLockDuration);
835 printf("No longer locked\n");
838 LocalFree(lpqslsBuf);
839 log_error_message("Could not lock database");
843 // The database is locked, so it is safe to make changes.
845 sdBuf.lpDescription = lpDesc;
847 if(!ChangeServiceDescription(
848 hService, // handle to service
849 SERVICE_CONFIG_DESCRIPTION, // change: description
850 &sdBuf) ) { // value: new description
851 log_error_message("ChangeServiceConfig2");
853 printf("ChangeServiceConfig2 SUCCESS\n");
856 // Release the database lock.
857 UnlockServiceDatabase(sclLock);