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,
42 #include "winbacula.h"
43 #include "winservice.h"
47 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
50 // OS-SPECIFIC ROUTINES
52 // Create an instance of the bacService class to cause the static fields to be
53 // initialised properly
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;
73 // IsWin95 - returns a BOOL indicating whether the current OS is Win95
77 return (g_platform_id == VER_PLATFORM_WIN32_WINDOWS);
80 // IsWinNT - returns a bool indicating whether the current OS is WinNT
84 return (g_platform_id == VER_PLATFORM_WIN32_NT);
87 // Internal routine to find the Bacula menu class window and
88 // post a message to it!
91 PostToBacula(UINT message, WPARAM wParam, LPARAM lParam)
93 // Locate the hidden Bacula menu window
94 HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
95 if (hservwnd == NULL) {
99 // Post the message to Bacula
100 PostMessage(hservwnd, message, wParam, lParam);
105 // Static routine to show the Properties dialog for a currently-running
106 // copy of Bacula, (usually a servicified version.)
109 bacService::ShowProperties()
114 // Static routine to show the Default Properties dialog for a currently-running
115 // copy of Bacula, (usually a servicified version.)
118 bacService::ShowDefaultProperties()
123 // Static routine to show the About dialog for a currently-running
124 // copy of Bacula, (usually a servicified version.)
127 bacService::ShowAboutBox()
129 // Post to the Bacula menu window
130 if (!PostToBacula(MENU_ABOUTBOX_SHOW, 0, 0)) {
131 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
137 // Static routine to show the Status dialog for a currently-running
138 // copy of Bacula, (usually a servicified version.)
141 bacService::ShowStatus()
143 // Post to the Bacula menu window
144 if (!PostToBacula(MENU_STATUS_SHOW, 0, 0)) {
145 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
152 // Static routine to show the Events dialog for a currently-running
153 // copy of Bacula, (usually a servicified version.)
156 bacService::ShowEvents()
158 // Post to the Bacula menu window
159 if (!PostToBacula(MENU_EVENTS_SHOW, 0, 0)) {
160 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
167 // Static routine to tell a locally-running instance of the server
168 // to connect out to a new client
171 bacService::PostAddNewClient(unsigned long ipaddress)
173 // Post to the Bacula menu window
174 if (!PostToBacula(MENU_ADD_CLIENT_MSG, 0, ipaddress)) {
175 MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
183 // SERVICE-MODE ROUTINES
185 // Service-mode defines:
188 #define BAC_APPNAME "bacula"
190 // Internal service name
191 #define BAC_SERVICENAME "Bacula"
193 // Displayed service name
194 #define BAC_SERVICEDISPLAYNAME "Bacula File Server"
196 // List other required serves
197 #define BAC_DEPENDENCIES ""
200 // Internal service state
201 SERVICE_STATUS g_srvstatus; // current status of the service
202 SERVICE_STATUS_HANDLE g_hstatus;
204 DWORD g_servicethread = 0;
205 char* g_errortext[256];
208 // Forward defines of internal service functions
209 void WINAPI ServiceMain(DWORD argc, char **argv);
210 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
212 void WINAPI ServiceCtrl(DWORD ctrlcode);
213 bool WINAPI CtrlHandler (DWORD ctrltype);
214 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
216 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
218 BOOL g_servicemode = FALSE;
221 bacService::RunningAsService()
223 return g_servicemode;
227 bacService::KillRunningCopy()
229 while (PostToBacula(WM_CLOSE, 0, 0))
236 // ROUTINE TO POST THE HANDLE OF THE CURRENT USER TO THE RUNNING Bacula, IN ORDER
237 // THAT IT CAN LOAD THE APPROPRIATE SETTINGS. THIS IS USED ONLY BY THE SVCHELPER
238 // OPTION, WHEN RUNNING UNDER NT
240 bacService::PostUserHelperMessage()
242 // - Check the platform type
247 // - Get the current process ID
248 DWORD processId = GetCurrentProcessId();
250 // - Post it to the existing Bacula
251 if (!PostToBacula(MENU_SERVICEHELPER_MSG, 0, (LPARAM)processId)) {
255 // - Wait until it's been used
259 // ROUTINE TO PROCESS AN INCOMING INSTANCE OF THE ABOVE MESSAGE
261 bacService::ProcessUserHelperMessage(WPARAM wParam, LPARAM lParam) {
267 // SERVICE MAIN ROUTINE
269 bacService::BaculaServiceMain()
271 // Mark that we are a service
272 g_servicemode = TRUE;
274 // How to run as a service depends upon the OS being used
275 switch (g_platform_id) {
278 case VER_PLATFORM_WIN32_WINDOWS:
280 // Obtain a handle to the kernel library
281 HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
282 if (kerneldll == NULL) {
283 MessageBox(NULL, "KERNEL32.DLL not found: Bacula service not started",
284 "Bacula Service", MB_OK);
288 // And find the RegisterServiceProcess function
289 DWORD (WINAPI *RegisterService)(DWORD, DWORD);
290 RegisterService = (DWORD (WINAPI *)(DWORD, DWORD))
291 GetProcAddress(kerneldll, "RegisterServiceProcess");
292 if (RegisterService == NULL) {
293 MessageBox(NULL, "Registry service not found: Bacula service not started",
294 "Bacula Service", MB_OK);
295 log_error_message("Registry service not found");
299 // Register this process with the OS as a service!
300 RegisterService(0, 1);
302 // Run the main program as a service
305 // Then remove the service from the system service table
306 RegisterService(0, 0);
308 // Free the kernel library
309 FreeLibrary(kerneldll);
314 // Windows NT, Win2K, WinXP
315 case VER_PLATFORM_WIN32_NT:
317 // Create a service entry table
318 SERVICE_TABLE_ENTRY dispatchTable[] = {
319 {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
323 // Call the service control dispatcher with our entry table
324 if (!StartServiceCtrlDispatcher(dispatchTable)) {
325 log_error_message("StartServiceCtrlDispatcher failed.");
333 // SERVICE MAIN ROUTINE - NT ONLY !!!
334 // NT/Win2K/WinXP ONLY !!!
335 void WINAPI ServiceMain(DWORD argc, char **argv)
339 // Register the service control handler
340 g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
342 if (g_hstatus == 0) {
343 log_error_message("RegisterServiceCtlHandler failed");
344 MessageBox(NULL, "Contact Register Service Handler failure",
345 "Bacula service", MB_OK);
349 // Set up some standard service state values
350 g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
351 g_srvstatus.dwServiceSpecificExitCode = 0;
353 // Give this status to the SCM
355 SERVICE_START_PENDING, // Service state
356 NO_ERROR, // Exit code type
357 45000)) { // Hint as to how long Bacula should have hung before you assume error
359 ReportStatus(SERVICE_STOPPED, g_error, 0);
360 log_error_message("ReportStatus STOPPED failed 1");
364 // Now start the service for real
365 (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
369 // SERVICE START ROUTINE - thread that calls BaculaAppMain
371 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
374 // Save the current thread identifier
375 g_servicethread = GetCurrentThreadId();
377 // report the status to the service control manager.
380 SERVICE_RUNNING, // service state
381 NO_ERROR, // exit code
383 MessageBox(NULL, "Report Service failure", "Bacula Service", MB_OK);
384 log_error_message("ReportStatus RUNNING failed");
388 /* Call Bacula main code */
391 /* Mark that we're no longer running */
394 /* Tell the service manager that we've stopped */
395 ReportStatus(SERVICE_STOPPED, g_error, 0);
400 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
403 // Post a quit message to the main service thread
404 if (g_servicethread != 0) {
405 PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
409 // SERVICE INSTALL ROUTINE
411 bacService::InstallService()
413 const int pathlength = 2048;
414 char path[pathlength];
415 char servicecmd[pathlength];
418 // Get the filename of this executable
419 if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
420 MessageBox(NULL, "Unable to install Bacula service", szAppName, MB_ICONEXCLAMATION | MB_OK);
424 // Append the service-start flag to the end of the path:
425 if ((int)strlen(path) + 20 + (int)strlen(BaculaRunService) < pathlength) {
426 sprintf(servicecmd, "\"%s\" %s -c \"%s\"", path, BaculaRunService, path);
427 len = strlen(servicecmd) - 1;
428 for ( ; len > 0; len--) {
429 if (servicecmd[len] == '\\') {
435 strcat(servicecmd, "\\bacula-fd.conf");
438 log_error_message("Service command length too long");
439 MessageBox(NULL, "Service command length too long. Service not registered.",
440 szAppName, MB_ICONEXCLAMATION | MB_OK);
444 // How to add the Bacula service depends upon the OS
445 switch (g_platform_id) {
448 case VER_PLATFORM_WIN32_WINDOWS:
449 // Locate the RunService registry entry
451 if (RegCreateKey(HKEY_LOCAL_MACHINE,
452 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
453 &runservices) != ERROR_SUCCESS) {
454 log_error_message("Cannot write System Registry");
455 MessageBox(NULL, "The System Registry could not be updated - the Bacula service was not installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
459 // Attempt to add a Bacula key
460 if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS) {
461 RegCloseKey(runservices);
462 log_error_message("Cannot add Bacula key to System Registry");
463 MessageBox(NULL, "The Bacula service could not be installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
467 RegCloseKey(runservices);
469 // We have successfully installed the service!
471 "The Bacula File service was successfully installed.\n"
472 "The service may be started by double clicking on the\n"
473 "Bacula \"Start\" icon and will be automatically\n"
474 "be run the next time this machine is rebooted. ",
476 MB_ICONINFORMATION | MB_OK);
479 // Windows NT, Win2K, WinXP
480 case VER_PLATFORM_WIN32_NT:
482 SC_HANDLE hsrvmanager;
484 // Open the default, local Service Control Manager database
485 hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
486 if (hsrvmanager == NULL) {
487 log_error_message("OpenSCManager failed");
489 "The Service Control Manager could not be contacted - the Bacula service was not installed",
490 szAppName, MB_ICONEXCLAMATION | MB_OK);
494 // Create an entry for the Bacula service
495 hservice = CreateService(
496 hsrvmanager, // SCManager database
497 BAC_SERVICENAME, // name of service
498 BAC_SERVICEDISPLAYNAME, // name to display
499 SERVICE_ALL_ACCESS, // desired access
500 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
502 SERVICE_AUTO_START, // start type
503 SERVICE_ERROR_NORMAL, // error control type
504 servicecmd, // service's binary
505 NULL, // no load ordering group
506 NULL, // no tag identifier
507 BAC_DEPENDENCIES, // dependencies
508 NULL, // LocalSystem account
509 NULL); // no password
510 if (hservice == NULL) {
511 CloseServiceHandle(hsrvmanager);
512 log_error_message("CreateService failed");
514 "The Bacula service could not be installed",
515 szAppName, MB_ICONEXCLAMATION | MB_OK);
519 set_service_description(hsrvmanager,hservice,
520 "Provides file backup and restore services. Bacula -- the network backup solution.");
522 CloseServiceHandle(hsrvmanager);
523 CloseServiceHandle(hservice);
525 // Now install the servicehelper registry setting...
526 // Locate the RunService registry entry
528 if (RegCreateKey(HKEY_LOCAL_MACHINE,
529 "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
530 &runapps) != ERROR_SUCCESS) {
531 MessageBox(NULL, "WARNING: Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded",
532 szAppName, MB_ICONEXCLAMATION | MB_OK);
534 char servicehelpercmd[pathlength];
536 // Append the service-helper-start flag to the end of the path:
537 if ((int)strlen(path) + 4 + (int)strlen(BaculaRunServiceHelper) < pathlength) {
538 sprintf(servicehelpercmd, "\"%s\" %s", path, BaculaRunServiceHelper);
540 // Add the Bacula Service Helper entry
541 if (RegSetValueEx(runapps, szAppName, 0, REG_SZ,
542 (unsigned char *)servicehelpercmd, strlen(servicehelpercmd)+1) != ERROR_SUCCESS) {
543 MessageBox(NULL, "WARNING:Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded", szAppName, MB_ICONEXCLAMATION | MB_OK);
545 RegCloseKey(runapps);
549 // Everything went fine
551 "The Bacula File service was successfully installed.\n"
552 "The service may be started from the Control Panel and will\n"
553 "automatically be run the next time this machine is rebooted.",
555 MB_ICONINFORMATION | MB_OK);
558 log_error_message("Unknown Windows System version");
560 "Unknown Windows operating system.\n"
561 "Cannot install Bacula service.\n",
562 szAppName, MB_ICONEXCLAMATION | MB_OK);
570 // SERVICE REMOVE ROUTINE
572 bacService::RemoveService()
574 // How to remove the Bacula service depends upon the OS
575 switch (g_platform_id) {
578 case VER_PLATFORM_WIN32_WINDOWS:
579 // Locate the RunService registry entry
581 if (RegOpenKey(HKEY_LOCAL_MACHINE,
582 "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
583 &runservices) != ERROR_SUCCESS) {
585 "Could not find registry entry.\nService probably not registerd - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
587 // Attempt to delete the Bacula key
588 if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS) {
589 RegCloseKey(runservices);
590 MessageBox(NULL, "Could not delete Registry key.\nThe Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
593 RegCloseKey(runservices);
597 // Try to kill any running copy of Bacula
598 if (!KillRunningCopy()) {
600 "Bacula could not be contacted, probably not running",
601 szAppName, MB_ICONEXCLAMATION | MB_OK);
605 // We have successfully removed the service!
606 MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
609 // Windows NT, Win2K, WinXP
610 case VER_PLATFORM_WIN32_NT:
612 SC_HANDLE hsrvmanager;
614 // Attempt to remove the service-helper hook
616 if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
617 &runapps) == ERROR_SUCCESS) {
618 // Attempt to delete the Bacula key
619 if (RegDeleteValue(runapps, szAppName) != ERROR_SUCCESS) {
620 MessageBox(NULL, "WARNING:The ServiceHelper hook entry could not be removed from the registry", szAppName, MB_ICONEXCLAMATION | MB_OK);
622 RegCloseKey(runapps);
626 hsrvmanager = OpenSCManager(
627 NULL, // machine (NULL == local)
628 NULL, // database (NULL == default)
629 SC_MANAGER_ALL_ACCESS // access required
632 hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
633 if (hservice != NULL) {
634 SERVICE_STATUS status;
636 // Try to stop the Bacula service
637 if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
638 while(QueryServiceStatus(hservice, &status)) {
639 if (status.dwCurrentState == SERVICE_STOP_PENDING) {
646 if (status.dwCurrentState != SERVICE_STOPPED) {
647 MessageBox(NULL, "The Bacula service could not be stopped", szAppName, MB_ICONEXCLAMATION | MB_OK);
651 // Now remove the service from the SCM
652 if(DeleteService(hservice)) {
653 MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
655 MessageBox(NULL, "The Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
658 CloseServiceHandle(hservice);
660 MessageBox(NULL, "The Bacula service could not be found", szAppName, MB_ICONEXCLAMATION | MB_OK);
663 CloseServiceHandle(hsrvmanager);
665 MessageBox(NULL, "The SCM could not be contacted - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
672 // USEFUL SERVICE SUPPORT ROUTINES
674 // Service control routine
675 void WINAPI ServiceCtrl(DWORD ctrlcode)
677 // What control code have we been sent?
679 case SERVICE_CONTROL_STOP:
680 // STOP : The service must stop
681 g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
685 case SERVICE_CONTROL_INTERROGATE:
686 // QUERY : Service control manager just wants to know our state
690 // Control code not recognised
694 // Tell the control manager what we're up to.
695 ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
698 // Service manager status reporting
699 BOOL ReportStatus(DWORD state,
703 static DWORD checkpoint = 1;
706 // If we're in the start state then we don't want the control manager
707 // sending us control messages because they'll confuse us.
708 if (state == SERVICE_START_PENDING) {
709 g_srvstatus.dwControlsAccepted = 0;
711 g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
714 // Save the new status we've been given
715 g_srvstatus.dwCurrentState = state;
716 g_srvstatus.dwWin32ExitCode = exitcode;
717 g_srvstatus.dwWaitHint = waithint;
719 // Update the checkpoint variable to let the SCM know that we
720 // haven't died if requests take a long time
721 if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
722 g_srvstatus.dwCheckPoint = 0;
724 g_srvstatus.dwCheckPoint = checkpoint++;
727 // Tell the SCM our new status
728 if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
729 log_error_message("SetServiceStatus failed");
736 void LogErrorMsg(char *message, char *fname, int lineno)
743 // Get the error code
744 g_error = GetLastError();
745 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
746 FORMAT_MESSAGE_FROM_SYSTEM,
754 // Use event logging to log the error
755 heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
757 sprintf(msgbuff, "\n\n%s error: %ld at %s:%d",
758 BAC_SERVICENAME, g_error, fname, lineno);
759 strings[0] = msgbuff;
760 strings[1] = message;
763 if (heventsrc != NULL) {
767 heventsrc, // handle of event source
768 EVENTLOG_ERROR_TYPE, // event type
771 NULL, // current user's SID
772 3, // strings in 'strings'
773 0, // no bytes of raw data
774 (const char **)strings, // array of error strings
775 NULL); // no raw data
777 DeregisterEventSource(heventsrc);
781 typedef BOOL (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
783 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
787 LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
788 SERVICE_DESCRIPTION sdBuf;
790 WinAPI ChangeServiceDescription;
792 HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
796 ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
797 "ChangeServiceConfig2A");
799 if (!ChangeServiceDescription) {
803 // Need to acquire database lock before reconfiguring.
804 sclLock = LockServiceDatabase(hSCManager);
806 // If the database cannot be locked, report the details.
807 if (sclLock == NULL) {
808 // Exit if the database is not locked by another process.
809 if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
810 log_error_message("LockServiceDatabase");
814 // Allocate a buffer to get details about the lock.
815 lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc(
816 LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
817 if (lpqslsBuf == NULL) {
818 log_error_message("LocalAlloc");
822 // Get and print the lock status information.
823 if (!QueryServiceLockStatus(
826 sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
828 log_error_message("QueryServiceLockStatus");
831 if (lpqslsBuf->fIsLocked) {
832 printf("Locked by: %s, duration: %ld seconds\n",
833 lpqslsBuf->lpLockOwner,
834 lpqslsBuf->dwLockDuration);
836 printf("No longer locked\n");
839 LocalFree(lpqslsBuf);
840 log_error_message("Could not lock database");
844 // The database is locked, so it is safe to make changes.
846 sdBuf.lpDescription = lpDesc;
848 if(!ChangeServiceDescription(
849 hService, // handle to service
850 SERVICE_CONFIG_DESCRIPTION, // change: description
851 &sdBuf) ) { // value: new description
852 log_error_message("ChangeServiceConfig2");
854 printf("ChangeServiceConfig2 SUCCESS\n");
857 // Release the database lock.
858 UnlockServiceDatabase(sclLock);