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