]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/dird/winservice.cpp
1b71b617b55c38470b74f060a2e53ef080f76298
[bacula/bacula] / bacula / src / win32 / dird / winservice.cpp
1 //  Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
2 //
3 //  This file was 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 (C) 2000-2006 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 "bacula.h"
41 #include "winbacula.h"
42 #include "winservice.h"
43
44 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
45                              LPSTR lpDesc);
46
47 // OS-SPECIFIC ROUTINES
48
49 // Create an instance of the bacService class to cause the static fields to be
50 // initialised properly
51
52 bacService init;
53
54 extern bool    opt_debug;
55
56 // SERVICE-MODE ROUTINES
57
58 // Service-mode defines:
59
60 // Internal service name
61 #define BAC_SERVICENAME        "Bacula-dir"
62
63 // Displayed service name
64 #define BAC_SERVICEDISPLAYNAME "Bacula Director"
65
66 // List other required services 
67 #define BAC_DEPENDENCIES __TEXT("tcpip\0afd\0") 
68
69
70 // Internal service state
71 SERVICE_STATUS          g_srvstatus;       // current status of the service
72 SERVICE_STATUS_HANDLE   g_hstatus;
73 DWORD                   g_error = 0;
74 DWORD                   g_servicethread = 0;
75 char*                   g_errortext[256];
76
77
78 // Forward defines of internal service functions
79 void WINAPI ServiceMain(DWORD argc, char **argv);
80 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
81 void ServiceStop();
82 void WINAPI ServiceCtrl(DWORD ctrlcode);
83 bool WINAPI CtrlHandler (DWORD ctrltype);
84 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
85
86 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
87
88 BOOL    g_servicemode = FALSE;
89
90 BOOL
91 bacService::RunningAsService()
92 {
93    return g_servicemode;
94 }
95
96 BOOL
97 bacService::KillRunningCopy()
98 {
99 //  while (PostToBacula(WM_CLOSE, 0, 0))
100 //      {  }
101   return TRUE;
102 }
103
104 // SERVICE MAIN ROUTINE
105 int
106 bacService::BaculaServiceMain()
107 {
108    // Mark that we are a service
109    g_servicemode = TRUE;
110
111    // Create a service entry table
112    SERVICE_TABLE_ENTRY dispatchTable[] = {
113       {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
114       {NULL, NULL}
115    };
116
117    // Call the service control dispatcher with our entry table
118    if (!StartServiceCtrlDispatcher(dispatchTable)) {
119       log_error_message(_("StartServiceCtrlDispatcher failed."));
120    }
121
122    return 0;
123 }
124
125 // SERVICE MAIN ROUTINE - NT ONLY !!!
126 // NT/Win2K/WinXP ONLY !!!
127 void WINAPI ServiceMain(DWORD argc, char **argv)
128 {
129     DWORD dwThreadID;
130
131     // Register the service control handler
132     g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
133
134     if (g_hstatus == 0) {
135        log_error_message(_("RegisterServiceCtlHandler failed")); 
136        MessageBox(NULL, _("Contact Register Service Handler failure"),
137           "Bacula service", MB_OK);
138        return;
139     }
140
141      // Set up some standard service state values
142     g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
143     g_srvstatus.dwServiceSpecificExitCode = 0;
144
145         // Give this status to the SCM
146     if (!ReportStatus(
147             SERVICE_START_PENDING,          // Service state
148             NO_ERROR,                       // Exit code type
149             45000)) {                       // Hint as to how long Bacula should have hung before you assume error
150
151         ReportStatus(SERVICE_STOPPED, g_error,  0);
152         log_error_message(_("ReportStatus STOPPED failed 1")); 
153         return;
154     }
155
156         // Now start the service for real
157     (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
158     return;
159 }
160
161 // SERVICE START ROUTINE - thread that calls BaculaAppMain
162 //   NT ONLY !!!!
163 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
164 {
165
166     // Save the current thread identifier
167     g_servicethread = GetCurrentThreadId();
168
169     // report the status to the service control manager.
170     //
171     if (!ReportStatus(
172           SERVICE_RUNNING,       // service state
173           NO_ERROR,              // exit code
174           0)) {                  // wait hint
175        MessageBox(NULL, _("Report Service failure"), "Bacula Service", MB_OK);
176        log_error_message("ReportStatus RUNNING failed"); 
177        return 0;
178     }
179
180     /* Call Bacula main code */
181     BaculaAppMain();
182
183     /* Mark that we're no longer running */
184     g_servicethread = 0;
185
186     /* Tell the service manager that we've stopped */
187     ReportStatus(SERVICE_STOPPED, g_error, 0);
188     return 0;
189 }
190
191
192 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
193 void ServiceStop()
194 {
195    // Post a quit message to the main service thread
196    if (g_servicethread != 0) {
197       PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
198    }
199 }
200
201 // SERVICE INSTALL ROUTINE
202 int
203 bacService::InstallService(const char *pszCmdLine)
204 {
205    const int pathlength = 2048;
206    char path[pathlength];
207    char servicecmd[pathlength];
208
209    // Get the filename of this executable
210    if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
211       MessageBox(NULL, _("Unable to install Bacula Director service"), szAppName, MB_ICONEXCLAMATION | MB_OK);
212       return 0;
213    }
214
215    // Append the service-start flag to the end of the path:
216    if ((int)strlen(path) + 5 + (int)strlen(BaculaRunService) + (int)strlen(pszCmdLine) < pathlength) {
217       sprintf(servicecmd, "\"%s\" %s %s", path, BaculaRunService, pszCmdLine);
218    } else {
219       log_error_message(_("Service command length too long")); 
220       MessageBox(NULL, _("Service command length too long. Service not registered."),
221           szAppName, MB_ICONEXCLAMATION | MB_OK);
222       return 0;
223    }
224
225    SC_HANDLE   hservice;
226    SC_HANDLE   hsrvmanager;
227
228    // Open the default, local Service Control Manager database
229    hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
230    if (hsrvmanager == NULL) {
231       log_error_message("OpenSCManager failed"); 
232       MessageBox(NULL,
233          _("The Service Control Manager could not be contacted - the Bacula Director service was not installed"),
234          szAppName, MB_ICONEXCLAMATION | MB_OK);
235       return 0;
236    }
237
238    // Create an entry for the Bacula service
239    hservice = CreateService(
240            hsrvmanager,                    // SCManager database
241            BAC_SERVICENAME,                // name of service
242            BAC_SERVICEDISPLAYNAME,         // name to display
243            SERVICE_ALL_ACCESS,             // desired access
244            SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
245                                                                    // service type
246            SERVICE_AUTO_START,             // start type
247            SERVICE_ERROR_NORMAL,           // error control type
248            servicecmd,                     // service's binary
249            NULL,                           // no load ordering group
250            NULL,                           // no tag identifier
251            BAC_DEPENDENCIES,               // dependencies
252            NULL,                           // LocalSystem account
253            NULL);                          // no password
254    if (hservice == NULL) {
255       CloseServiceHandle(hsrvmanager);
256       log_error_message("CreateService failed"); 
257       MessageBox(NULL,
258           _("The Bacula Director service could not be installed"),
259            szAppName, MB_ICONEXCLAMATION | MB_OK);
260       return 0;
261    }
262
263    set_service_description(hsrvmanager,hservice, 
264 _("Provides director services. Bacula -- the network backup solution."));
265
266    CloseServiceHandle(hsrvmanager);
267    CloseServiceHandle(hservice);
268
269    // Everything went fine
270    if (opt_debug) {
271       MessageBox(NULL,
272               _("The Bacula Director service was successfully installed.\n"
273               "The service may be started from the Control Panel and will\n"
274               "automatically be run the next time this machine is rebooted."),
275               szAppName,
276               MB_ICONINFORMATION | MB_OK);
277    }
278    return 0;
279 }
280
281
282 // SERVICE REMOVE ROUTINE
283 int
284 bacService::RemoveService()
285 {
286    SC_HANDLE   hservice;
287    SC_HANDLE   hsrvmanager;
288
289    // Open the SCM
290    hsrvmanager = OpenSCManager(
291       NULL,                   // machine (NULL == local)
292       NULL,                   // database (NULL == default)
293       SC_MANAGER_ALL_ACCESS   // access required
294       );
295    if (hsrvmanager) {
296       hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
297       if (hservice != NULL) {
298          SERVICE_STATUS status;
299
300          // Try to stop the Bacula service
301          if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
302             while(QueryServiceStatus(hservice, &status)) {
303                if (status.dwCurrentState == SERVICE_STOP_PENDING) {
304                   Sleep(1000);
305                } else {
306                   break;
307                }
308             }
309
310             if (status.dwCurrentState != SERVICE_STOPPED) {
311                MessageBox(NULL, _("The Bacula Director service could not be stopped"), szAppName, MB_ICONEXCLAMATION | MB_OK);
312             }
313          }
314
315          // Now remove the service from the SCM
316          if(DeleteService(hservice)) {
317             if (opt_debug) {
318                MessageBox(NULL, _("The Bacula Director service has been removed"), szAppName, MB_ICONINFORMATION | MB_OK);
319             }
320          } else {
321             MessageBox(NULL, _("The Bacula Director service could not be removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
322          }
323
324          CloseServiceHandle(hservice);
325       } else {
326          MessageBox(NULL, _("The Bacula Director service could not be found"), szAppName, MB_ICONEXCLAMATION | MB_OK);
327       }
328
329       CloseServiceHandle(hsrvmanager);
330    } else {
331       MessageBox(NULL, _("The SCM could not be contacted - the Bacula Director service was not removed"), szAppName, MB_ICONEXCLAMATION | MB_OK);
332    }
333    return 0;
334 }
335
336 // USEFUL SERVICE SUPPORT ROUTINES
337
338 // Service control routine
339 void WINAPI ServiceCtrl(DWORD ctrlcode)
340 {
341     // What control code have we been sent?
342     switch(ctrlcode) {
343     case SERVICE_CONTROL_STOP:
344         // STOP : The service must stop
345         g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
346         ServiceStop();
347         break;
348
349     case SERVICE_CONTROL_INTERROGATE:
350         // QUERY : Service control manager just wants to know our state
351         break;
352
353      default:
354         // Control code not recognised
355         break;
356     }
357
358     // Tell the control manager what we're up to.
359     ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
360 }
361
362 // Service manager status reporting
363 BOOL ReportStatus(DWORD state,
364                   DWORD exitcode,
365                   DWORD waithint)
366 {
367     static DWORD checkpoint = 1;
368     BOOL result = TRUE;
369
370     // If we're in the start state then we don't want the control manager
371     // sending us control messages because they'll confuse us.
372     if (state == SERVICE_START_PENDING) {
373        g_srvstatus.dwControlsAccepted = 0;
374     } else {
375        g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
376     }
377
378     // Save the new status we've been given
379     g_srvstatus.dwCurrentState = state;
380     g_srvstatus.dwWin32ExitCode = exitcode;
381     g_srvstatus.dwWaitHint = waithint;
382
383     // Update the checkpoint variable to let the SCM know that we
384     // haven't died if requests take a long time
385     if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
386        g_srvstatus.dwCheckPoint = 0;
387     } else {
388        g_srvstatus.dwCheckPoint = checkpoint++;
389     }
390
391     // Tell the SCM our new status
392     if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
393        log_error_message(_("SetServiceStatus failed"));
394     }
395
396     return result;
397 }
398
399 // Error reporting
400 void LogErrorMsg(char *message, char *fname, int lineno)
401 {
402    char        msgbuff[256];
403    HANDLE      heventsrc;
404    char *      strings[32];
405    LPTSTR      msg;
406
407    // Get the error code
408    g_error = GetLastError();
409    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
410                  FORMAT_MESSAGE_FROM_SYSTEM,
411                  NULL,
412                  g_error,
413                  0,
414                  (LPTSTR)&msg,
415                  0,
416                  NULL);
417
418    // Use event logging to log the error
419    heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
420
421    sprintf(msgbuff, _("\n\n%s error: %ld at %s:%d"), 
422       BAC_SERVICENAME, g_error, fname, lineno);
423    strings[0] = msgbuff;
424    strings[1] = message;
425    strings[2] = msg;
426
427    if (heventsrc != NULL) {
428       MessageBeep(MB_OK);
429
430       ReportEvent(
431               heventsrc,              // handle of event source
432               EVENTLOG_ERROR_TYPE,    // event type
433               0,                      // event category
434               0,                      // event ID
435               NULL,                   // current user's SID
436               3,                      // strings in 'strings'
437               0,                      // no bytes of raw data
438               (const char **)strings, // array of error strings
439               NULL);                  // no raw data
440
441       DeregisterEventSource(heventsrc);
442    }
443    LocalFree(msg);
444 }
445 typedef BOOL  (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
446
447 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
448                              LPSTR lpDesc) 
449
450     SC_LOCK sclLock; 
451     LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf; 
452     SERVICE_DESCRIPTION sdBuf;
453     DWORD dwBytesNeeded;
454     WinAPI ChangeServiceDescription;
455  
456     HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
457     if (!hLib) {
458        return;
459     }
460     ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
461        "ChangeServiceConfig2A");
462     FreeLibrary(hLib);
463     if (!ChangeServiceDescription) {
464        return;
465     }
466     
467     // Need to acquire database lock before reconfiguring. 
468     sclLock = LockServiceDatabase(hSCManager); 
469  
470     // If the database cannot be locked, report the details. 
471     if (sclLock == NULL) {
472        // Exit if the database is not locked by another process. 
473        if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
474           log_error_message("LockServiceDatabase"); 
475           return;
476        }
477  
478        // Allocate a buffer to get details about the lock. 
479        lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc( 
480             LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256); 
481        if (lpqslsBuf == NULL) {
482           log_error_message("LocalAlloc"); 
483           return;
484        }
485  
486        // Get and print the lock status information. 
487        if (!QueryServiceLockStatus( 
488               hSCManager, 
489               lpqslsBuf, 
490               sizeof(QUERY_SERVICE_LOCK_STATUS)+256, 
491               &dwBytesNeeded)) {
492           log_error_message("QueryServiceLockStatus"); 
493        }
494  
495        if (lpqslsBuf->fIsLocked) {
496           printf(_("Locked by: %s, duration: %ld seconds\n"), 
497                 lpqslsBuf->lpLockOwner, 
498                 lpqslsBuf->dwLockDuration); 
499        } else {
500           printf(_("No longer locked\n")); 
501        }
502  
503        LocalFree(lpqslsBuf); 
504        log_error_message(_("Could not lock database")); 
505        return;
506     } 
507  
508     // The database is locked, so it is safe to make changes. 
509  
510     sdBuf.lpDescription = lpDesc;
511
512     if (!ChangeServiceDescription(
513          hService,                   // handle to service
514          SERVICE_CONFIG_DESCRIPTION, // change: description
515          &sdBuf) ) {                 // value: new description
516        log_error_message("ChangeServiceConfig2");
517     }
518
519     // Release the database lock. 
520     UnlockServiceDatabase(sclLock); 
521 }