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