2 * NT Service interface Utility.
3 * Based on code written by
4 * Chas Woodfield, Fretwell Downing Datasystems.
6 * Revision 1.1 1997-11-07 13:31:52 adam
7 * Added NT Service name part of statserv_options_block. Moved NT
8 * service utility to server library.
10 * Revision 1.6 1997/09/18 08:49:14 adam
11 * Option -runnormal no needed to run server in standalone mode.
13 * Revision 1.5 1997/09/17 12:10:43 adam
16 * Revision 1.4 1997/09/09 10:10:20 adam
17 * Another MSV5.0 port. Changed projects to include proper
18 * library/include paths.
19 * Server starts server in test-mode when no options are given.
21 * Revision 1.3 1997/09/04 13:50:30 adam
26 /************************************************************/
27 /* Note this file is shared by all processes */
28 /* Should really put it somewhere other than here */
29 /* For some strange reason it won't work when part of a lib */
30 /************************************************************/
40 static AppService *pService = NULL;
41 static BOOL bRunAsService = TRUE;
42 static void *pAppHandle = NULL;
44 /* Private functions to this module */
45 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv);
46 void Service_Delete();
47 void Service_Initialize();
48 BOOL NotifyServiceController();
49 BOOL UpdateServiceStatus(DWORD Status);
50 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode);
51 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart);
52 void CmdRemoveService();
53 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize);
54 BOOL CheckServiceArguments(int argc, char *argv[]);
56 /* Callback functions for thee service manager */
57 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[]);
58 void WINAPI ServiceControlHandler(DWORD fdwControl);
60 /* Function to handle Ctrl + C etc... */
61 BOOL EventHandlerRoutine(DWORD dwCtrlType);
63 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv)
65 pService = malloc(sizeof(AppService));
66 pService->pAppName = pAppName;
67 pService->pServiceName = pServiceName;
68 pService->pServiceDisplayName = pServiceDisplayName;
69 pService->pDependancies = pDependancies;
70 pService->hService = 0;
71 pService->ServiceTable[0].lpServiceName = pServiceName;
72 pService->ServiceTable[0].lpServiceProc = ServiceMain;
73 pService->ServiceTable[1].lpServiceName = NULL;
74 pService->ServiceTable[1].lpServiceProc = NULL;
75 pService->argc = argc;
76 pService->argv = argv;
83 /* Mark the service as stopping */
84 UpdateServiceStatus(SERVICE_STOP_PENDING);
86 /* Stop the service */
87 StopAppService(pAppHandle);
89 /* Service has now stopped */
90 UpdateServiceStatus(SERVICE_STOPPED);
98 void Service_Initialize()
100 if (pService != NULL)
102 /* Register ourselves with the control dispatcher */
103 StartServiceCtrlDispatcher(pService->ServiceTable);
107 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[])
109 if (pService != NULL)
111 if (NotifyServiceController())
113 /* Set the status to pending */
114 UpdateServiceStatus(SERVICE_START_PENDING);
116 /* Lets attempt to start the service */
117 if (StartAppService(pAppHandle, pService->argc, pService->argv))
119 /* Service is now up and running */
120 UpdateServiceStatus(SERVICE_RUNNING);
122 /* Lets wait for our clients */
123 RunAppService(pAppHandle);
127 FailServiceStart(GetLastError(), 0);
134 BOOL NotifyServiceController()
136 if (pService == NULL)
144 pService->ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
145 pService->ServiceStatus.dwCurrentState = SERVICE_STOPPED;
146 pService->ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
147 pService->ServiceStatus.dwWin32ExitCode = 0;
148 pService->ServiceStatus.dwServiceSpecificExitCode = 0;
149 pService->ServiceStatus.dwCheckPoint = 0;
150 pService->ServiceStatus.dwWaitHint = 0;
151 pService->hService = RegisterServiceCtrlHandler(pService->pServiceName, ServiceControlHandler);
153 if (pService->hService)
154 UpdateServiceStatus(SERVICE_START_PENDING);
162 void WINAPI ServiceControlHandler(DWORD fdwControl)
164 if (pService != NULL)
168 case SERVICE_CONTROL_STOP:
169 /* Update the service status to be pending */
173 case SERVICE_CONTROL_INTERROGATE:
174 UpdateServiceStatus(pService->ServiceStatus.dwCurrentState);
183 BOOL UpdateServiceStatus(DWORD Status)
185 if (pService != NULL)
187 if (pService->hService)
189 pService->ServiceStatus.dwCurrentState = Status;
190 if ((Status == SERVICE_START_PENDING) || (Status == SERVICE_STOP_PENDING))
192 pService->ServiceStatus.dwCheckPoint ++;
193 pService->ServiceStatus.dwWaitHint = 5000; /* 5 sec.*/
197 pService->ServiceStatus.dwCheckPoint = 0;
198 pService->ServiceStatus.dwWaitHint = 0;
201 return(SetServiceStatus(pService->hService, &pService->ServiceStatus));
208 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode)
210 if (pService != NULL)
212 pService->ServiceStatus.dwWin32ExitCode = Win32Code;
213 pService->ServiceStatus.dwServiceSpecificExitCode = PrivateCode;
214 UpdateServiceStatus(SERVICE_STOPPED);
218 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart)
220 if (pService != NULL)
222 SC_HANDLE schService;
223 SC_HANDLE schSCManager;
227 if (GetModuleFileName(NULL, szPath, 512) == 0)
229 _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(pService->pServiceDisplayName), GetLastErrorText(pService->szErr, 256));
235 strcat (szPath, TEXT(" -runservice"));
236 for (i = 1; i < argc; i++)
238 /* We will add the given command line arguments to the command */
239 /* We are not interested in the install and remove options */
240 if ((stricmp("-install", argv[i]) != 0) &&
241 (stricmp("-installa", argv[i]) != 0) &&
242 (stricmp("-remove", argv[i]) != 0))
244 strcat(szPath, TEXT(" "));
245 strcat(szPath, argv[i]);
249 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
250 NULL, /* database (NULL == default) */
251 SC_MANAGER_ALL_ACCESS); /* access required */
254 schService = CreateService(schSCManager, /* SCManager database */
255 TEXT(pService->pServiceName), /* name of service */
256 TEXT(pService->pServiceDisplayName), /* name to display */
257 SERVICE_ALL_ACCESS, /* desired access */
258 SERVICE_WIN32_OWN_PROCESS, /* service type */
259 bAutoStart ? SERVICE_AUTO_START :
260 SERVICE_DEMAND_START, /* start type */
261 SERVICE_ERROR_NORMAL, /* error control type */
262 szPath, /* service's binary */
263 NULL, /* no load ordering group */
264 NULL, /* no tag identifier */
265 TEXT(pService->pDependancies), /* dependencies */
266 NULL, /* LocalSystem account */
267 NULL); /* no password */
271 _tprintf(TEXT("%s installed.\n"), TEXT(pService->pServiceDisplayName));
272 CloseServiceHandle(schService);
276 _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(pService->szErr, 256));
279 CloseServiceHandle(schSCManager);
282 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
287 void CmdRemoveService()
289 if (pService != NULL)
291 SC_HANDLE schService;
292 SC_HANDLE schSCManager;
294 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
295 NULL, /* database (NULL == default) */
296 SC_MANAGER_ALL_ACCESS); /* access required */
299 schService = OpenService(schSCManager, TEXT(pService->pServiceName), SERVICE_ALL_ACCESS);
303 /* try to stop the service */
304 if (ControlService(schService, SERVICE_CONTROL_STOP, &pService->ServiceStatus))
306 _tprintf(TEXT("Stopping %s."), TEXT(pService->pServiceDisplayName));
309 while (QueryServiceStatus(schService, &pService->ServiceStatus))
311 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING)
320 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOPPED)
321 _tprintf(TEXT("\n%s stopped.\n"), TEXT(pService->pServiceDisplayName));
323 _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(pService->pServiceDisplayName));
327 /* now remove the service */
328 if(DeleteService(schService))
329 _tprintf(TEXT("%s removed.\n"), TEXT(pService->pServiceDisplayName));
331 _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(pService->szErr,256));
333 CloseServiceHandle(schService);
336 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(pService->szErr,256));
338 CloseServiceHandle(schSCManager);
341 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
345 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize)
348 LPTSTR lpszTemp = NULL;
350 dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
358 /* supplied buffer is not long enough */
359 if (!dwRet || ((long)dwSize < (long)dwRet + 14))
360 lpszBuf[0] = TEXT('\0');
363 lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); /* remove cr and newline character */
364 _stprintf(lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError());
368 LocalFree((HLOCAL)lpszTemp);
373 BOOL CheckServiceArguments(int argc, char *argv[])
377 /* Lets process the arguments */
378 for (i = 1; i < argc; i++)
380 if (stricmp("-install", argv[i]) == 0)
382 /* They want to install the service */
383 CmdInstallService(argc, argv, FALSE);
385 /* We don't carry on, after we have installed the service */
388 else if (stricmp("-installa", argv[i]) == 0)
390 /* They want to install the service */
391 CmdInstallService(argc, argv, TRUE);
393 /* We don't carry on, after we have installed the service */
396 else if (stricmp("-remove", argv[i]) == 0)
398 /* Here they want to remove it */
401 /* We don't carry on, after we have removed the service */
404 else if (stricmp ("-runservice", argv[i]) == 0)
406 /* We can carry on, if we reached here */
411 bRunAsService = FALSE;
415 BOOL SetupService(int argc, char *argv[], void *pHandle, LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies)
417 BOOL bDeleteService = TRUE;
418 BOOL bResult = FALSE;
420 /* Save the handle for later use */
421 pAppHandle = pHandle;
423 /* Create our service class */
424 Service_Create(pAppName, pServiceName, pServiceDisplayName, pDependancies, argc, argv);
426 if (CheckServiceArguments(argc, argv))
430 /* No need to set the console control handler, as the service manager handles all this for us */
431 Service_Initialize();
432 bDeleteService = FALSE;
436 /* Set the console control handler for exiting the program */
437 /* SetConsoleCtrlHandler((PHANDLER_ROUTINE)EventHandlerRoutine, TRUE);
440 /* Now do the main work */
441 ServiceMain(argc, argv);
444 /* We have been successful initializing, so let the caller know */
450 /* Finished with the service now */
456 BOOL EventHandlerRoutine(DWORD dwCtrlType)
458 /* This routine dosn't seem to get called all the time, Why ??? */
461 case CTRL_C_EVENT: /* A CTRL+C signal was received, either from keyboard input or from a signal generated by the GenerateConsoleCtrlEvent function.*/
462 case CTRL_BREAK_EVENT: /* A CTRL+BREAK signal was received, either from keyboard input or from a signal generated by GenerateConsoleCtrlEvent.*/
463 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).*/
464 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.*/
465 case CTRL_SHUTDOWN_EVENT: /* A signal that the system sends to all console processes when the system */
466 /* We are basically shutting down, so call Service_Delete */
472 /* we are not handling this one, so return FALSE */