2 * NT Service interface Utility.
3 * Based on code written by
4 * Chas Woodfield, Fretwell Downing Datasystems.
6 * Revision 1.4 2000-12-05 19:05:10 adam
7 * Service automatically starts in the directory from which it was installed.
9 * Revision 1.3 1999/06/10 11:45:30 adam
10 * Added bend_start, bend_stop handlers and removed pre_init.
11 * Handlers bend_start/bend_stop are called when service/daemon is
14 * Revision 1.2 1999/02/02 13:57:36 adam
15 * Uses preprocessor define WIN32 instead of WINDOWS to build code
16 * for Microsoft WIN32.
18 * Revision 1.1 1997/11/07 13:31:52 adam
19 * Added NT Service name part of statserv_options_block. Moved NT
20 * service utility to server library.
22 * Revision 1.6 1997/09/18 08:49:14 adam
23 * Option -runnormal no needed to run server in standalone mode.
25 * Revision 1.5 1997/09/17 12:10:43 adam
28 * Revision 1.4 1997/09/09 10:10:20 adam
29 * Another MSV5.0 port. Changed projects to include proper
30 * library/include paths.
31 * Server starts server in test-mode when no options are given.
33 * Revision 1.3 1997/09/04 13:50:30 adam
38 /************************************************************/
39 /* Note this file is shared by all processes */
40 /* Should really put it somewhere other than here */
41 /* For some strange reason it won't work when part of a lib */
42 /************************************************************/
53 static AppService *pService = NULL;
54 static BOOL bRunAsService = TRUE;
55 static void *pAppHandle = NULL;
57 /* Private functions to this module */
58 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv);
59 void Service_Delete();
60 void Service_Initialize();
61 BOOL NotifyServiceController();
62 BOOL UpdateServiceStatus(DWORD Status);
63 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode);
64 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart);
65 void CmdRemoveService();
66 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize);
67 BOOL CheckServiceArguments(int argc, char *argv[]);
69 /* Callback functions for thee service manager */
70 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[]);
71 void WINAPI ServiceControlHandler(DWORD fdwControl);
73 /* Function to handle Ctrl + C etc... */
74 BOOL EventHandlerRoutine(DWORD dwCtrlType);
76 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv)
78 pService = malloc(sizeof(AppService));
79 pService->pAppName = pAppName;
80 pService->pServiceName = pServiceName;
81 pService->pServiceDisplayName = pServiceDisplayName;
82 pService->pDependancies = pDependancies;
83 pService->hService = 0;
84 pService->ServiceTable[0].lpServiceName = pServiceName;
85 pService->ServiceTable[0].lpServiceProc = ServiceMain;
86 pService->ServiceTable[1].lpServiceName = NULL;
87 pService->ServiceTable[1].lpServiceProc = NULL;
88 pService->argc = argc;
89 pService->argv = argv;
96 /* Mark the service as stopping */
97 UpdateServiceStatus(SERVICE_STOP_PENDING);
99 /* Stop the service */
100 StopAppService(pAppHandle);
102 /* Service has now stopped */
103 UpdateServiceStatus(SERVICE_STOPPED);
105 /* Free the memory */
111 void Service_Initialize()
113 if (pService != NULL)
115 /* Register ourselves with the control dispatcher */
116 StartServiceCtrlDispatcher(pService->ServiceTable);
120 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[])
122 if (pService != NULL)
124 if (NotifyServiceController())
126 /* Set the status to pending */
127 UpdateServiceStatus(SERVICE_START_PENDING);
129 /* Lets attempt to start the service */
130 if (StartAppService(pAppHandle, pService->argc, pService->argv))
132 /* Service is now up and running */
133 UpdateServiceStatus(SERVICE_RUNNING);
135 /* Lets wait for our clients */
136 RunAppService(pAppHandle);
140 FailServiceStart(GetLastError(), 0);
147 BOOL NotifyServiceController()
149 if (pService == NULL)
157 pService->ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
158 pService->ServiceStatus.dwCurrentState = SERVICE_STOPPED;
159 pService->ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
160 pService->ServiceStatus.dwWin32ExitCode = 0;
161 pService->ServiceStatus.dwServiceSpecificExitCode = 0;
162 pService->ServiceStatus.dwCheckPoint = 0;
163 pService->ServiceStatus.dwWaitHint = 0;
164 pService->hService = RegisterServiceCtrlHandler(pService->pServiceName, ServiceControlHandler);
166 if (pService->hService)
167 UpdateServiceStatus(SERVICE_START_PENDING);
175 void WINAPI ServiceControlHandler(DWORD fdwControl)
177 if (pService != NULL)
181 case SERVICE_CONTROL_STOP:
182 /* Update the service status to be pending */
186 case SERVICE_CONTROL_INTERROGATE:
187 UpdateServiceStatus(pService->ServiceStatus.dwCurrentState);
196 BOOL UpdateServiceStatus(DWORD Status)
198 if (pService != NULL)
200 if (pService->hService)
202 pService->ServiceStatus.dwCurrentState = Status;
203 if ((Status == SERVICE_START_PENDING) || (Status == SERVICE_STOP_PENDING))
205 pService->ServiceStatus.dwCheckPoint ++;
206 pService->ServiceStatus.dwWaitHint = 5000; /* 5 sec.*/
210 pService->ServiceStatus.dwCheckPoint = 0;
211 pService->ServiceStatus.dwWaitHint = 0;
214 return(SetServiceStatus(pService->hService, &pService->ServiceStatus));
221 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode)
223 if (pService != NULL)
225 pService->ServiceStatus.dwWin32ExitCode = Win32Code;
226 pService->ServiceStatus.dwServiceSpecificExitCode = PrivateCode;
227 UpdateServiceStatus(SERVICE_STOPPED);
231 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart)
233 if (pService != NULL)
235 SC_HANDLE schService;
236 SC_HANDLE schSCManager;
240 if (GetModuleFileName(NULL, szPath, 512) == 0)
242 _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(pService->pServiceDisplayName), GetLastErrorText(pService->szErr, 256));
247 char cwdstr[_MAX_PATH];
249 if (!_getcwd(cwdstr, sizeof(cwdstr)))
250 strcpy (cwdstr, ".");
252 strcat (szPath, TEXT(" -runservice \""));
253 strcat (szPath, cwdstr);
254 strcat (szPath, "\"");
256 for (i = 1; i < argc; i++)
258 /* We will add the given command line arguments to the command */
259 /* We are not interested in the install and remove options */
260 if ((stricmp("-install", argv[i]) != 0) &&
261 (stricmp("-installa", argv[i]) != 0) &&
262 (stricmp("-remove", argv[i]) != 0))
264 strcat(szPath, TEXT(" "));
265 strcat(szPath, argv[i]);
269 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
270 NULL, /* database (NULL == default) */
271 SC_MANAGER_ALL_ACCESS); /* access required */
274 schService = CreateService(schSCManager, /* SCManager database */
275 TEXT(pService->pServiceName), /* name of service */
276 TEXT(pService->pServiceDisplayName), /* name to display */
277 SERVICE_ALL_ACCESS, /* desired access */
278 SERVICE_WIN32_OWN_PROCESS, /* service type */
279 bAutoStart ? SERVICE_AUTO_START :
280 SERVICE_DEMAND_START, /* start type */
281 SERVICE_ERROR_NORMAL, /* error control type */
282 szPath, /* service's binary */
283 NULL, /* no load ordering group */
284 NULL, /* no tag identifier */
285 TEXT(pService->pDependancies), /* dependencies */
286 NULL, /* LocalSystem account */
287 NULL); /* no password */
291 _tprintf(TEXT("%s installed.\n"), TEXT(pService->pServiceDisplayName));
292 CloseServiceHandle(schService);
296 _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(pService->szErr, 256));
299 CloseServiceHandle(schSCManager);
302 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
307 void CmdRemoveService()
309 if (pService != NULL)
311 SC_HANDLE schService;
312 SC_HANDLE schSCManager;
314 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
315 NULL, /* database (NULL == default) */
316 SC_MANAGER_ALL_ACCESS); /* access required */
319 schService = OpenService(schSCManager, TEXT(pService->pServiceName), SERVICE_ALL_ACCESS);
323 /* try to stop the service */
324 if (ControlService(schService, SERVICE_CONTROL_STOP, &pService->ServiceStatus))
326 _tprintf(TEXT("Stopping %s."), TEXT(pService->pServiceDisplayName));
329 while (QueryServiceStatus(schService, &pService->ServiceStatus))
331 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING)
340 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOPPED)
341 _tprintf(TEXT("\n%s stopped.\n"), TEXT(pService->pServiceDisplayName));
343 _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(pService->pServiceDisplayName));
347 /* now remove the service */
348 if(DeleteService(schService))
349 _tprintf(TEXT("%s removed.\n"), TEXT(pService->pServiceDisplayName));
351 _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(pService->szErr,256));
353 CloseServiceHandle(schService);
356 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(pService->szErr,256));
358 CloseServiceHandle(schSCManager);
361 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
365 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize)
368 LPTSTR lpszTemp = NULL;
370 dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
378 /* supplied buffer is not long enough */
379 if (!dwRet || ((long)dwSize < (long)dwRet + 14))
380 lpszBuf[0] = TEXT('\0');
383 lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); /* remove cr and newline character */
384 _stprintf(lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError());
388 LocalFree((HLOCAL)lpszTemp);
393 BOOL CheckServiceArguments(int argc, char *argv[])
397 /* Lets process the arguments */
398 for (i = 1; i < argc; i++)
400 if (stricmp("-install", argv[i]) == 0)
402 /* They want to install the service */
403 CmdInstallService(argc, argv, FALSE);
405 /* We don't carry on, after we have installed the service */
408 else if (stricmp("-installa", argv[i]) == 0)
410 /* They want to install the service */
411 CmdInstallService(argc, argv, TRUE);
413 /* We don't carry on, after we have installed the service */
416 else if (stricmp("-remove", argv[i]) == 0)
418 /* Here they want to remove it */
421 /* We don't carry on, after we have removed the service */
424 else if (stricmp ("-runservice", argv[i]) == 0)
426 /* We can carry on, if we reached here */
433 bRunAsService = FALSE;
437 BOOL SetupService(int argc, char *argv[], void *pHandle, LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies)
439 BOOL bDeleteService = TRUE;
440 BOOL bResult = FALSE;
442 /* Save the handle for later use */
443 pAppHandle = pHandle;
445 /* Create our service class */
446 Service_Create(pAppName, pServiceName, pServiceDisplayName, pDependancies, argc, argv);
448 if (CheckServiceArguments(argc, argv))
452 /* No need to set the console control handler, as the service manager handles all this for us */
453 Service_Initialize();
454 bDeleteService = FALSE;
458 /* Set the console control handler for exiting the program */
459 SetConsoleCtrlHandler((PHANDLER_ROUTINE)EventHandlerRoutine, TRUE);
461 /* Now do the main work */
462 ServiceMain(argc, argv);
465 /* We have been successful initializing, so let the caller know */
471 /* Finished with the service now */
477 BOOL EventHandlerRoutine(DWORD dwCtrlType)
479 /* This routine dosn't seem to get called all the time, Why ??? */
482 case CTRL_C_EVENT: /* A CTRL+C signal was received, either from keyboard input or from a signal generated by the GenerateConsoleCtrlEvent function.*/
483 case CTRL_BREAK_EVENT: /* A CTRL+BREAK signal was received, either from keyboard input or from a signal generated by GenerateConsoleCtrlEvent.*/
484 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).*/
485 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.*/
486 case CTRL_SHUTDOWN_EVENT: /* A signal that the system sends to all console processes when the system */
487 /* We are basically shutting down, so call Service_Delete */
493 /* we are not handling this one, so return FALSE */