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