2 * NT Service interface Utility.
3 * Based on code written by
4 * Chas Woodfield, Fretwell Downing Informatics.
6 * Revision 1.1 2003-10-27 12:21:35 adam
7 * Source restructure. yaz-marcdump part of installation
9 * Revision 1.5 2001/11/13 23:00:42 adam
10 * Separate malloc debug library. Removal of ASN_COMPILED-#ifdefs.
12 * Revision 1.4 2000/12/05 19:05:10 adam
13 * Service automatically starts in the directory from which it was installed.
15 * Revision 1.3 1999/06/10 11:45:30 adam
16 * Added bend_start, bend_stop handlers and removed pre_init.
17 * Handlers bend_start/bend_stop are called when service/daemon is
20 * Revision 1.2 1999/02/02 13:57:36 adam
21 * Uses preprocessor define WIN32 instead of WINDOWS to build code
22 * for Microsoft WIN32.
24 * Revision 1.1 1997/11/07 13:31:52 adam
25 * Added NT Service name part of statserv_options_block. Moved NT
26 * service utility to server library.
28 * Revision 1.6 1997/09/18 08:49:14 adam
29 * Option -runnormal no needed to run server in standalone mode.
31 * Revision 1.5 1997/09/17 12:10:43 adam
34 * Revision 1.4 1997/09/09 10:10:20 adam
35 * Another MSV5.0 port. Changed projects to include proper
36 * library/include paths.
37 * Server starts server in test-mode when no options are given.
39 * Revision 1.3 1997/09/04 13:50:30 adam
44 /************************************************************/
45 /* Note this file is shared by all processes */
46 /* Should really put it somewhere other than here */
47 /* For some strange reason it won't work when part of a lib */
48 /************************************************************/
59 static AppService *pService = NULL;
60 static BOOL bRunAsService = TRUE;
61 static void *pAppHandle = NULL;
63 /* Private functions to this module */
64 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv);
65 void Service_Delete();
66 void Service_Initialize();
67 BOOL NotifyServiceController();
68 BOOL UpdateServiceStatus(DWORD Status);
69 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode);
70 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart);
71 void CmdRemoveService();
72 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize);
73 BOOL CheckServiceArguments(int argc, char *argv[]);
75 /* Callback functions for thee service manager */
76 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[]);
77 void WINAPI ServiceControlHandler(DWORD fdwControl);
79 /* Function to handle Ctrl + C etc... */
80 BOOL EventHandlerRoutine(DWORD dwCtrlType);
82 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv)
84 pService = malloc(sizeof(AppService));
85 pService->pAppName = pAppName;
86 pService->pServiceName = pServiceName;
87 pService->pServiceDisplayName = pServiceDisplayName;
88 pService->pDependancies = pDependancies;
89 pService->hService = 0;
90 pService->ServiceTable[0].lpServiceName = pServiceName;
91 pService->ServiceTable[0].lpServiceProc = ServiceMain;
92 pService->ServiceTable[1].lpServiceName = NULL;
93 pService->ServiceTable[1].lpServiceProc = NULL;
94 pService->argc = argc;
95 pService->argv = argv;
100 if (pService != NULL)
102 /* Mark the service as stopping */
103 UpdateServiceStatus(SERVICE_STOP_PENDING);
105 /* Stop the service */
106 StopAppService(pAppHandle);
108 /* Service has now stopped */
109 UpdateServiceStatus(SERVICE_STOPPED);
111 /* Free the memory */
117 void Service_Initialize()
119 if (pService != NULL)
121 /* Register ourselves with the control dispatcher */
122 StartServiceCtrlDispatcher(pService->ServiceTable);
126 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[])
128 if (pService != NULL)
130 if (NotifyServiceController())
132 /* Set the status to pending */
133 UpdateServiceStatus(SERVICE_START_PENDING);
135 /* Lets attempt to start the service */
136 if (StartAppService(pAppHandle, pService->argc, pService->argv))
138 /* Service is now up and running */
139 UpdateServiceStatus(SERVICE_RUNNING);
141 /* Lets wait for our clients */
142 RunAppService(pAppHandle);
146 FailServiceStart(GetLastError(), 0);
153 BOOL NotifyServiceController()
155 if (pService == NULL)
163 pService->ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
164 pService->ServiceStatus.dwCurrentState = SERVICE_STOPPED;
165 pService->ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
166 pService->ServiceStatus.dwWin32ExitCode = 0;
167 pService->ServiceStatus.dwServiceSpecificExitCode = 0;
168 pService->ServiceStatus.dwCheckPoint = 0;
169 pService->ServiceStatus.dwWaitHint = 0;
170 pService->hService = RegisterServiceCtrlHandler(pService->pServiceName, ServiceControlHandler);
172 if (pService->hService)
173 UpdateServiceStatus(SERVICE_START_PENDING);
181 void WINAPI ServiceControlHandler(DWORD fdwControl)
183 if (pService != NULL)
187 case SERVICE_CONTROL_STOP:
188 /* Update the service status to be pending */
192 case SERVICE_CONTROL_INTERROGATE:
193 UpdateServiceStatus(pService->ServiceStatus.dwCurrentState);
202 BOOL UpdateServiceStatus(DWORD Status)
204 if (pService != NULL)
206 if (pService->hService)
208 pService->ServiceStatus.dwCurrentState = Status;
209 if ((Status == SERVICE_START_PENDING) || (Status == SERVICE_STOP_PENDING))
211 pService->ServiceStatus.dwCheckPoint ++;
212 pService->ServiceStatus.dwWaitHint = 5000; /* 5 sec.*/
216 pService->ServiceStatus.dwCheckPoint = 0;
217 pService->ServiceStatus.dwWaitHint = 0;
220 return(SetServiceStatus(pService->hService, &pService->ServiceStatus));
227 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode)
229 if (pService != NULL)
231 pService->ServiceStatus.dwWin32ExitCode = Win32Code;
232 pService->ServiceStatus.dwServiceSpecificExitCode = PrivateCode;
233 UpdateServiceStatus(SERVICE_STOPPED);
237 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart)
239 if (pService != NULL)
241 SC_HANDLE schService;
242 SC_HANDLE schSCManager;
246 if (GetModuleFileName(NULL, szPath, 512) == 0)
248 _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(pService->pServiceDisplayName), GetLastErrorText(pService->szErr, 256));
253 char cwdstr[_MAX_PATH];
255 if (!_getcwd(cwdstr, sizeof(cwdstr)))
256 strcpy (cwdstr, ".");
258 strcat (szPath, TEXT(" -runservice \""));
259 strcat (szPath, cwdstr);
260 strcat (szPath, "\"");
262 for (i = 1; i < argc; i++)
264 /* We will add the given command line arguments to the command */
265 /* We are not interested in the install and remove options */
266 if ((stricmp("-install", argv[i]) != 0) &&
267 (stricmp("-installa", argv[i]) != 0) &&
268 (stricmp("-remove", argv[i]) != 0))
270 strcat(szPath, TEXT(" "));
271 strcat(szPath, argv[i]);
275 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
276 NULL, /* database (NULL == default) */
277 SC_MANAGER_ALL_ACCESS); /* access required */
280 schService = CreateService(schSCManager, /* SCManager database */
281 TEXT(pService->pServiceName), /* name of service */
282 TEXT(pService->pServiceDisplayName), /* name to display */
283 SERVICE_ALL_ACCESS, /* desired access */
284 SERVICE_WIN32_OWN_PROCESS, /* service type */
285 bAutoStart ? SERVICE_AUTO_START :
286 SERVICE_DEMAND_START, /* start type */
287 SERVICE_ERROR_NORMAL, /* error control type */
288 szPath, /* service's binary */
289 NULL, /* no load ordering group */
290 NULL, /* no tag identifier */
291 TEXT(pService->pDependancies), /* dependencies */
292 NULL, /* LocalSystem account */
293 NULL); /* no password */
297 _tprintf(TEXT("%s installed.\n"), TEXT(pService->pServiceDisplayName));
298 CloseServiceHandle(schService);
302 _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(pService->szErr, 256));
305 CloseServiceHandle(schSCManager);
308 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
313 void CmdRemoveService()
315 if (pService != NULL)
317 SC_HANDLE schService;
318 SC_HANDLE schSCManager;
320 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
321 NULL, /* database (NULL == default) */
322 SC_MANAGER_ALL_ACCESS); /* access required */
325 schService = OpenService(schSCManager, TEXT(pService->pServiceName), SERVICE_ALL_ACCESS);
329 /* try to stop the service */
330 if (ControlService(schService, SERVICE_CONTROL_STOP, &pService->ServiceStatus))
332 _tprintf(TEXT("Stopping %s."), TEXT(pService->pServiceDisplayName));
335 while (QueryServiceStatus(schService, &pService->ServiceStatus))
337 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING)
346 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOPPED)
347 _tprintf(TEXT("\n%s stopped.\n"), TEXT(pService->pServiceDisplayName));
349 _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(pService->pServiceDisplayName));
353 /* now remove the service */
354 if(DeleteService(schService))
355 _tprintf(TEXT("%s removed.\n"), TEXT(pService->pServiceDisplayName));
357 _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(pService->szErr,256));
359 CloseServiceHandle(schService);
362 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(pService->szErr,256));
364 CloseServiceHandle(schSCManager);
367 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
371 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize)
374 LPTSTR lpszTemp = NULL;
376 dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
384 /* supplied buffer is not long enough */
385 if (!dwRet || ((long)dwSize < (long)dwRet + 14))
386 lpszBuf[0] = TEXT('\0');
389 lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); /* remove cr and newline character */
390 _stprintf(lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError());
394 LocalFree((HLOCAL)lpszTemp);
399 BOOL CheckServiceArguments(int argc, char *argv[])
403 /* Lets process the arguments */
404 for (i = 1; i < argc; i++)
406 if (stricmp("-install", argv[i]) == 0)
408 /* They want to install the service */
409 CmdInstallService(argc, argv, FALSE);
411 /* We don't carry on, after we have installed the service */
414 else if (stricmp("-installa", argv[i]) == 0)
416 /* They want to install the service */
417 CmdInstallService(argc, argv, TRUE);
419 /* We don't carry on, after we have installed the service */
422 else if (stricmp("-remove", argv[i]) == 0)
424 /* Here they want to remove it */
427 /* We don't carry on, after we have removed the service */
430 else if (stricmp ("-runservice", argv[i]) == 0)
432 /* We can carry on, if we reached here */
439 bRunAsService = FALSE;
443 BOOL SetupService(int argc, char *argv[], void *pHandle, LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies)
445 BOOL bDeleteService = TRUE;
446 BOOL bResult = FALSE;
448 /* Save the handle for later use */
449 pAppHandle = pHandle;
451 /* Create our service class */
452 Service_Create(pAppName, pServiceName, pServiceDisplayName, pDependancies, argc, argv);
454 if (CheckServiceArguments(argc, argv))
458 /* No need to set the console control handler, as the service manager handles all this for us */
459 Service_Initialize();
460 bDeleteService = FALSE;
464 /* Set the console control handler for exiting the program */
465 SetConsoleCtrlHandler((PHANDLER_ROUTINE)EventHandlerRoutine, TRUE);
467 /* Now do the main work */
468 ServiceMain(argc, argv);
471 /* We have been successful initializing, so let the caller know */
477 /* Finished with the service now */
483 BOOL EventHandlerRoutine(DWORD dwCtrlType)
485 /* This routine dosn't seem to get called all the time, Why ??? */
488 case CTRL_C_EVENT: /* A CTRL+C signal was received, either from keyboard input or from a signal generated by the GenerateConsoleCtrlEvent function.*/
489 case CTRL_BREAK_EVENT: /* A CTRL+BREAK signal was received, either from keyboard input or from a signal generated by GenerateConsoleCtrlEvent.*/
490 case CTRL_CLOSE_EVENT: /* A signal that the system sends to all processes attached to a console when the user closes the console (either by choosing the Close command from the console window's System menu, or by choosing the End Task command from the Task List).*/
491 case CTRL_LOGOFF_EVENT: /* A signal that the system sends to all console processes when a user is logging off. This signal does not indicate which user is logging off, so no assumptions can be made.*/
492 case CTRL_SHUTDOWN_EVENT: /* A signal that the system sends to all console processes when the system */
493 /* We are basically shutting down, so call Service_Delete */
499 /* we are not handling this one, so return FALSE */