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