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