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