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