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