]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/win32/winservice.cpp
Use dcr more in SD + int to bool conversions
[bacula/bacula] / bacula / src / filed / win32 / 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 (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 #undef PASCAL
42 #include "winbacula.h"
43 #include "winservice.h"
44 #include "wintray.h"
45                        
46
47 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
48                              LPSTR lpDesc);
49
50 // OS-SPECIFIC ROUTINES
51
52 // Create an instance of the bacService class to cause the static fields to be
53 // initialised properly
54
55 bacService init;
56
57 DWORD   g_platform_id;
58
59 bacService::bacService()
60 {
61    OSVERSIONINFO osversioninfo;
62    osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
63
64    // Get the current OS version
65    if (!GetVersionEx(&osversioninfo)) {
66       g_platform_id = 0;
67    } else {
68       g_platform_id = osversioninfo.dwPlatformId;
69    }
70 }
71
72
73 // IsWin95 - returns a BOOL indicating whether the current OS is Win95
74 BOOL
75 bacService::IsWin95()
76 {
77    return (g_platform_id == VER_PLATFORM_WIN32_WINDOWS);
78 }
79
80 // IsWinNT - returns a bool indicating whether the current OS is WinNT
81 BOOL
82 bacService::IsWinNT()
83 {
84    return (g_platform_id == VER_PLATFORM_WIN32_NT);
85 }
86
87 // Internal routine to find the  Bacula menu class window and
88 // post a message to it!
89
90 BOOL
91 PostToBacula(UINT message, WPARAM wParam, LPARAM lParam)
92 {
93   // Locate the hidden Bacula menu window
94   HWND hservwnd = FindWindow(MENU_CLASS_NAME, NULL);
95   if (hservwnd == NULL) {
96      return FALSE;
97   }
98
99   // Post the message to Bacula
100   PostMessage(hservwnd, message, wParam, lParam);
101   return TRUE;
102 }
103
104
105 // Static routine to show the Properties dialog for a currently-running
106 // copy of Bacula, (usually a servicified version.)
107
108 BOOL
109 bacService::ShowProperties()
110 {
111    return TRUE;
112 }
113
114 // Static routine to show the Default Properties dialog for a currently-running
115 // copy of Bacula, (usually a servicified version.)
116
117 BOOL
118 bacService::ShowDefaultProperties()
119 {
120    return TRUE;
121 }
122
123 // Static routine to show the About dialog for a currently-running
124 // copy of Bacula, (usually a servicified version.)
125
126 BOOL
127 bacService::ShowAboutBox()
128 {
129   // Post to the Bacula menu window
130   if (!PostToBacula(MENU_ABOUTBOX_SHOW, 0, 0)) {
131      MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
132      return FALSE;
133   }
134   return TRUE;
135 }
136
137 // Static routine to show the Status dialog for a currently-running
138 // copy of Bacula, (usually a servicified version.)
139
140 BOOL
141 bacService::ShowStatus()
142 {
143   // Post to the Bacula menu window
144   if (!PostToBacula(MENU_STATUS_SHOW, 0, 0)) {
145      MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
146      return FALSE;
147   }
148   return TRUE;
149 }
150
151 #ifdef xxx_needed
152 // Static routine to show the Events dialog for a currently-running
153 // copy of Bacula, (usually a servicified version.)
154
155 BOOL
156 bacService::ShowEvents()
157 {
158   // Post to the Bacula menu window
159   if (!PostToBacula(MENU_EVENTS_SHOW, 0, 0)) {
160      MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
161      return FALSE;
162   }
163   return TRUE;
164 }
165
166
167 // Static routine to tell a locally-running instance of the server
168 // to connect out to a new client
169
170 BOOL
171 bacService::PostAddNewClient(unsigned long ipaddress)
172 {
173   // Post to the Bacula menu window
174   if (!PostToBacula(MENU_ADD_CLIENT_MSG, 0, ipaddress)) {
175      MessageBox(NULL, "No existing instance of Bacula could be contacted", szAppName, MB_ICONEXCLAMATION | MB_OK);
176      return FALSE;
177   }
178
179   return TRUE;
180 }
181 #endif
182
183 // SERVICE-MODE ROUTINES
184
185 // Service-mode defines:
186
187 // Executable name
188 #define BAC_APPNAME            "bacula"
189
190 // Internal service name
191 #define BAC_SERVICENAME        "Bacula"
192
193 // Displayed service name
194 #define BAC_SERVICEDISPLAYNAME "Bacula File Server"
195
196 // List other required serves 
197 #define BAC_DEPENDENCIES       ""
198
199
200 // Internal service state
201 SERVICE_STATUS          g_srvstatus;       // current status of the service
202 SERVICE_STATUS_HANDLE   g_hstatus;
203 DWORD                   g_error = 0;
204 DWORD                   g_servicethread = 0;
205 char*                   g_errortext[256];
206
207
208 // Forward defines of internal service functions
209 void WINAPI ServiceMain(DWORD argc, char **argv);
210 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam);
211 void ServiceStop();
212 void WINAPI ServiceCtrl(DWORD ctrlcode);
213 bool WINAPI CtrlHandler (DWORD ctrltype);
214 BOOL ReportStatus(DWORD state, DWORD exitcode, DWORD waithint);
215
216 // ROUTINE TO QUERY WHETHER THIS PROCESS IS RUNNING AS A SERVICE OR NOT
217
218 BOOL    g_servicemode = FALSE;
219
220 BOOL
221 bacService::RunningAsService()
222 {
223    return g_servicemode;
224 }
225
226 BOOL
227 bacService::KillRunningCopy()
228 {
229   while (PostToBacula(WM_CLOSE, 0, 0))
230       {  }
231   return TRUE;
232 }
233
234 #ifdef xxx_needed
235
236 // ROUTINE TO POST THE HANDLE OF THE CURRENT USER TO THE RUNNING Bacula, IN ORDER
237 // THAT IT CAN LOAD THE APPROPRIATE SETTINGS.  THIS IS USED ONLY BY THE SVCHELPER
238 // OPTION, WHEN RUNNING UNDER NT
239 BOOL
240 bacService::PostUserHelperMessage()
241 {
242   // - Check the platform type
243   if (!IsWinNT()) {
244      return TRUE;
245   }
246
247   // - Get the current process ID
248   DWORD processId = GetCurrentProcessId();
249
250   // - Post it to the existing Bacula
251   if (!PostToBacula(MENU_SERVICEHELPER_MSG, 0, (LPARAM)processId)) {
252      return FALSE;
253   }
254
255   // - Wait until it's been used
256   return TRUE;
257 }
258
259 // ROUTINE TO PROCESS AN INCOMING INSTANCE OF THE ABOVE MESSAGE
260 BOOL
261 bacService::ProcessUserHelperMessage(WPARAM wParam, LPARAM lParam) {
262    return TRUE;
263 }
264
265 #endif
266
267 // SERVICE MAIN ROUTINE
268 int
269 bacService::BaculaServiceMain()
270 {
271    // Mark that we are a service
272    g_servicemode = TRUE;
273
274    // How to run as a service depends upon the OS being used
275    switch (g_platform_id) {
276
277    // Windows 95/98/Me
278    case VER_PLATFORM_WIN32_WINDOWS:
279       {
280       // Obtain a handle to the kernel library
281       HINSTANCE kerneldll = LoadLibrary("KERNEL32.DLL");
282       if (kerneldll == NULL) {
283          MessageBox(NULL, "KERNEL32.DLL not found: Bacula service not started", 
284              "Bacula Service", MB_OK);
285          break;
286       }
287
288       // And find the RegisterServiceProcess function
289       DWORD (WINAPI *RegisterService)(DWORD, DWORD);
290       RegisterService = (DWORD (WINAPI *)(DWORD, DWORD))
291               GetProcAddress(kerneldll, "RegisterServiceProcess");
292       if (RegisterService == NULL) {
293          MessageBox(NULL, "Registry service not found: Bacula service not started",
294             "Bacula Service", MB_OK);
295          log_error_message("Registry service not found"); 
296          break;
297       }
298       
299       // Register this process with the OS as a service!
300       RegisterService(0, 1);
301
302       // Run the main program as a service
303       BaculaAppMain();
304
305       // Then remove the service from the system service table
306       RegisterService(0, 0);
307
308       // Free the kernel library
309       FreeLibrary(kerneldll);
310       break;
311       }
312
313
314    // Windows NT, Win2K, WinXP 
315    case VER_PLATFORM_WIN32_NT:
316       {
317       // Create a service entry table
318       SERVICE_TABLE_ENTRY dispatchTable[] = {
319          {BAC_SERVICENAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
320          {NULL, NULL}
321       };
322
323       // Call the service control dispatcher with our entry table
324       if (!StartServiceCtrlDispatcher(dispatchTable)) {
325          log_error_message("StartServiceCtrlDispatcher failed.");
326       }
327       break;
328       } /* end case */
329    } /* end switch */
330    return 0;
331 }
332
333 // SERVICE MAIN ROUTINE - NT ONLY !!!
334 // NT/Win2K/WinXP ONLY !!!
335 void WINAPI ServiceMain(DWORD argc, char **argv)
336 {
337     DWORD dwThreadID;
338
339     // Register the service control handler
340     g_hstatus = RegisterServiceCtrlHandler(BAC_SERVICENAME, ServiceCtrl);
341
342     if (g_hstatus == 0) {
343        log_error_message("RegisterServiceCtlHandler failed"); 
344        MessageBox(NULL, "Contact Register Service Handler failure",
345           "Bacula service", MB_OK);
346        return;
347     }
348
349      // Set up some standard service state values
350     g_srvstatus.dwServiceType = SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS;
351     g_srvstatus.dwServiceSpecificExitCode = 0;
352
353         // Give this status to the SCM
354     if (!ReportStatus(
355             SERVICE_START_PENDING,          // Service state
356             NO_ERROR,                       // Exit code type
357             45000)) {                       // Hint as to how long Bacula should have hung before you assume error
358
359         ReportStatus(SERVICE_STOPPED, g_error,  0);
360         log_error_message("ReportStatus STOPPED failed 1"); 
361         return;
362     }
363
364         // Now start the service for real
365     (void)CreateThread(NULL, 0, ServiceWorkThread, NULL, 0, &dwThreadID);
366     return;
367 }
368
369 // SERVICE START ROUTINE - thread that calls BaculaAppMain
370 //   NT ONLY !!!!
371 DWORD WINAPI ServiceWorkThread(LPVOID lpwThreadParam)
372 {
373
374     // Save the current thread identifier
375     g_servicethread = GetCurrentThreadId();
376
377     // report the status to the service control manager.
378     //
379     if (!ReportStatus(
380           SERVICE_RUNNING,       // service state
381           NO_ERROR,              // exit code
382           0)) {                  // wait hint
383        MessageBox(NULL, "Report Service failure", "Bacula Service", MB_OK);
384        log_error_message("ReportStatus RUNNING failed"); 
385        return 0;
386     }
387
388     /* Call Bacula main code */
389     BaculaAppMain();
390
391     /* Mark that we're no longer running */
392     g_servicethread = 0;
393
394     /* Tell the service manager that we've stopped */
395     ReportStatus(SERVICE_STOPPED, g_error, 0);
396     return 0;
397 }
398
399
400 // SERVICE STOP ROUTINE - post a quit message to the relevant thread
401 void ServiceStop()
402 {
403    // Post a quit message to the main service thread
404    if (g_servicethread != 0) {
405       PostThreadMessage(g_servicethread, WM_QUIT, 0, 0);
406    }
407 }
408
409 // SERVICE INSTALL ROUTINE
410 int
411 bacService::InstallService()
412 {
413    const int pathlength = 2048;
414    char path[pathlength];
415    char servicecmd[pathlength];
416    int len;
417
418    // Get the filename of this executable
419    if (GetModuleFileName(NULL, path, pathlength-(strlen(BaculaRunService)+2)) == 0) {
420       MessageBox(NULL, "Unable to install Bacula service", szAppName, MB_ICONEXCLAMATION | MB_OK);
421       return 0;
422    }
423
424    // Append the service-start flag to the end of the path:
425    if ((int)strlen(path) + 20 + (int)strlen(BaculaRunService) < pathlength) {
426       sprintf(servicecmd, "\"%s\" %s -c \"%s\"", path, BaculaRunService, path);
427       len = strlen(servicecmd) - 1;
428       for ( ; len > 0; len--) {
429          if (servicecmd[len] == '\\') {
430             servicecmd[len] = 0;
431             break;
432          }
433          servicecmd[len] = 0;
434       }
435       strcat(servicecmd, "\\bacula-fd.conf");
436
437    } else {
438       log_error_message("Service command length too long"); 
439       MessageBox(NULL, "Service command length too long. Service not registered.",
440           szAppName, MB_ICONEXCLAMATION | MB_OK);
441       return 0;
442    }
443
444    // How to add the Bacula service depends upon the OS
445    switch (g_platform_id) {
446
447    // Windows 95/98/Me
448    case VER_PLATFORM_WIN32_WINDOWS:
449       // Locate the RunService registry entry
450       HKEY runservices;
451       if (RegCreateKey(HKEY_LOCAL_MACHINE, 
452               "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
453               &runservices) != ERROR_SUCCESS) {
454          log_error_message("Cannot write System Registry"); 
455          MessageBox(NULL, "The System Registry could not be updated - the Bacula service was not installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
456          break;
457       }
458
459       // Attempt to add a Bacula key
460       if (RegSetValueEx(runservices, szAppName, 0, REG_SZ, (unsigned char *)servicecmd, strlen(servicecmd)+1) != ERROR_SUCCESS) {
461          RegCloseKey(runservices);
462          log_error_message("Cannot add Bacula key to System Registry"); 
463          MessageBox(NULL, "The Bacula service could not be installed", szAppName, MB_ICONEXCLAMATION | MB_OK);
464          break;
465       }
466
467       RegCloseKey(runservices);
468
469       // We have successfully installed the service!
470       MessageBox(NULL,
471               "The Bacula File service was successfully installed.\n"
472               "The service may be started by double clicking on the\n"
473               "Bacula \"Start\" icon and will be automatically\n"
474               "be run the next time this machine is rebooted. ",
475               szAppName,
476               MB_ICONINFORMATION | MB_OK);
477       break;
478
479    // Windows NT, Win2K, WinXP
480    case VER_PLATFORM_WIN32_NT:
481       SC_HANDLE   hservice;
482       SC_HANDLE   hsrvmanager;
483
484       // Open the default, local Service Control Manager database
485       hsrvmanager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
486       if (hsrvmanager == NULL) {
487          log_error_message("OpenSCManager failed"); 
488          MessageBox(NULL,
489             "The Service Control Manager could not be contacted - the Bacula service was not installed",
490             szAppName, MB_ICONEXCLAMATION | MB_OK);
491          break;
492       }
493
494       // Create an entry for the Bacula service
495       hservice = CreateService(
496               hsrvmanager,                    // SCManager database
497               BAC_SERVICENAME,                // name of service
498               BAC_SERVICEDISPLAYNAME,         // name to display
499               SERVICE_ALL_ACCESS,             // desired access
500               SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
501                                                                       // service type
502               SERVICE_AUTO_START,             // start type
503               SERVICE_ERROR_NORMAL,           // error control type
504               servicecmd,                     // service's binary
505               NULL,                           // no load ordering group
506               NULL,                           // no tag identifier
507               BAC_DEPENDENCIES,               // dependencies
508               NULL,                           // LocalSystem account
509               NULL);                          // no password
510       if (hservice == NULL) {
511          CloseServiceHandle(hsrvmanager);
512          log_error_message("CreateService failed"); 
513          MessageBox(NULL,
514              "The Bacula service could not be installed",
515               szAppName, MB_ICONEXCLAMATION | MB_OK);
516          break;
517       }
518
519       set_service_description(hsrvmanager,hservice, 
520 "Provides file backup and restore services. Bacula -- the network backup solution.");
521
522       CloseServiceHandle(hsrvmanager);
523       CloseServiceHandle(hservice);
524
525       // Now install the servicehelper registry setting...
526       // Locate the RunService registry entry
527       HKEY runapps;
528       if (RegCreateKey(HKEY_LOCAL_MACHINE, 
529               "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
530               &runapps) != ERROR_SUCCESS) {
531          MessageBox(NULL, "WARNING: Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded", 
532             szAppName, MB_ICONEXCLAMATION | MB_OK);
533       } else {
534          char servicehelpercmd[pathlength];
535
536          // Append the service-helper-start flag to the end of the path:
537          if ((int)strlen(path) + 4 + (int)strlen(BaculaRunServiceHelper) < pathlength) {
538             sprintf(servicehelpercmd, "\"%s\" %s", path, BaculaRunServiceHelper);
539
540             // Add the Bacula Service Helper entry
541              if (RegSetValueEx(runapps, szAppName, 0, REG_SZ,
542                  (unsigned char *)servicehelpercmd, strlen(servicehelpercmd)+1) != ERROR_SUCCESS) {
543                 MessageBox(NULL, "WARNING:Unable to install the ServiceHelper hook\nGlobal user-specific registry settings will not be loaded", szAppName, MB_ICONEXCLAMATION | MB_OK);
544              }
545              RegCloseKey(runapps);
546          }
547       }
548
549       // Everything went fine
550       MessageBox(NULL,
551               "The Bacula File service was successfully installed.\n"
552               "The service may be started from the Control Panel and will\n"
553               "automatically be run the next time this machine is rebooted.",
554               szAppName,
555               MB_ICONINFORMATION | MB_OK);
556       break;
557    default:
558       log_error_message("Unknown Windows System version"); 
559       MessageBox(NULL, 
560                  "Unknown Windows operating system.\n"     
561                  "Cannot install Bacula service.\n",
562                  szAppName, MB_ICONEXCLAMATION | MB_OK);
563        break;     
564    };
565
566    return 0;
567 }
568
569
570 // SERVICE REMOVE ROUTINE
571 int
572 bacService::RemoveService()
573 {
574    // How to remove the Bacula service depends upon the OS
575    switch (g_platform_id) {
576
577    // Windows 95/98/Me
578    case VER_PLATFORM_WIN32_WINDOWS:
579       // Locate the RunService registry entry
580       HKEY runservices;
581       if (RegOpenKey(HKEY_LOCAL_MACHINE, 
582               "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
583               &runservices) != ERROR_SUCCESS) {
584          MessageBox(NULL, 
585             "Could not find registry entry.\nService probably not registerd - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
586       } else {
587          // Attempt to delete the Bacula key
588          if (RegDeleteValue(runservices, szAppName) != ERROR_SUCCESS) {
589             RegCloseKey(runservices);
590             MessageBox(NULL, "Could not delete Registry key.\nThe Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
591          }
592
593          RegCloseKey(runservices);
594          break;
595       }
596
597       // Try to kill any running copy of Bacula
598       if (!KillRunningCopy()) {
599          MessageBox(NULL,
600              "Bacula could not be contacted, probably not running",
601              szAppName, MB_ICONEXCLAMATION | MB_OK);
602          break;
603       }
604
605       // We have successfully removed the service!
606       MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
607       break;
608
609    // Windows NT, Win2K, WinXP
610    case VER_PLATFORM_WIN32_NT:
611       SC_HANDLE   hservice;
612       SC_HANDLE   hsrvmanager;
613
614       // Attempt to remove the service-helper hook
615       HKEY runapps;
616       if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
617               &runapps) == ERROR_SUCCESS) {
618          // Attempt to delete the Bacula key
619          if (RegDeleteValue(runapps, szAppName) != ERROR_SUCCESS) {
620             MessageBox(NULL, "WARNING:The ServiceHelper hook entry could not be removed from the registry", szAppName, MB_ICONEXCLAMATION | MB_OK);
621          }
622          RegCloseKey(runapps);
623       }
624
625       // Open the SCM
626       hsrvmanager = OpenSCManager(
627          NULL,                   // machine (NULL == local)
628          NULL,                   // database (NULL == default)
629          SC_MANAGER_ALL_ACCESS   // access required
630          );
631       if (hsrvmanager) {
632          hservice = OpenService(hsrvmanager, BAC_SERVICENAME, SERVICE_ALL_ACCESS);
633          if (hservice != NULL) {
634             SERVICE_STATUS status;
635
636             // Try to stop the Bacula service
637             if (ControlService(hservice, SERVICE_CONTROL_STOP, &status)) {
638                while(QueryServiceStatus(hservice, &status)) {
639                   if (status.dwCurrentState == SERVICE_STOP_PENDING) {
640                      Sleep(1000);
641                   } else {
642                      break;
643                   }
644                }
645
646                if (status.dwCurrentState != SERVICE_STOPPED) {
647                   MessageBox(NULL, "The Bacula service could not be stopped", szAppName, MB_ICONEXCLAMATION | MB_OK);
648                }
649             }
650
651             // Now remove the service from the SCM
652             if(DeleteService(hservice)) {
653                MessageBox(NULL, "The Bacula service has been removed", szAppName, MB_ICONINFORMATION | MB_OK);
654             } else {
655                MessageBox(NULL, "The Bacula service could not be removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
656             }
657
658             CloseServiceHandle(hservice);
659          } else {
660             MessageBox(NULL, "The Bacula service could not be found", szAppName, MB_ICONEXCLAMATION | MB_OK);
661          }
662
663          CloseServiceHandle(hsrvmanager);
664       } else {
665          MessageBox(NULL, "The SCM could not be contacted - the Bacula service was not removed", szAppName, MB_ICONEXCLAMATION | MB_OK);
666       }
667       break;
668    }
669    return 0;
670 }
671
672 // USEFUL SERVICE SUPPORT ROUTINES
673
674 // Service control routine
675 void WINAPI ServiceCtrl(DWORD ctrlcode)
676 {
677     // What control code have we been sent?
678     switch(ctrlcode) {
679     case SERVICE_CONTROL_STOP:
680         // STOP : The service must stop
681         g_srvstatus.dwCurrentState = SERVICE_STOP_PENDING;
682         ServiceStop();
683         break;
684
685     case SERVICE_CONTROL_INTERROGATE:
686         // QUERY : Service control manager just wants to know our state
687         break;
688
689      default:
690         // Control code not recognised
691         break;
692     }
693
694     // Tell the control manager what we're up to.
695     ReportStatus(g_srvstatus.dwCurrentState, NO_ERROR, 0);
696 }
697
698 // Service manager status reporting
699 BOOL ReportStatus(DWORD state,
700                   DWORD exitcode,
701                   DWORD waithint)
702 {
703     static DWORD checkpoint = 1;
704     BOOL result = TRUE;
705
706     // If we're in the start state then we don't want the control manager
707     // sending us control messages because they'll confuse us.
708     if (state == SERVICE_START_PENDING) {
709        g_srvstatus.dwControlsAccepted = 0;
710     } else {
711        g_srvstatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
712     }
713
714     // Save the new status we've been given
715     g_srvstatus.dwCurrentState = state;
716     g_srvstatus.dwWin32ExitCode = exitcode;
717     g_srvstatus.dwWaitHint = waithint;
718
719     // Update the checkpoint variable to let the SCM know that we
720     // haven't died if requests take a long time
721     if ((state == SERVICE_RUNNING) || (state == SERVICE_STOPPED)) {
722        g_srvstatus.dwCheckPoint = 0;
723     } else {
724        g_srvstatus.dwCheckPoint = checkpoint++;
725     }
726
727     // Tell the SCM our new status
728     if (!(result = SetServiceStatus(g_hstatus, &g_srvstatus))) {
729        log_error_message("SetServiceStatus failed");
730     }
731
732     return result;
733 }
734
735 // Error reporting
736 void LogErrorMsg(char *message, char *fname, int lineno)
737 {
738    char        msgbuff[256];
739    HANDLE      heventsrc;
740    char *      strings[32];
741    LPTSTR      msg;
742
743    // Get the error code
744    g_error = GetLastError();
745    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
746                  FORMAT_MESSAGE_FROM_SYSTEM,
747                  NULL,
748                  g_error,
749                  0,
750                  (LPTSTR)&msg,
751                  0,
752                  NULL);
753
754    // Use event logging to log the error
755    heventsrc = RegisterEventSource(NULL, BAC_SERVICENAME);
756
757    sprintf(msgbuff, "\n\n%s error: %ld at %s:%d", 
758       BAC_SERVICENAME, g_error, fname, lineno);
759    strings[0] = msgbuff;
760    strings[1] = message;
761    strings[2] = msg;
762
763    if (heventsrc != NULL) {
764       MessageBeep(MB_OK);
765
766       ReportEvent(
767               heventsrc,              // handle of event source
768               EVENTLOG_ERROR_TYPE,    // event type
769               0,                      // event category
770               0,                      // event ID
771               NULL,                   // current user's SID
772               3,                      // strings in 'strings'
773               0,                      // no bytes of raw data
774               (const char **)strings, // array of error strings
775               NULL);                  // no raw data
776
777       DeregisterEventSource(heventsrc);
778    }
779    LocalFree(msg);
780 }
781 typedef BOOL  (WINAPI * WinAPI)(SC_HANDLE, DWORD, LPVOID);
782
783 void set_service_description(SC_HANDLE hSCManager, SC_HANDLE hService,
784                              LPSTR lpDesc) 
785
786     SC_LOCK sclLock; 
787     LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf; 
788     SERVICE_DESCRIPTION sdBuf;
789     DWORD dwBytesNeeded;
790     WinAPI ChangeServiceDescription;
791  
792     HINSTANCE hLib = LoadLibrary("ADVAPI32.DLL");
793     if (!hLib) {
794        return;
795     }
796     ChangeServiceDescription = (WinAPI)GetProcAddress(hLib,
797        "ChangeServiceConfig2A");
798     FreeLibrary(hLib);
799     if (!ChangeServiceDescription) {
800        return;
801     }
802     
803     // Need to acquire database lock before reconfiguring. 
804     sclLock = LockServiceDatabase(hSCManager); 
805  
806     // If the database cannot be locked, report the details. 
807     if (sclLock == NULL) {
808        // Exit if the database is not locked by another process. 
809        if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) {
810           log_error_message("LockServiceDatabase"); 
811           return;
812        }
813  
814        // Allocate a buffer to get details about the lock. 
815        lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS)LocalAlloc( 
816             LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256); 
817        if (lpqslsBuf == NULL) {
818           log_error_message("LocalAlloc"); 
819           return;
820        }
821  
822        // Get and print the lock status information. 
823        if (!QueryServiceLockStatus( 
824               hSCManager, 
825               lpqslsBuf, 
826               sizeof(QUERY_SERVICE_LOCK_STATUS)+256, 
827               &dwBytesNeeded)) {
828           log_error_message("QueryServiceLockStatus"); 
829        }
830  
831        if (lpqslsBuf->fIsLocked) {
832           printf("Locked by: %s, duration: %ld seconds\n", 
833                 lpqslsBuf->lpLockOwner, 
834                 lpqslsBuf->dwLockDuration); 
835        } else {
836           printf("No longer locked\n"); 
837        }
838  
839        LocalFree(lpqslsBuf); 
840        log_error_message("Could not lock database"); 
841        return;
842     } 
843  
844     // The database is locked, so it is safe to make changes. 
845  
846     sdBuf.lpDescription = lpDesc;
847
848     if(!ChangeServiceDescription(
849          hService,                   // handle to service
850          SERVICE_CONFIG_DESCRIPTION, // change: description
851          &sdBuf) ) {                 // value: new description
852        log_error_message("ChangeServiceConfig2");
853     } else {
854        printf("ChangeServiceConfig2 SUCCESS\n");
855     }
856
857     // Release the database lock. 
858     UnlockServiceDatabase(sclLock); 
859 }