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