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