]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/win32/winservice.cpp
- Apply patch from Christopher Hull
[bacula/bacula] / bacula / src / filed / win32 / winservice.cpp
1 //  Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
2 //
3 //  This file was part of the VNC system.
4 //
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.
9 //
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.
14 //
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,
18 //  USA.
19 //
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.
23 //
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.
27 //
28 // Copyright (C) 2000-2006 Kern E. Sibbald
29 //
30
31
32 // winService
33
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,
37 // show events, ...)
38
39
40 #include <lmcons.h>
41 #undef PASCAL
42 #include "winbacula.h"
43 #include "winservice.h"
44 #include "wintray.h"
45 #include "bacula.h" 
46
47 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
48                              LPSTR lpDesc);
49
50 // OS-SPECIFIC ROUTINES
51
52 // Create an instance of the bacService class to cause the static fields to be
53 // initialised properly
54
55 bacService init;
56
57 extern DWORD   g_platform_id;
58
59 bacService::bacService()
60 {
61    OSVERSIONINFO osversioninfo;
62    osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
63
64    // Get the current OS version
65    if (!GetVersionEx(&osversioninfo)) {
66       g_platform_id = 0;
67    } else {
68       g_platform_id = osversioninfo.dwPlatformId;
69    }
70 }
71
72
73 // IsWin95 - returns a BOOL indicating whether the current OS is Win95
74 BOOL
75 bacService::IsWin95()
76 {
77    return (g_platform_id == VER_PLATFORM_WIN32_WINDOWS);
78 }
79
80 // IsWinNT - returns a bool indicating whether the current OS is WinNT
81 BOOL
82 bacService::IsWinNT()
83 {
84    return (g_platform_id == VER_PLATFORM_WIN32_NT);
85 }
86
87 // Internal routine to find the  Bacula menu class window and
88 // post a message to it!
89
90 BOOL
91 PostToBacula(UINT message, WPARAM wParam, LPARAM lParam)
92 {
93   // Locate the hidden Bacula menu window
94   HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
95   if (hservwnd == NULL) {
96      return FALSE;
97   }
98
99   // Post the message to Bacula
100   PostMessage(hservwnd, message, wParam, lParam);
101   return TRUE;
102 }
103
104
105 // Static routine to show the Properties dialog for a currently-running
106 // copy of Bacula, (usually a servicified version.)
107
108 BOOL
109 bacService::ShowProperties()
110 {
111    return TRUE;
112 }
113
114 // Static routine to show the Default Properties dialog for a currently-running
115 // copy of Bacula, (usually a servicified version.)
116
117 BOOL
118 bacService::ShowDefaultProperties()
119 {
120    return TRUE;
121 }
122
123 // Static routine to show the About dialog for a currently-running
124 // copy of Bacula, (usually a servicified version.)
125
126 BOOL
127 bacService::ShowAboutBox()
128 {
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);
132      return FALSE;
133   }
134   return TRUE;
135 }
136
137 // Static routine to show the Status dialog for a currently-running
138 // copy of Bacula, (usually a servicified version.)
139
140 BOOL
141 bacService::ShowStatus()
142 {
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);
146      return FALSE;
147   }
148   return TRUE;
149 }
150
151 // SERVICE-MODE ROUTINES
152
153 // Service-mode defines:
154
155 // Executable name
156 #define BAC_APPNAME            "bacula"
157
158 // Internal service name
159 #define BAC_SERVICENAME        "Bacula"
160
161 // Displayed service name
162 #define BAC_SERVICEDISPLAYNAME "Bacula File Server"
163
164 // List other required serves 
165 #define BAC_DEPENDENCIES __TEXT("tcpip\0afd\0+File System\0") 
166
167
168 // Internal service state
169 SERVICE_STATUS          g_srvstatus;       // current status of the service
170 SERVICE_STATUS_HANDLE   g_hstatus;
171 DWORD                   g_error = 0;
172 DWORD                   g_servicethread = 0;
173 char*                   g_errortext[256];
174
175
176 // Forward defines of internal service functions
177 void WINAPI ServiceMain(DWORD argc, char **argv);
178 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
179 void ServiceStop();
180 void WINAPI ServiceCtrl(DWORD ctrlcode);
181 bool WINAPI CtrlHandler (DWORD ctrltype);
182 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
183
184 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
185
186 BOOL    g_servicemode = FALSE;
187
188 BOOL
189 bacService::RunningAsService()
190 {
191    return g_servicemode;
192 }
193
194 BOOL
195 bacService::KillRunningCopy()
196 {
197   while (PostToBacula(WM_CLOSE, 0, 0))
198       {  }
199   return TRUE;
200 }
201
202 // SERVICE MAIN ROUTINE
203 int
204 bacService::BaculaServiceMain()
205 {
206    // Mark that we are a service
207    g_servicemode = TRUE;
208
209    // How to run as a service depends upon the OS being used
210    switch (g_platform_id) {
211
212    // Windows 95/98/Me
213    case VER_PLATFORM_WIN32_WINDOWS:
214       {
215       // Obtain a handle to the kernel library
216       HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
217       if (kerneldll == NULL) {
218          MessageBox(NULL, _("KERNEL32.DLL not found: Bacula service not started"), 
219              "Bacula Service", MB_OK);
220          break;
221       }
222
223       // And find the RegisterServiceProcess function
224       DWORD (WINAPI *RegisterService)(DWORD, DWORD);
225       RegisterService = (DWORD (WINAPI *)(DWORD, DWORD))
226               GetProcAddress(kerneldll, "RegisterServiceProcess");
227       if (RegisterService == NULL) {
228          MessageBox(NULL, _("Registry service not found: Bacula service not started"),
229             "Bacula Service", MB_OK);
230          log_error_message(_("Registry service not found")); 
231          break;
232       }
233       
234       // Register this process with the OS as a service!
235       RegisterService(0, 1);
236
237       // Run the main program as a service
238       BaculaAppMain();
239
240       // Then remove the service from the system service table
241       RegisterService(0, 0);
242
243       // Free the kernel library
244       FreeLibrary(kerneldll);
245       break;
246       }
247
248
249    // Windows NT, Win2K, WinXP 
250    case VER_PLATFORM_WIN32_NT:
251       {
252       // Create a service entry table
253       SERVICE_TABLE_ENTRY dispatchTable[] = {
254          {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
255          {NULL, NULL}
256       };
257
258       // Call the service control dispatcher with our entry table
259       if (!StartServiceCtrlDispatcher(dispatchTable)) {
260          log_error_message(_("StartServiceCtrlDispatcher failed."));
261       }
262       break;
263       } /* end case */
264    } /* end switch */
265    return 0;
266 }
267
268 // SERVICE MAIN ROUTINE - NT ONLY !!!
269 // NT/Win2K/WinXP ONLY !!!
270 void WINAPI ServiceMain(DWORD argc, char **argv)
271 {
272     DWORD dwThreadID;
273
274     // Register the service control handler
275     g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
276
277     if (g_hstatus == 0) {
278        log_error_message(_("RegisterServiceCtlHandler failed")); 
279        MessageBox(NULL, _("Contact Register Service Handler failure"),
280           "Bacula service", MB_OK);
281        return;
282     }
283
284      // Set up some standard service state values
285     g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
286     g_srvstatus.dwServiceSpecificExitCode = 0;
287
288         // Give this status to the SCM
289     if (!ReportStatus(
290             SERVICE_START_PENDING,          // Service state
291             NO_ERROR,                       // Exit code type
292             45000)) {                       // Hint as to how long Bacula should have hung before you assume error
293
294         ReportStatus(SERVICE_STOPPED, g_error,  0);
295         log_error_message(_("ReportStatus STOPPED failed 1")); 
296         return;
297     }
298
299         // Now start the service for real
300     (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
301     return;
302 }
303
304 // SERVICE START ROUTINE - thread that calls BaculaAppMain
305 //   NT ONLY !!!!
306 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
307 {
308
309     // Save the current thread identifier
310     g_servicethread = GetCurrentThreadId();
311
312     // report the status to the service control manager.
313     //
314     if (!ReportStatus(
315           SERVICE_RUNNING,       // service state
316           NO_ERROR,              // exit code
317           0)) {                  // wait hint
318        MessageBox(NULL, _("Report Service failure"), "Bacula Service", MB_OK);
319        log_error_message("ReportStatus RUNNING failed"); 
320        return 0;
321     }
322
323     /* Call Bacula main code */
324     BaculaAppMain();
325
326     /* Mark that we're no longer running */
327     g_servicethread = 0;
328
329     /* Tell the service manager that we've stopped */
330     ReportStatus(SERVICE_STOPPED, g_error, 0);
331     return 0;
332 }
333
334
335 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
336 void ServiceStop()
337 {
338    // Post a quit message to the main service thread
339    if (g_servicethread != 0) {
340       PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
341    }
342 }
343
344 // SERVICE INSTALL ROUTINE
345 int
346 bacService::InstallService(bool silent)
347 {
348    const int pathlength = 2048;
349    char path[pathlength];
350    char servicecmd[pathlength];
351    int len;
352
353    // Get the filename of this executable
354    if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
355       MessageBox(NULL, _("Unable to install Bacula service"), szAppName, MB_ICONEXCLAMATION | MB_OK);
356       return 0;
357    }
358
359    // Append the service-start flag to the end of the path:
360    if ((int)strlen(path) + 20 + (int)strlen(BaculaRunService) < pathlength) {
361       sprintf(servicecmd, "\"%s\" %s -c \"%s\"", path, BaculaRunService, path);
362       len = strlen(servicecmd) - 1;
363       for ( ; len > 0; len--) {
364          if (servicecmd[len] == '\\') {
365             servicecmd[len] = 0;
366             break;
367          }
368          servicecmd[len] = 0;
369       }
370       strcat(servicecmd, "\\bacula-fd.conf");
371
372    } else {
373       log_error_message(_("Service command length too long")); 
374       MessageBox(NULL, _("Service command length too long. Service not registered."),
375           szAppName, MB_ICONEXCLAMATION | MB_OK);
376       return 0;
377    }
378
379    // How to add the Bacula service depends upon the OS
380    switch (g_platform_id) {
381
382    // Windows 95/98/Me
383    case VER_PLATFORM_WIN32_WINDOWS:
384       // Locate the RunService registry entry
385       HKEY runservices;
386       if (RegCreateKey(HKEY_LOCAL_MACHINE, 
387               "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
388               &runservices) != ERROR_SUCCESS) {
389          log_error_message(_("Cannot write System Registry")); 
390          MessageBox(NULL, _("The System Registry could not be updated - the Bacula service was not installed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
391          break;
392       }
393
394       // Attempt to add a Bacula key
395       if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS) {
396          RegCloseKey(runservices);
397          log_error_message(_("Cannot add Bacula key to System Registry")); 
398          MessageBox(NULL, _("The Bacula service could not be installed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
399          break;
400       }
401
402       RegCloseKey(runservices);
403
404       // We have successfully installed the service!
405       if (!silent) {
406          MessageBox(NULL,
407               _("The Bacula File service was successfully installed.\n"
408               "The service may be started by double clicking on the\n"
409               "Bacula \"Start\" icon and will be automatically\n"
410               "be run the next time this machine is rebooted. "),
411               szAppName,
412               MB_ICONINFORMATION | MB_OK);
413       }
414       break;
415
416    // Windows NT, Win2K, WinXP
417    case VER_PLATFORM_WIN32_NT:
418       SC_HANDLE   hservice;
419       SC_HANDLE   hsrvmanager;
420
421       // Open the default, local Service Control Manager database
422       hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
423       if (hsrvmanager == NULL) {
424          log_error_message("OpenSCManager failed"); 
425          MessageBox(NULL,
426             _("The Service Control Manager could not be contacted - the Bacula service was not installed"),
427             szAppName, MB_ICONEXCLAMATION | MB_OK);
428          break;
429       }
430
431       // Create an entry for the Bacula service
432       hservice = CreateService(
433               hsrvmanager,                    // SCManager database
434               BAC_SERVICENAME,                // name of service
435               BAC_SERVICEDISPLAYNAME,         // name to display
436               SERVICE_ALL_ACCESS,             // desired access
437               SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
438                                                                       // service type
439               SERVICE_AUTO_START,             // start type
440               SERVICE_ERROR_NORMAL,           // error control type
441               servicecmd,                     // service's binary
442               NULL,                           // no load ordering group
443               NULL,                           // no tag identifier
444               BAC_DEPENDENCIES,               // dependencies
445               NULL,                           // LocalSystem account
446               NULL);                          // no password
447       if (hservice == NULL) {
448          CloseServiceHandle(hsrvmanager);
449          log_error_message("CreateService failed"); 
450          MessageBox(NULL,
451              _("The Bacula service could not be installed"),
452               szAppName, MB_ICONEXCLAMATION | MB_OK);
453          break;
454       }
455
456       set_service_description(hsrvmanager,hservice, 
457 _("Provides file backup and restore services. Bacula -- the network backup solution."));
458
459       CloseServiceHandle(hsrvmanager);
460       CloseServiceHandle(hservice);
461
462       // Everything went fine
463       if (!silent) {
464          MessageBox(NULL,
465               _("The Bacula File service was successfully installed.\n"
466               "The service may be started from the Control Panel and will\n"
467               "automatically be run the next time this machine is rebooted."),
468               szAppName,
469               MB_ICONINFORMATION | MB_OK);
470       }
471       break;
472    default:
473       log_error_message("Unknown Windows System version"); 
474       MessageBox(NULL, 
475                  _("Unknown Windows operating system.\n"     
476                  "Cannot install Bacula service.\n"),
477                  szAppName, MB_ICONEXCLAMATION | MB_OK);
478        break;     
479    };
480
481    return 0;
482 }
483
484
485 // SERVICE REMOVE ROUTINE
486 int
487 bacService::RemoveService(bool silent)
488 {
489    // How to remove the Bacula service depends upon the OS
490    switch (g_platform_id) {
491
492    // Windows 95/98/Me
493    case VER_PLATFORM_WIN32_WINDOWS:
494       // Locate the RunService registry entry
495       HKEY runservices;
496       if (RegOpenKey(HKEY_LOCAL_MACHINE, 
497               "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
498               &runservices) != ERROR_SUCCESS) {
499          MessageBox(NULL, 
500             _("Could not find registry entry.\nService probably not registerd - the Bacula service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
501       } else {
502          // Attempt to delete the Bacula key
503          if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS) {
504             RegCloseKey(runservices);
505             MessageBox(NULL, _("Could not delete Registry key.\nThe Bacula service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
506          }
507
508          RegCloseKey(runservices);
509          break;
510       }
511
512       // Try to kill any running copy of Bacula
513       if (!KillRunningCopy()) {
514          MessageBox(NULL,
515              _("Bacula could not be contacted, probably not running"),
516              szAppName, MB_ICONEXCLAMATION | MB_OK);
517          break;
518       }
519
520       // We have successfully removed the service!
521       if (!silent) {
522          MessageBox(NULL, _("The Bacula service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
523       }
524       break;
525
526    // Windows NT, Win2K, WinXP
527    case VER_PLATFORM_WIN32_NT:
528       SC_HANDLE   hservice;
529       SC_HANDLE   hsrvmanager;
530
531       // Open the SCM
532       hsrvmanager = OpenSCManager(
533          NULL,                   // machine (NULL == local)
534          NULL,                   // database (NULL == default)
535          SC_MANAGER_ALL_ACCESS   // access required
536          );
537       if (hsrvmanager) {
538          hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
539          if (hservice != NULL) {
540             SERVICE_STATUS status;
541
542             // Try to stop the Bacula service
543             if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
544                while(QueryServiceStatus(hservice, &status)) {
545                   if (status.dwCurrentState == SERVICE_STOP_PENDING) {
546                      Sleep(1000);
547                   } else {
548                      break;
549                   }
550                }
551
552                if (status.dwCurrentState != SERVICE_STOPPED) {
553                   MessageBox(NULL, _("The Bacula service could not be stopped"), szAppName, MB_ICONEXCLAMATION | MB_OK);
554                }
555             }
556
557             // Now remove the service from the SCM
558             if (DeleteService(hservice)) {
559                if (!silent) {
560                   MessageBox(NULL, _("The Bacula service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
561                }
562             } else {
563                MessageBox(NULL, _("The Bacula service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
564             }
565
566             CloseServiceHandle(hservice);
567          } else {
568             MessageBox(NULL, _("The Bacula service could not be found"), szAppName, MB_ICONEXCLAMATION | MB_OK);
569          }
570
571          CloseServiceHandle(hsrvmanager);
572       } else {
573          MessageBox(NULL, _("The SCM could not be contacted - the Bacula service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
574       }
575       break;
576    }
577    return 0;
578 }
579
580 // USEFUL SERVICE SUPPORT ROUTINES
581
582 // Service control routine
583 void WINAPI ServiceCtrl(DWORD ctrlcode)
584 {
585     // What control code have we been sent?
586     switch(ctrlcode) {
587     case SERVICE_CONTROL_STOP:
588         // STOP : The service must stop
589         g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
590         ServiceStop();
591         break;
592
593     case SERVICE_CONTROL_INTERROGATE:
594         // QUERY : Service control manager just wants to know our state
595         break;
596
597      default:
598         // Control code not recognised
599         break;
600     }
601
602     // Tell the control manager what we're up to.
603     ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
604 }
605
606 // Service manager status reporting
607 BOOL ReportStatus(DWORD state,
608                   DWORD exitcode,
609                   DWORD waithint)
610 {
611     static DWORD checkpoint = 1;
612     BOOL result = TRUE;
613
614     // If we're in the start state then we don't want the control manager
615     // sending us control messages because they'll confuse us.
616     if (state == SERVICE_START_PENDING) {
617        g_srvstatus.dwControlsAccepted = 0;
618     } else {
619        g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
620     }
621
622     // Save the new status we've been given
623     g_srvstatus.dwCurrentState = state;
624     g_srvstatus.dwWin32ExitCode = exitcode;
625     g_srvstatus.dwWaitHint = waithint;
626
627     // Update the checkpoint variable to let the SCM know that we
628     // haven't died if requests take a long time
629     if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
630        g_srvstatus.dwCheckPoint = 0;
631     } else {
632        g_srvstatus.dwCheckPoint = checkpoint++;
633     }
634
635     // Tell the SCM our new status
636     if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
637        log_error_message(_("SetServiceStatus failed"));
638     }
639
640     return result;
641 }
642
643 // Error reporting
644 void LogErrorMsg(char *message, char *fname, int lineno)
645 {
646    char        msgbuff[256];
647    HANDLE      heventsrc;
648    char *      strings[32];
649    LPTSTR      msg;
650
651    // Get the error code
652    g_error = GetLastError();
653    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
654                  FORMAT_MESSAGE_FROM_SYSTEM,
655                  NULL,
656                  g_error,
657                  0,
658                  (LPTSTR)&msg,
659                  0,
660                  NULL);
661
662    // Use event logging to log the error
663    heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
664
665    sprintf(msgbuff, _("\n\n%s error: %ld at %s:%d"), 
666       BAC_SERVICENAME, g_error, fname, lineno);
667    strings[0] = msgbuff;
668    strings[1] = message;
669    strings[2] = msg;
670
671    if (heventsrc != NULL) {
672       MessageBeep(MB_OK);
673
674       ReportEvent(
675               heventsrc,              // handle of event source
676               EVENTLOG_ERROR_TYPE,    // event type
677               0,                      // event category
678               0,                      // event ID
679               NULL,                   // current user's SID
680               3,                      // strings in 'strings'
681               0,                      // no bytes of raw data
682               (const char **)strings, // array of error strings
683               NULL);                  // no raw data
684
685       DeregisterEventSource(heventsrc);
686    }
687    LocalFree(msg);
688 }
689 typedef BOOL  (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
690
691 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
692                              LPSTR lpDesc) 
693
694     SC_LOCK sclLock; 
695     LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf; 
696     SERVICE_DESCRIPTION sdBuf;
697     DWORD dwBytesNeeded;
698     WinAPI ChangeServiceDescription;
699  
700     HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
701     if (!hLib) {
702        return;
703     }
704     ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
705        "ChangeServiceConfig2A");
706     FreeLibrary(hLib);
707     if (!ChangeServiceDescription) {
708        return;
709     }
710     
711     // Need to acquire database lock before reconfiguring. 
712     sclLock = LockServiceDatabase(hSCManager); 
713  
714     // If the database cannot be locked, report the details. 
715     if (sclLock == NULL) {
716        // Exit if the database is not locked by another process. 
717        if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
718           log_error_message("LockServiceDatabase"); 
719           return;
720        }
721  
722        // Allocate a buffer to get details about the lock. 
723        lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc( 
724             LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256); 
725        if (lpqslsBuf == NULL) {
726           log_error_message("LocalAlloc"); 
727           return;
728        }
729  
730        // Get and print the lock status information. 
731        if (!QueryServiceLockStatus( 
732               hSCManager, 
733               lpqslsBuf, 
734               sizeof(QUERY_SERVICE_LOCK_STATUS)+256, 
735               &dwBytesNeeded)) {
736           log_error_message("QueryServiceLockStatus"); 
737        }
738  
739        if (lpqslsBuf->fIsLocked) {
740           printf(_("Locked by: %s, duration: %ld seconds\n"), 
741                 lpqslsBuf->lpLockOwner, 
742                 lpqslsBuf->dwLockDuration); 
743        } else {
744           printf(_("No longer locked\n")); 
745        }
746  
747        LocalFree(lpqslsBuf); 
748        log_error_message(_("Could not lock database")); 
749        return;
750     } 
751  
752     // The database is locked, so it is safe to make changes. 
753  
754     sdBuf.lpDescription = lpDesc;
755
756     if (!ChangeServiceDescription(
757          hService,                   // handle to service
758          SERVICE_CONFIG_DESCRIPTION, // change: description
759          &sdBuf) ) {                 // value: new description
760        log_error_message("ChangeServiceConfig2");
761     }
762
763     // Release the database lock. 
764     UnlockServiceDatabase(sclLock); 
765 }