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