2 * NT Service interface Utility.
3 * Based on code written by
4 * Chas Woodfield, Fretwell Downing Datasystems.
6 * Revision 1.2 1999-02-02 13:57:36 adam
7 * Uses preprocessor define WIN32 instead of WINDOWS to build code
10 * Revision 1.1 1997/11/07 13:31:52 adam
11 * Added NT Service name part of statserv_options_block. Moved NT
12 * service utility to server library.
14 * Revision 1.6 1997/09/18 08:49:14 adam
15 * Option -runnormal no needed to run server in standalone mode.
17 * Revision 1.5 1997/09/17 12:10:43 adam
20 * Revision 1.4 1997/09/09 10:10:20 adam
21 * Another MSV5.0 port. Changed projects to include proper
22 * library/include paths.
23 * Server starts server in test-mode when no options are given.
25 * Revision 1.3 1997/09/04 13:50:30 adam
30 /************************************************************/
31 /* Note this file is shared by all processes */
32 /* Should really put it somewhere other than here */
33 /* For some strange reason it won't work when part of a lib */
34 /************************************************************/
44 static AppService *pService = NULL;
45 static BOOL bRunAsService = TRUE;
46 static void *pAppHandle = NULL;
48 /* Private functions to this module */
49 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv);
50 void Service_Delete();
51 void Service_Initialize();
52 BOOL NotifyServiceController();
53 BOOL UpdateServiceStatus(DWORD Status);
54 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode);
55 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart);
56 void CmdRemoveService();
57 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize);
58 BOOL CheckServiceArguments(int argc, char *argv[]);
60 /* Callback functions for thee service manager */
61 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[]);
62 void WINAPI ServiceControlHandler(DWORD fdwControl);
64 /* Function to handle Ctrl + C etc... */
65 BOOL EventHandlerRoutine(DWORD dwCtrlType);
67 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv)
69 pService = malloc(sizeof(AppService));
70 pService->pAppName = pAppName;
71 pService->pServiceName = pServiceName;
72 pService->pServiceDisplayName = pServiceDisplayName;
73 pService->pDependancies = pDependancies;
74 pService->hService = 0;
75 pService->ServiceTable[0].lpServiceName = pServiceName;
76 pService->ServiceTable[0].lpServiceProc = ServiceMain;
77 pService->ServiceTable[1].lpServiceName = NULL;
78 pService->ServiceTable[1].lpServiceProc = NULL;
79 pService->argc = argc;
80 pService->argv = argv;
87 /* Mark the service as stopping */
88 UpdateServiceStatus(SERVICE_STOP_PENDING);
90 /* Stop the service */
91 StopAppService(pAppHandle);
93 /* Service has now stopped */
94 UpdateServiceStatus(SERVICE_STOPPED);
102 void Service_Initialize()
104 if (pService != NULL)
106 /* Register ourselves with the control dispatcher */
107 StartServiceCtrlDispatcher(pService->ServiceTable);
111 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[])
113 if (pService != NULL)
115 if (NotifyServiceController())
117 /* Set the status to pending */
118 UpdateServiceStatus(SERVICE_START_PENDING);
120 /* Lets attempt to start the service */
121 if (StartAppService(pAppHandle, pService->argc, pService->argv))
123 /* Service is now up and running */
124 UpdateServiceStatus(SERVICE_RUNNING);
126 /* Lets wait for our clients */
127 RunAppService(pAppHandle);
131 FailServiceStart(GetLastError(), 0);
138 BOOL NotifyServiceController()
140 if (pService == NULL)
148 pService->ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
149 pService->ServiceStatus.dwCurrentState = SERVICE_STOPPED;
150 pService->ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
151 pService->ServiceStatus.dwWin32ExitCode = 0;
152 pService->ServiceStatus.dwServiceSpecificExitCode = 0;
153 pService->ServiceStatus.dwCheckPoint = 0;
154 pService->ServiceStatus.dwWaitHint = 0;
155 pService->hService = RegisterServiceCtrlHandler(pService->pServiceName, ServiceControlHandler);
157 if (pService->hService)
158 UpdateServiceStatus(SERVICE_START_PENDING);
166 void WINAPI ServiceControlHandler(DWORD fdwControl)
168 if (pService != NULL)
172 case SERVICE_CONTROL_STOP:
173 /* Update the service status to be pending */
177 case SERVICE_CONTROL_INTERROGATE:
178 UpdateServiceStatus(pService->ServiceStatus.dwCurrentState);
187 BOOL UpdateServiceStatus(DWORD Status)
189 if (pService != NULL)
191 if (pService->hService)
193 pService->ServiceStatus.dwCurrentState = Status;
194 if ((Status == SERVICE_START_PENDING) || (Status == SERVICE_STOP_PENDING))
196 pService->ServiceStatus.dwCheckPoint ++;
197 pService->ServiceStatus.dwWaitHint = 5000; /* 5 sec.*/
201 pService->ServiceStatus.dwCheckPoint = 0;
202 pService->ServiceStatus.dwWaitHint = 0;
205 return(SetServiceStatus(pService->hService, &pService->ServiceStatus));
212 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode)
214 if (pService != NULL)
216 pService->ServiceStatus.dwWin32ExitCode = Win32Code;
217 pService->ServiceStatus.dwServiceSpecificExitCode = PrivateCode;
218 UpdateServiceStatus(SERVICE_STOPPED);
222 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart)
224 if (pService != NULL)
226 SC_HANDLE schService;
227 SC_HANDLE schSCManager;
231 if (GetModuleFileName(NULL, szPath, 512) == 0)
233 _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(pService->pServiceDisplayName), GetLastErrorText(pService->szErr, 256));
239 strcat (szPath, TEXT(" -runservice"));
240 for (i = 1; i < argc; i++)
242 /* We will add the given command line arguments to the command */
243 /* We are not interested in the install and remove options */
244 if ((stricmp("-install", argv[i]) != 0) &&
245 (stricmp("-installa", argv[i]) != 0) &&
246 (stricmp("-remove", argv[i]) != 0))
248 strcat(szPath, TEXT(" "));
249 strcat(szPath, argv[i]);
253 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
254 NULL, /* database (NULL == default) */
255 SC_MANAGER_ALL_ACCESS); /* access required */
258 schService = CreateService(schSCManager, /* SCManager database */
259 TEXT(pService->pServiceName), /* name of service */
260 TEXT(pService->pServiceDisplayName), /* name to display */
261 SERVICE_ALL_ACCESS, /* desired access */
262 SERVICE_WIN32_OWN_PROCESS, /* service type */
263 bAutoStart ? SERVICE_AUTO_START :
264 SERVICE_DEMAND_START, /* start type */
265 SERVICE_ERROR_NORMAL, /* error control type */
266 szPath, /* service's binary */
267 NULL, /* no load ordering group */
268 NULL, /* no tag identifier */
269 TEXT(pService->pDependancies), /* dependencies */
270 NULL, /* LocalSystem account */
271 NULL); /* no password */
275 _tprintf(TEXT("%s installed.\n"), TEXT(pService->pServiceDisplayName));
276 CloseServiceHandle(schService);
280 _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(pService->szErr, 256));
283 CloseServiceHandle(schSCManager);
286 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
291 void CmdRemoveService()
293 if (pService != NULL)
295 SC_HANDLE schService;
296 SC_HANDLE schSCManager;
298 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
299 NULL, /* database (NULL == default) */
300 SC_MANAGER_ALL_ACCESS); /* access required */
303 schService = OpenService(schSCManager, TEXT(pService->pServiceName), SERVICE_ALL_ACCESS);
307 /* try to stop the service */
308 if (ControlService(schService, SERVICE_CONTROL_STOP, &pService->ServiceStatus))
310 _tprintf(TEXT("Stopping %s."), TEXT(pService->pServiceDisplayName));
313 while (QueryServiceStatus(schService, &pService->ServiceStatus))
315 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING)
324 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOPPED)
325 _tprintf(TEXT("\n%s stopped.\n"), TEXT(pService->pServiceDisplayName));
327 _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(pService->pServiceDisplayName));
331 /* now remove the service */
332 if(DeleteService(schService))
333 _tprintf(TEXT("%s removed.\n"), TEXT(pService->pServiceDisplayName));
335 _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(pService->szErr,256));
337 CloseServiceHandle(schService);
340 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(pService->szErr,256));
342 CloseServiceHandle(schSCManager);
345 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
349 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize)
352 LPTSTR lpszTemp = NULL;
354 dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
362 /* supplied buffer is not long enough */
363 if (!dwRet || ((long)dwSize < (long)dwRet + 14))
364 lpszBuf[0] = TEXT('\0');
367 lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); /* remove cr and newline character */
368 _stprintf(lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError());
372 LocalFree((HLOCAL)lpszTemp);
377 BOOL CheckServiceArguments(int argc, char *argv[])
381 /* Lets process the arguments */
382 for (i = 1; i < argc; i++)
384 if (stricmp("-install", argv[i]) == 0)
386 /* They want to install the service */
387 CmdInstallService(argc, argv, FALSE);
389 /* We don't carry on, after we have installed the service */
392 else if (stricmp("-installa", argv[i]) == 0)
394 /* They want to install the service */
395 CmdInstallService(argc, argv, TRUE);
397 /* We don't carry on, after we have installed the service */
400 else if (stricmp("-remove", argv[i]) == 0)
402 /* Here they want to remove it */
405 /* We don't carry on, after we have removed the service */
408 else if (stricmp ("-runservice", argv[i]) == 0)
410 /* We can carry on, if we reached here */
415 bRunAsService = FALSE;
419 BOOL SetupService(int argc, char *argv[], void *pHandle, LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies)
421 BOOL bDeleteService = TRUE;
422 BOOL bResult = FALSE;
424 /* Save the handle for later use */
425 pAppHandle = pHandle;
427 /* Create our service class */
428 Service_Create(pAppName, pServiceName, pServiceDisplayName, pDependancies, argc, argv);
430 if (CheckServiceArguments(argc, argv))
434 /* No need to set the console control handler, as the service manager handles all this for us */
435 Service_Initialize();
436 bDeleteService = FALSE;
440 /* Set the console control handler for exiting the program */
441 /* SetConsoleCtrlHandler((PHANDLER_ROUTINE)EventHandlerRoutine, TRUE);
444 /* Now do the main work */
445 ServiceMain(argc, argv);
448 /* We have been successful initializing, so let the caller know */
454 /* Finished with the service now */
460 BOOL EventHandlerRoutine(DWORD dwCtrlType)
462 /* This routine dosn't seem to get called all the time, Why ??? */
465 case CTRL_C_EVENT: /* A CTRL+C signal was received, either from keyboard input or from a signal generated by the GenerateConsoleCtrlEvent function.*/
466 case CTRL_BREAK_EVENT: /* A CTRL+BREAK signal was received, either from keyboard input or from a signal generated by GenerateConsoleCtrlEvent.*/
467 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).*/
468 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.*/
469 case CTRL_SHUTDOWN_EVENT: /* A signal that the system sends to all console processes when the system */
470 /* We are basically shutting down, so call Service_Delete */
476 /* we are not handling this one, so return FALSE */