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