]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/libwin32/service.cpp
Implement more automatic build of Win64 client. Note, there are still
[bacula/bacula] / bacula / src / win32 / libwin32 / service.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27  */
28 /* 
29  * 
30  *  Kern Sibbald, August 2007
31  *
32  *  Version $Id$
33  *
34  * This is a generic service routine, which is used by all three
35  *  of the daemons. Each one compiles it with slightly different
36  *  #defines.
37  *
38  */
39
40 #include "bacula.h"
41 #include "win32.h"
42
43 /* Forward reference */
44 static void set_service_description(SC_HANDLE hSCManager, 
45                                     SC_HANDLE hService, LPSTR lpDesc);
46   
47 /* Other Window component dependencies */
48 #define BAC_DEPENDENCIES __TEXT("tcpip\0afd\0") 
49
50 /* Service globals */
51 SERVICE_STATUS_HANDLE  service_handle;
52 SERVICE_STATUS service_status;
53 DWORD service_error = 0;
54 static bool is_service = false;
55
56 /* Forward references */
57 void WINAPI serviceControlCallback(DWORD ctrlcode);
58 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
59 DWORD WINAPI baculaWorkerThread(LPVOID lpwThreadParam);
60
61
62 /*
63  * Post a message to a running instance of the app
64  */
65 bool postToBacula(UINT message, WPARAM wParam, LPARAM lParam)
66 {
67    /* Locate the Bacula menu window */
68    HWND hservwnd = FindWindow(APP_NAME, NULL);
69    if (hservwnd == NULL) {
70       return false;
71    }
72
73    /* Post the message to Bacula */
74    PostMessage(hservwnd, message, wParam, lParam);
75    return true;
76 }
77
78
79 /*
80  * Running as a service?
81  */
82 bool isAService()
83 {
84    return is_service;
85 }
86
87 /* 
88  * terminate any running Bacula
89  */
90 int stopRunningBacula()
91 {
92    postToBacula(WM_CLOSE, 0, 0);
93    sleep(5);
94    return 0;
95 }
96
97 /*
98  * New style service start callback handler for the OS.
99  *   the OS returns control here immediately after starting
100  *   the service.
101  */
102 void WINAPI serviceStartCallback(DWORD argc, char **argv)
103 {
104    DWORD dwThreadID;
105   
106    /* Register our service */
107    service_handle = RegisterServiceCtrlHandler(APP_NAME, serviceControlCallback);
108    if (!service_handle) {
109       log_error_message(_("RegisterServiceCtlHandler failed")); 
110       MessageBox(NULL, _("Failure contacting the Service Handler"),
111          APP_DESC, MB_OK);
112       return;
113    }
114
115    service_status.dwServiceType = SERVICE_WIN32|SERVICE_INTERACTIVE_PROCESS;
116    service_status.dwServiceSpecificExitCode = 0;
117
118    /* Report status */
119    if (!ReportStatus(SERVICE_START_PENDING, NO_ERROR, 45000)) {
120        ReportStatus(SERVICE_STOPPED, service_error,  0);
121        log_error_message(_("Service start report failed")); 
122        return;
123    }
124
125    /* Now create the Bacula worker thread */
126    (void)CreateThread(NULL, 0, baculaWorkerThread, NULL, 0, &dwThreadID);
127    return;
128 }
129
130 /*
131  *  Stop our service 
132  */
133 static void serviceStop()
134 {
135    /* Post a quit message our service thread */
136    if (service_thread_id != 0) {
137       PostThreadMessage(service_thread_id, WM_QUIT, 0, 0);
138    }
139 }
140
141 /*
142  * Service Control callback handler.  The OS can call us here
143  *   at any time, most often to stop the service.
144  */
145 void WINAPI serviceControlCallback(DWORD ctrlcode)
146 {
147    switch(ctrlcode) {
148    case SERVICE_CONTROL_STOP:
149       service_status.dwCurrentState = SERVICE_STOP_PENDING;
150       serviceStop();  /* our stop service routine */
151       break;
152    }
153
154    /* Report our status */
155    ReportStatus(service_status.dwCurrentState, NO_ERROR, 0);
156 }
157
158
159 /*
160  * Run Bacula as a service 
161  */
162 int baculaServiceMain()
163 {
164    is_service = true;                 /* indicate we are running as a service */
165
166    if (have_service_api) {            /* New style service API */
167       /* Tell OS where to dispatch service calls to us */
168       SERVICE_TABLE_ENTRY dispatchTable[] = {
169          {APP_NAME, (LPSERVICE_MAIN_FUNCTION)serviceStartCallback},
170          {NULL, NULL}};
171
172       /* Start the service control dispatcher */
173       if (!StartServiceCtrlDispatcher(dispatchTable)) {
174          log_error_message(_("StartServiceCtrlDispatcher failed."));
175       }
176       /* Note, this thread continues in the ServiceCallback routine */
177
178    } else {                           /* old style Win95/98/Me */
179       HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
180       if (kerneldll == NULL) {
181          MessageBox(NULL, _("KERNEL32.DLL not found: Bacula service not started"), 
182              APP_DESC, MB_OK);
183          return 1;
184       }
185
186       /* Get entry point for RegisterServiceProcess function */
187       DWORD (WINAPI *RegisterService)(DWORD, DWORD);
188       RegisterService = (DWORD (WINAPI *)(DWORD, DWORD))
189               GetProcAddress(kerneldll, "RegisterServiceProcess");
190       if (RegisterService == NULL) {
191          MessageBox(NULL, _("Registry service not found: Bacula service not started"),
192             APP_DESC, MB_OK);
193          log_error_message(_("Registry service entry point not found")); 
194          return 1;
195       }
196       
197       RegisterService(0, 1);             /* register us as a service */
198       BaculaAppMain();                   /* call the main Bacula code */
199       RegisterService(0, 0);             /* terminate the service */
200       FreeLibrary(kerneldll);            /* free up kernel dll */
201    } 
202    return 0;
203 }
204
205
206 /* 
207  * New style service bacula worker thread
208  */
209 DWORD WINAPI baculaWorkerThread(LPVOID lpwThreadParam)
210 {
211    service_thread_id = GetCurrentThreadId();
212
213    if (!ReportStatus(SERVICE_RUNNING, NO_ERROR, 0)) {
214       MessageBox(NULL, _("Report Service failure"), APP_DESC, MB_OK);
215       log_error_message("ReportStatus RUNNING failed"); 
216       return 0;
217    }
218
219    /* Call Bacula main code */
220    BaculaAppMain();
221
222    /* Mark that we're no longer running */
223    service_thread_id = 0;
224
225    /* Tell the service manager that we've stopped */
226    ReportStatus(SERVICE_STOPPED, service_error, 0);
227    return 0;
228 }
229
230
231
232 /*
233  * Install the Bacula service on the OS -- very complicated
234  */
235 int installService(const char *cmdOpts)
236 {
237    const int maxlen = 2048;
238    char path[maxlen];
239    char svcmd[maxlen];
240
241    bsnprintf(svcmd, sizeof(svcmd), "service: install: %s", cmdOpts, APP_DESC, MB_OK);
242
243    /* Get our filename */
244    if (GetModuleFileName(NULL, path, maxlen-11) == 0) {
245       MessageBox(NULL, _("Unable to install the service"), APP_DESC, MB_ICONEXCLAMATION | MB_OK);
246       return 0;
247    }
248
249    /* Create a valid command for starting the service */
250    if ((int)strlen(path) + (int)strlen(cmdOpts) + 30  < maxlen) {
251       bsnprintf(svcmd, sizeof(svcmd), "\"%s\" /service %s", path, cmdOpts);
252    } else {
253       log_error_message(_("Service command length too long")); 
254       MessageBox(NULL, _("Service command length too long. Service not registered."),
255           APP_DESC, MB_ICONEXCLAMATION | MB_OK);
256       return 0;
257    }
258
259    if (have_service_api) {
260       SC_HANDLE baculaService, serviceManager;
261
262       /* Open the service control manager */
263       serviceManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
264       if (!serviceManager) {
265          log_error_message("Open Service Manager failed"); 
266          MessageBox(NULL,
267             _("The Service Control Manager could not be contacted - the service was not installed"),
268             APP_DESC, MB_ICONEXCLAMATION | MB_OK);
269          return 0;
270       }
271
272       /* Now actually create the Bacula service entry */
273       baculaService = CreateService(
274               serviceManager, 
275               APP_NAME,                       /* Our service name */
276               APP_DESC,                       /* Display name */
277               SERVICE_ALL_ACCESS,
278               SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
279               SERVICE_AUTO_START,
280               SERVICE_ERROR_NORMAL,
281               svcmd,                          /* Command string to start the service */
282               NULL,
283               NULL,
284               BAC_DEPENDENCIES,               /* Services to start before us */
285               NULL,                           /* Use default SYSTEM account */
286               NULL);
287       if (!baculaService) {
288          CloseServiceHandle(serviceManager);
289          log_error_message("CreateService failed for " APP_DESC); 
290          MessageBox(NULL, _("The Bacula service: " APP_NAME " could not be installed"),
291               APP_DESC, MB_ICONEXCLAMATION | MB_OK);
292          return 0;
293       }
294
295       /* Set a text description in the service manager's control panel */
296       set_service_description(serviceManager, baculaService,
297 _("Provides file backup and restore services. Bacula -- the network backup solution."));
298
299       CloseServiceHandle(serviceManager);
300       CloseServiceHandle(baculaService);
301
302    } else {
303       /* Old style service -- create appropriate registry key path */
304       HKEY runservices;
305       if (RegCreateKey(HKEY_LOCAL_MACHINE, 
306               "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
307               &runservices) != ERROR_SUCCESS) {
308          log_error_message(_("Cannot write System Registry for " APP_DESC)); 
309          MessageBox(NULL, _("The System Registry could not be updated - the Bacula service was not installed"), 
310              APP_DESC, MB_ICONEXCLAMATION | MB_OK);
311          return 0;
312       }
313
314       /* Add the Bacula values */
315       if (RegSetValueEx(runservices, APP_NAME, 0, REG_SZ, 
316                         (unsigned char *)svcmd, strlen(svcmd)+1) != ERROR_SUCCESS) {
317          RegCloseKey(runservices);
318          log_error_message(_("Cannot add Bacula key to System Registry")); 
319          MessageBox(NULL, _("The Bacula service: " APP_NAME " could not be installed"), 
320              APP_DESC, MB_ICONEXCLAMATION | MB_OK);
321          return 0;
322       }
323       RegCloseKey(runservices);
324    }
325
326    /* At this point the service is installed */
327    if (opt_debug) {
328       MessageBox(NULL,
329            _("The " APP_DESC "was successfully installed.\n"
330              "The service may be started by double clicking on the\n"
331              "Bacula \"Start\" icon and will be automatically\n"
332              "be run the next time this machine is rebooted. "),
333          APP_DESC, MB_ICONINFORMATION | MB_OK);
334    }
335    return 0;
336 }
337
338
339 /*
340  * Remove a service from the OS (normally done when we are installing
341  *   a new version).
342  */
343 int removeService()
344 {
345    if (have_service_api) {      /* Newer Windows platform (NT, Win2K, ...) */
346       SC_HANDLE serviceManager, baculaService;
347       int stat = 0;
348
349       serviceManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
350       if (serviceManager) {
351          /* Now get the Bacula service entry */
352          baculaService = OpenService(serviceManager, APP_NAME, SERVICE_ALL_ACCESS);
353          if (baculaService) {
354             SERVICE_STATUS status;
355
356             /* If the service is running, stop it */
357             if (ControlService(baculaService, SERVICE_CONTROL_STOP, &status)) {
358                while(QueryServiceStatus(baculaService, &status)) {
359                   if (status.dwCurrentState == SERVICE_STOP_PENDING) {
360                      sleep(1);
361                   } else {
362                      return 0;
363                   }
364                }
365                if (status.dwCurrentState != SERVICE_STOPPED) {
366                   MessageBox(NULL, _("The Bacula service: " APP_NAME " could not be stopped"), 
367                      APP_DESC, MB_ICONEXCLAMATION | MB_OK);
368                }
369             }
370
371             if (DeleteService(baculaService)) {
372                if (opt_debug) {
373                   MessageBox(NULL, _("The Bacula service: " APP_NAME " has been removed"), 
374                      APP_DESC, MB_ICONINFORMATION | MB_OK);
375                }
376             } else {
377                MessageBox(NULL, _("The Bacula service: " APP_NAME " could not be removed"), 
378                   APP_DESC, MB_ICONEXCLAMATION | MB_OK);
379                stat = 1; /* error */
380             }
381             CloseServiceHandle(baculaService);
382
383          } else {
384             MessageBox(NULL, _("A existing Bacula service: " APP_NAME " could not be found for "
385                 "removal. This is not normally an error."),
386                 APP_DESC, MB_ICONEXCLAMATION | MB_OK);
387          }
388          CloseServiceHandle(serviceManager);
389          return stat;
390
391       } else {
392          MessageBox(NULL, _("The service Manager could not be contacted - the Bacula service was not removed"), 
393             APP_DESC, MB_ICONEXCLAMATION | MB_OK);
394          return 1; /* error */
395       }
396
397    } else {                     /* Old Win95/98/Me OS */
398       /* Open the registry path key */
399       HKEY runservices;
400       if (RegOpenKey(HKEY_LOCAL_MACHINE, 
401             "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
402             &runservices) != ERROR_SUCCESS) {
403          MessageBox(NULL, 
404             _("Could not find registry entry.\nService probably not registerd - the Bacula service was not removed"), 
405                APP_DESC, MB_ICONEXCLAMATION | MB_OK);
406       } else {
407          /* Now delete the Bacula entry */
408          if (RegDeleteValue(runservices, APP_NAME) != ERROR_SUCCESS) {
409             RegCloseKey(runservices);
410             MessageBox(NULL, _("Could not delete Registry key for " APP_NAME ".\n"
411                "The Bacula service could not be removed"), APP_DESC, MB_ICONEXCLAMATION | MB_OK);
412          }
413          RegCloseKey(runservices);
414          return 1;
415       }
416
417       /* Stop any running Bacula */
418       if (!stopRunningBacula()) {
419          MessageBox(NULL,
420              _("Bacula could not be contacted, probably not running"),
421              APP_DESC, MB_ICONEXCLAMATION | MB_OK);
422          return 0;   /* not really an error */
423       }
424
425       /* At this point, the service has been removed */
426       if (opt_debug) {
427          MessageBox(NULL, _("The Bacula service has been removed"), APP_DESC, MB_ICONINFORMATION | MB_OK);
428       }
429    }
430    return 0;
431 }
432
433
434 /*
435  * This subroutine is called to report our current status to the
436  *  new style service manager
437  */
438 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint)
439 {
440    static DWORD checkpoint = 1;
441    BOOL result = TRUE;
442
443    /* No callbacks until we are started */
444    if (state == SERVICE_START_PENDING) {
445       service_status.dwControlsAccepted = 0;
446    } else {
447       service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
448    }
449
450    /* Save global service_status state */
451    service_status.dwCurrentState = state;
452    service_status.dwWin32ExitCode = exitcode;
453    service_status.dwWaitHint = waithint;
454
455    /*
456     * Update the checkpoint variable so the service manager knows
457     *   we are alive.
458     */
459    if (state == SERVICE_RUNNING || state == SERVICE_STOPPED) {
460       service_status.dwCheckPoint = 0;
461    } else {
462       service_status.dwCheckPoint = checkpoint++;
463    }
464
465    /* Send our new status */
466    result = SetServiceStatus(service_handle, &service_status);
467    if (!result) {
468       log_error_message(_("SetServiceStatus failed"));
469    }
470    return result;
471 }
472
473 /* Log an error message */
474 void LogErrorMsg(const char *message, const char *fname, int lineno)
475 {
476    char msgbuf[500];
477    HANDLE eventHandler;
478    const char *strings[3];
479    LPTSTR msg;
480
481    service_error = GetLastError();
482    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
483                  FORMAT_MESSAGE_FROM_SYSTEM,
484                  NULL,
485                  service_error,
486                  0,
487                  (LPTSTR)&msg,
488                  0,
489                  NULL);
490
491    /* Use the OS event logging to log the error */
492    eventHandler = RegisterEventSource(NULL, APP_NAME);
493
494    bsnprintf(msgbuf, sizeof(msgbuf), _("\n\n%s error: %ld at %s:%d"), 
495       APP_NAME, service_error, fname, lineno);
496
497    strings[0] = msgbuf;
498    strings[1] = message;
499    strings[2] = msg;
500
501    if (eventHandler) {
502       ReportEvent(eventHandler, EVENTLOG_ERROR_TYPE,
503               0,                      /* category */
504               0,                      /* ID */
505               NULL,                   /* SID */
506               3,                      /* Number of strings */
507               0,                      /* raw data size */
508               (const char **)strings, /* error strings */
509               NULL);                  /* raw data */
510       DeregisterEventSource(eventHandler);
511    }
512    LocalFree(msg);
513 }
514
515 typedef BOOL  (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
516
517 /*
518  * This is amazingly complicated just to get a bit of English explanation
519  *  in the service manager's dialog box.
520  */
521 static void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
522                              LPSTR lpDesc) 
523
524     SC_LOCK sclLock; 
525     LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf; 
526     SERVICE_DESCRIPTION sdBuf;
527     DWORD dwBytesNeeded;
528     WinAPI ChangeServiceDescription;
529  
530     HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
531     if (!hLib) {
532        return;
533     }
534     ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
535        "ChangeServiceConfig2A");
536     FreeLibrary(hLib);
537     if (!ChangeServiceDescription) {
538        return;
539     }
540     
541     // Need to acquire database lock before reconfiguring. 
542     sclLock = LockServiceDatabase(hSCManager); 
543  
544     // If the database cannot be locked, report the details. 
545     if (sclLock == NULL) {
546        // Exit if the database is not locked by another process. 
547        if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
548           log_error_message("LockServiceDatabase"); 
549           return;
550        }
551  
552        // Allocate a buffer to get details about the lock. 
553        lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc( 
554             LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256); 
555        if (lpqslsBuf == NULL) {
556           log_error_message("LocalAlloc"); 
557           return;
558        }
559  
560        // Get and print the lock status information. 
561        if (!QueryServiceLockStatus( 
562               hSCManager, 
563               lpqslsBuf, 
564               sizeof(QUERY_SERVICE_LOCK_STATUS)+256, 
565               &dwBytesNeeded)) {
566           log_error_message("QueryServiceLockStatus"); 
567        }
568  
569        if (lpqslsBuf->fIsLocked) {
570           printf(_("Locked by: %s, duration: %ld seconds\n"), 
571                 lpqslsBuf->lpLockOwner, 
572                 lpqslsBuf->dwLockDuration); 
573        } else {
574           printf(_("No longer locked\n")); 
575        }
576  
577        LocalFree(lpqslsBuf); 
578        log_error_message(_("Could not lock database")); 
579        return;
580     } 
581  
582     // The database is locked, so it is safe to make changes. 
583  
584     sdBuf.lpDescription = lpDesc;
585
586     if (!ChangeServiceDescription(
587          hService,                   // handle to service
588          SERVICE_CONFIG_DESCRIPTION, // change: description
589          &sdBuf) ) {                 // value: new description
590        log_error_message("ChangeServiceConfig2");
591     }
592
593     // Release the database lock. 
594     UnlockServiceDatabase(sclLock); 
595 }