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