2 * NT Service interface Utility.
3 * Based on code written by
4 * Chas Woodfield, Fretwell Downing Datasystems.
6 * Revision 1.3 1999-06-10 11:45:30 adam
7 * Added bend_start, bend_stop handlers and removed pre_init.
8 * Handlers bend_start/bend_stop are called when service/daemon is
11 * Revision 1.2 1999/02/02 13:57:36 adam
12 * Uses preprocessor define WIN32 instead of WINDOWS to build code
13 * for Microsoft WIN32.
15 * Revision 1.1 1997/11/07 13:31:52 adam
16 * Added NT Service name part of statserv_options_block. Moved NT
17 * service utility to server library.
19 * Revision 1.6 1997/09/18 08:49:14 adam
20 * Option -runnormal no needed to run server in standalone mode.
22 * Revision 1.5 1997/09/17 12:10:43 adam
25 * Revision 1.4 1997/09/09 10:10:20 adam
26 * Another MSV5.0 port. Changed projects to include proper
27 * library/include paths.
28 * Server starts server in test-mode when no options are given.
30 * Revision 1.3 1997/09/04 13:50:30 adam
35 /************************************************************/
36 /* Note this file is shared by all processes */
37 /* Should really put it somewhere other than here */
38 /* For some strange reason it won't work when part of a lib */
39 /************************************************************/
49 static AppService *pService = NULL;
50 static BOOL bRunAsService = TRUE;
51 static void *pAppHandle = NULL;
53 /* Private functions to this module */
54 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv);
55 void Service_Delete();
56 void Service_Initialize();
57 BOOL NotifyServiceController();
58 BOOL UpdateServiceStatus(DWORD Status);
59 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode);
60 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart);
61 void CmdRemoveService();
62 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize);
63 BOOL CheckServiceArguments(int argc, char *argv[]);
65 /* Callback functions for thee service manager */
66 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[]);
67 void WINAPI ServiceControlHandler(DWORD fdwControl);
69 /* Function to handle Ctrl + C etc... */
70 BOOL EventHandlerRoutine(DWORD dwCtrlType);
72 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv)
74 pService = malloc(sizeof(AppService));
75 pService->pAppName = pAppName;
76 pService->pServiceName = pServiceName;
77 pService->pServiceDisplayName = pServiceDisplayName;
78 pService->pDependancies = pDependancies;
79 pService->hService = 0;
80 pService->ServiceTable[0].lpServiceName = pServiceName;
81 pService->ServiceTable[0].lpServiceProc = ServiceMain;
82 pService->ServiceTable[1].lpServiceName = NULL;
83 pService->ServiceTable[1].lpServiceProc = NULL;
84 pService->argc = argc;
85 pService->argv = argv;
92 /* Mark the service as stopping */
93 UpdateServiceStatus(SERVICE_STOP_PENDING);
95 /* Stop the service */
96 StopAppService(pAppHandle);
98 /* Service has now stopped */
99 UpdateServiceStatus(SERVICE_STOPPED);
101 /* Free the memory */
107 void Service_Initialize()
109 if (pService != NULL)
111 /* Register ourselves with the control dispatcher */
112 StartServiceCtrlDispatcher(pService->ServiceTable);
116 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[])
118 if (pService != NULL)
120 if (NotifyServiceController())
122 /* Set the status to pending */
123 UpdateServiceStatus(SERVICE_START_PENDING);
125 /* Lets attempt to start the service */
126 if (StartAppService(pAppHandle, pService->argc, pService->argv))
128 /* Service is now up and running */
129 UpdateServiceStatus(SERVICE_RUNNING);
131 /* Lets wait for our clients */
132 RunAppService(pAppHandle);
136 FailServiceStart(GetLastError(), 0);
143 BOOL NotifyServiceController()
145 if (pService == NULL)
153 pService->ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
154 pService->ServiceStatus.dwCurrentState = SERVICE_STOPPED;
155 pService->ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
156 pService->ServiceStatus.dwWin32ExitCode = 0;
157 pService->ServiceStatus.dwServiceSpecificExitCode = 0;
158 pService->ServiceStatus.dwCheckPoint = 0;
159 pService->ServiceStatus.dwWaitHint = 0;
160 pService->hService = RegisterServiceCtrlHandler(pService->pServiceName, ServiceControlHandler);
162 if (pService->hService)
163 UpdateServiceStatus(SERVICE_START_PENDING);
171 void WINAPI ServiceControlHandler(DWORD fdwControl)
173 if (pService != NULL)
177 case SERVICE_CONTROL_STOP:
178 /* Update the service status to be pending */
182 case SERVICE_CONTROL_INTERROGATE:
183 UpdateServiceStatus(pService->ServiceStatus.dwCurrentState);
192 BOOL UpdateServiceStatus(DWORD Status)
194 if (pService != NULL)
196 if (pService->hService)
198 pService->ServiceStatus.dwCurrentState = Status;
199 if ((Status == SERVICE_START_PENDING) || (Status == SERVICE_STOP_PENDING))
201 pService->ServiceStatus.dwCheckPoint ++;
202 pService->ServiceStatus.dwWaitHint = 5000; /* 5 sec.*/
206 pService->ServiceStatus.dwCheckPoint = 0;
207 pService->ServiceStatus.dwWaitHint = 0;
210 return(SetServiceStatus(pService->hService, &pService->ServiceStatus));
217 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode)
219 if (pService != NULL)
221 pService->ServiceStatus.dwWin32ExitCode = Win32Code;
222 pService->ServiceStatus.dwServiceSpecificExitCode = PrivateCode;
223 UpdateServiceStatus(SERVICE_STOPPED);
227 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart)
229 if (pService != NULL)
231 SC_HANDLE schService;
232 SC_HANDLE schSCManager;
236 if (GetModuleFileName(NULL, szPath, 512) == 0)
238 _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(pService->pServiceDisplayName), GetLastErrorText(pService->szErr, 256));
244 strcat (szPath, TEXT(" -runservice"));
245 for (i = 1; i < argc; i++)
247 /* We will add the given command line arguments to the command */
248 /* We are not interested in the install and remove options */
249 if ((stricmp("-install", argv[i]) != 0) &&
250 (stricmp("-installa", argv[i]) != 0) &&
251 (stricmp("-remove", argv[i]) != 0))
253 strcat(szPath, TEXT(" "));
254 strcat(szPath, argv[i]);
258 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
259 NULL, /* database (NULL == default) */
260 SC_MANAGER_ALL_ACCESS); /* access required */
263 schService = CreateService(schSCManager, /* SCManager database */
264 TEXT(pService->pServiceName), /* name of service */
265 TEXT(pService->pServiceDisplayName), /* name to display */
266 SERVICE_ALL_ACCESS, /* desired access */
267 SERVICE_WIN32_OWN_PROCESS, /* service type */
268 bAutoStart ? SERVICE_AUTO_START :
269 SERVICE_DEMAND_START, /* start type */
270 SERVICE_ERROR_NORMAL, /* error control type */
271 szPath, /* service's binary */
272 NULL, /* no load ordering group */
273 NULL, /* no tag identifier */
274 TEXT(pService->pDependancies), /* dependencies */
275 NULL, /* LocalSystem account */
276 NULL); /* no password */
280 _tprintf(TEXT("%s installed.\n"), TEXT(pService->pServiceDisplayName));
281 CloseServiceHandle(schService);
285 _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(pService->szErr, 256));
288 CloseServiceHandle(schSCManager);
291 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
296 void CmdRemoveService()
298 if (pService != NULL)
300 SC_HANDLE schService;
301 SC_HANDLE schSCManager;
303 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
304 NULL, /* database (NULL == default) */
305 SC_MANAGER_ALL_ACCESS); /* access required */
308 schService = OpenService(schSCManager, TEXT(pService->pServiceName), SERVICE_ALL_ACCESS);
312 /* try to stop the service */
313 if (ControlService(schService, SERVICE_CONTROL_STOP, &pService->ServiceStatus))
315 _tprintf(TEXT("Stopping %s."), TEXT(pService->pServiceDisplayName));
318 while (QueryServiceStatus(schService, &pService->ServiceStatus))
320 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING)
329 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOPPED)
330 _tprintf(TEXT("\n%s stopped.\n"), TEXT(pService->pServiceDisplayName));
332 _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(pService->pServiceDisplayName));
336 /* now remove the service */
337 if(DeleteService(schService))
338 _tprintf(TEXT("%s removed.\n"), TEXT(pService->pServiceDisplayName));
340 _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(pService->szErr,256));
342 CloseServiceHandle(schService);
345 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(pService->szErr,256));
347 CloseServiceHandle(schSCManager);
350 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
354 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize)
357 LPTSTR lpszTemp = NULL;
359 dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
367 /* supplied buffer is not long enough */
368 if (!dwRet || ((long)dwSize < (long)dwRet + 14))
369 lpszBuf[0] = TEXT('\0');
372 lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); /* remove cr and newline character */
373 _stprintf(lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError());
377 LocalFree((HLOCAL)lpszTemp);
382 BOOL CheckServiceArguments(int argc, char *argv[])
386 /* Lets process the arguments */
387 for (i = 1; i < argc; i++)
389 if (stricmp("-install", argv[i]) == 0)
391 /* They want to install the service */
392 CmdInstallService(argc, argv, FALSE);
394 /* We don't carry on, after we have installed the service */
397 else if (stricmp("-installa", argv[i]) == 0)
399 /* They want to install the service */
400 CmdInstallService(argc, argv, TRUE);
402 /* We don't carry on, after we have installed the service */
405 else if (stricmp("-remove", argv[i]) == 0)
407 /* Here they want to remove it */
410 /* We don't carry on, after we have removed the service */
413 else if (stricmp ("-runservice", argv[i]) == 0)
415 /* We can carry on, if we reached here */
420 bRunAsService = FALSE;
424 BOOL SetupService(int argc, char *argv[], void *pHandle, LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies)
426 BOOL bDeleteService = TRUE;
427 BOOL bResult = FALSE;
429 /* Save the handle for later use */
430 pAppHandle = pHandle;
432 /* Create our service class */
433 Service_Create(pAppName, pServiceName, pServiceDisplayName, pDependancies, argc, argv);
435 if (CheckServiceArguments(argc, argv))
439 /* No need to set the console control handler, as the service manager handles all this for us */
440 Service_Initialize();
441 bDeleteService = FALSE;
445 /* Set the console control handler for exiting the program */
446 SetConsoleCtrlHandler((PHANDLER_ROUTINE)EventHandlerRoutine, TRUE);
448 /* Now do the main work */
449 ServiceMain(argc, argv);
452 /* We have been successful initializing, so let the caller know */
458 /* Finished with the service now */
464 BOOL EventHandlerRoutine(DWORD dwCtrlType)
466 /* This routine dosn't seem to get called all the time, Why ??? */
469 case CTRL_C_EVENT: /* A CTRL+C signal was received, either from keyboard input or from a signal generated by the GenerateConsoleCtrlEvent function.*/
470 case CTRL_BREAK_EVENT: /* A CTRL+BREAK signal was received, either from keyboard input or from a signal generated by GenerateConsoleCtrlEvent.*/
471 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).*/
472 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.*/
473 case CTRL_SHUTDOWN_EVENT: /* A signal that the system sends to all console processes when the system */
474 /* We are basically shutting down, so call Service_Delete */
480 /* we are not handling this one, so return FALSE */