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