2 * NT Service interface Utility.
3 * Based on code written by
4 * Chas Woodfield, Fretwell Downing Informatics.
6 * Revision 1.5 2001-11-13 23:00:42 adam
7 * Separate malloc debug library. Removal of ASN_COMPILED-#ifdefs.
9 * Revision 1.4 2000/12/05 19:05:10 adam
10 * Service automatically starts in the directory from which it was installed.
12 * Revision 1.3 1999/06/10 11:45:30 adam
13 * Added bend_start, bend_stop handlers and removed pre_init.
14 * Handlers bend_start/bend_stop are called when service/daemon is
17 * Revision 1.2 1999/02/02 13:57:36 adam
18 * Uses preprocessor define WIN32 instead of WINDOWS to build code
19 * for Microsoft WIN32.
21 * Revision 1.1 1997/11/07 13:31:52 adam
22 * Added NT Service name part of statserv_options_block. Moved NT
23 * service utility to server library.
25 * Revision 1.6 1997/09/18 08:49:14 adam
26 * Option -runnormal no needed to run server in standalone mode.
28 * Revision 1.5 1997/09/17 12:10:43 adam
31 * Revision 1.4 1997/09/09 10:10:20 adam
32 * Another MSV5.0 port. Changed projects to include proper
33 * library/include paths.
34 * Server starts server in test-mode when no options are given.
36 * Revision 1.3 1997/09/04 13:50:30 adam
41 /************************************************************/
42 /* Note this file is shared by all processes */
43 /* Should really put it somewhere other than here */
44 /* For some strange reason it won't work when part of a lib */
45 /************************************************************/
56 static AppService *pService = NULL;
57 static BOOL bRunAsService = TRUE;
58 static void *pAppHandle = NULL;
60 /* Private functions to this module */
61 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv);
62 void Service_Delete();
63 void Service_Initialize();
64 BOOL NotifyServiceController();
65 BOOL UpdateServiceStatus(DWORD Status);
66 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode);
67 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart);
68 void CmdRemoveService();
69 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize);
70 BOOL CheckServiceArguments(int argc, char *argv[]);
72 /* Callback functions for thee service manager */
73 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[]);
74 void WINAPI ServiceControlHandler(DWORD fdwControl);
76 /* Function to handle Ctrl + C etc... */
77 BOOL EventHandlerRoutine(DWORD dwCtrlType);
79 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv)
81 pService = malloc(sizeof(AppService));
82 pService->pAppName = pAppName;
83 pService->pServiceName = pServiceName;
84 pService->pServiceDisplayName = pServiceDisplayName;
85 pService->pDependancies = pDependancies;
86 pService->hService = 0;
87 pService->ServiceTable[0].lpServiceName = pServiceName;
88 pService->ServiceTable[0].lpServiceProc = ServiceMain;
89 pService->ServiceTable[1].lpServiceName = NULL;
90 pService->ServiceTable[1].lpServiceProc = NULL;
91 pService->argc = argc;
92 pService->argv = argv;
99 /* Mark the service as stopping */
100 UpdateServiceStatus(SERVICE_STOP_PENDING);
102 /* Stop the service */
103 StopAppService(pAppHandle);
105 /* Service has now stopped */
106 UpdateServiceStatus(SERVICE_STOPPED);
108 /* Free the memory */
114 void Service_Initialize()
116 if (pService != NULL)
118 /* Register ourselves with the control dispatcher */
119 StartServiceCtrlDispatcher(pService->ServiceTable);
123 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[])
125 if (pService != NULL)
127 if (NotifyServiceController())
129 /* Set the status to pending */
130 UpdateServiceStatus(SERVICE_START_PENDING);
132 /* Lets attempt to start the service */
133 if (StartAppService(pAppHandle, pService->argc, pService->argv))
135 /* Service is now up and running */
136 UpdateServiceStatus(SERVICE_RUNNING);
138 /* Lets wait for our clients */
139 RunAppService(pAppHandle);
143 FailServiceStart(GetLastError(), 0);
150 BOOL NotifyServiceController()
152 if (pService == NULL)
160 pService->ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
161 pService->ServiceStatus.dwCurrentState = SERVICE_STOPPED;
162 pService->ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
163 pService->ServiceStatus.dwWin32ExitCode = 0;
164 pService->ServiceStatus.dwServiceSpecificExitCode = 0;
165 pService->ServiceStatus.dwCheckPoint = 0;
166 pService->ServiceStatus.dwWaitHint = 0;
167 pService->hService = RegisterServiceCtrlHandler(pService->pServiceName, ServiceControlHandler);
169 if (pService->hService)
170 UpdateServiceStatus(SERVICE_START_PENDING);
178 void WINAPI ServiceControlHandler(DWORD fdwControl)
180 if (pService != NULL)
184 case SERVICE_CONTROL_STOP:
185 /* Update the service status to be pending */
189 case SERVICE_CONTROL_INTERROGATE:
190 UpdateServiceStatus(pService->ServiceStatus.dwCurrentState);
199 BOOL UpdateServiceStatus(DWORD Status)
201 if (pService != NULL)
203 if (pService->hService)
205 pService->ServiceStatus.dwCurrentState = Status;
206 if ((Status == SERVICE_START_PENDING) || (Status == SERVICE_STOP_PENDING))
208 pService->ServiceStatus.dwCheckPoint ++;
209 pService->ServiceStatus.dwWaitHint = 5000; /* 5 sec.*/
213 pService->ServiceStatus.dwCheckPoint = 0;
214 pService->ServiceStatus.dwWaitHint = 0;
217 return(SetServiceStatus(pService->hService, &pService->ServiceStatus));
224 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode)
226 if (pService != NULL)
228 pService->ServiceStatus.dwWin32ExitCode = Win32Code;
229 pService->ServiceStatus.dwServiceSpecificExitCode = PrivateCode;
230 UpdateServiceStatus(SERVICE_STOPPED);
234 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart)
236 if (pService != NULL)
238 SC_HANDLE schService;
239 SC_HANDLE schSCManager;
243 if (GetModuleFileName(NULL, szPath, 512) == 0)
245 _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(pService->pServiceDisplayName), GetLastErrorText(pService->szErr, 256));
250 char cwdstr[_MAX_PATH];
252 if (!_getcwd(cwdstr, sizeof(cwdstr)))
253 strcpy (cwdstr, ".");
255 strcat (szPath, TEXT(" -runservice \""));
256 strcat (szPath, cwdstr);
257 strcat (szPath, "\"");
259 for (i = 1; i < argc; i++)
261 /* We will add the given command line arguments to the command */
262 /* We are not interested in the install and remove options */
263 if ((stricmp("-install", argv[i]) != 0) &&
264 (stricmp("-installa", argv[i]) != 0) &&
265 (stricmp("-remove", argv[i]) != 0))
267 strcat(szPath, TEXT(" "));
268 strcat(szPath, argv[i]);
272 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
273 NULL, /* database (NULL == default) */
274 SC_MANAGER_ALL_ACCESS); /* access required */
277 schService = CreateService(schSCManager, /* SCManager database */
278 TEXT(pService->pServiceName), /* name of service */
279 TEXT(pService->pServiceDisplayName), /* name to display */
280 SERVICE_ALL_ACCESS, /* desired access */
281 SERVICE_WIN32_OWN_PROCESS, /* service type */
282 bAutoStart ? SERVICE_AUTO_START :
283 SERVICE_DEMAND_START, /* start type */
284 SERVICE_ERROR_NORMAL, /* error control type */
285 szPath, /* service's binary */
286 NULL, /* no load ordering group */
287 NULL, /* no tag identifier */
288 TEXT(pService->pDependancies), /* dependencies */
289 NULL, /* LocalSystem account */
290 NULL); /* no password */
294 _tprintf(TEXT("%s installed.\n"), TEXT(pService->pServiceDisplayName));
295 CloseServiceHandle(schService);
299 _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(pService->szErr, 256));
302 CloseServiceHandle(schSCManager);
305 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
310 void CmdRemoveService()
312 if (pService != NULL)
314 SC_HANDLE schService;
315 SC_HANDLE schSCManager;
317 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
318 NULL, /* database (NULL == default) */
319 SC_MANAGER_ALL_ACCESS); /* access required */
322 schService = OpenService(schSCManager, TEXT(pService->pServiceName), SERVICE_ALL_ACCESS);
326 /* try to stop the service */
327 if (ControlService(schService, SERVICE_CONTROL_STOP, &pService->ServiceStatus))
329 _tprintf(TEXT("Stopping %s."), TEXT(pService->pServiceDisplayName));
332 while (QueryServiceStatus(schService, &pService->ServiceStatus))
334 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING)
343 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOPPED)
344 _tprintf(TEXT("\n%s stopped.\n"), TEXT(pService->pServiceDisplayName));
346 _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(pService->pServiceDisplayName));
350 /* now remove the service */
351 if(DeleteService(schService))
352 _tprintf(TEXT("%s removed.\n"), TEXT(pService->pServiceDisplayName));
354 _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(pService->szErr,256));
356 CloseServiceHandle(schService);
359 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(pService->szErr,256));
361 CloseServiceHandle(schSCManager);
364 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
368 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize)
371 LPTSTR lpszTemp = NULL;
373 dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
381 /* supplied buffer is not long enough */
382 if (!dwRet || ((long)dwSize < (long)dwRet + 14))
383 lpszBuf[0] = TEXT('\0');
386 lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); /* remove cr and newline character */
387 _stprintf(lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError());
391 LocalFree((HLOCAL)lpszTemp);
396 BOOL CheckServiceArguments(int argc, char *argv[])
400 /* Lets process the arguments */
401 for (i = 1; i < argc; i++)
403 if (stricmp("-install", argv[i]) == 0)
405 /* They want to install the service */
406 CmdInstallService(argc, argv, FALSE);
408 /* We don't carry on, after we have installed the service */
411 else if (stricmp("-installa", argv[i]) == 0)
413 /* They want to install the service */
414 CmdInstallService(argc, argv, TRUE);
416 /* We don't carry on, after we have installed the service */
419 else if (stricmp("-remove", argv[i]) == 0)
421 /* Here they want to remove it */
424 /* We don't carry on, after we have removed the service */
427 else if (stricmp ("-runservice", argv[i]) == 0)
429 /* We can carry on, if we reached here */
436 bRunAsService = FALSE;
440 BOOL SetupService(int argc, char *argv[], void *pHandle, LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies)
442 BOOL bDeleteService = TRUE;
443 BOOL bResult = FALSE;
445 /* Save the handle for later use */
446 pAppHandle = pHandle;
448 /* Create our service class */
449 Service_Create(pAppName, pServiceName, pServiceDisplayName, pDependancies, argc, argv);
451 if (CheckServiceArguments(argc, argv))
455 /* No need to set the console control handler, as the service manager handles all this for us */
456 Service_Initialize();
457 bDeleteService = FALSE;
461 /* Set the console control handler for exiting the program */
462 SetConsoleCtrlHandler((PHANDLER_ROUTINE)EventHandlerRoutine, TRUE);
464 /* Now do the main work */
465 ServiceMain(argc, argv);
468 /* We have been successful initializing, so let the caller know */
474 /* Finished with the service now */
480 BOOL EventHandlerRoutine(DWORD dwCtrlType)
482 /* This routine dosn't seem to get called all the time, Why ??? */
485 case CTRL_C_EVENT: /* A CTRL+C signal was received, either from keyboard input or from a signal generated by the GenerateConsoleCtrlEvent function.*/
486 case CTRL_BREAK_EVENT: /* A CTRL+BREAK signal was received, either from keyboard input or from a signal generated by GenerateConsoleCtrlEvent.*/
487 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).*/
488 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.*/
489 case CTRL_SHUTDOWN_EVENT: /* A signal that the system sends to all console processes when the system */
490 /* We are basically shutting down, so call Service_Delete */
496 /* we are not handling this one, so return FALSE */