2 * NT Service interface Utility.
3 * Based on code written by
4 * Chas Woodfield, Fretwell Downing Informatics.
5 * $Id: service.c,v 1.2 2004-10-15 00:19:00 adam Exp $
10 * \brief Implements NT service handling for GFS.
22 static AppService *pService = NULL;
23 static BOOL bRunAsService = TRUE;
24 static void *pAppHandle = NULL;
26 /* Private functions to this module */
27 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv);
28 void Service_Delete();
29 void Service_Initialize();
30 BOOL NotifyServiceController();
31 BOOL UpdateServiceStatus(DWORD Status);
32 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode);
33 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart);
34 void CmdRemoveService();
35 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize);
36 BOOL CheckServiceArguments(int argc, char *argv[]);
38 /* Callback functions for thee service manager */
39 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[]);
40 void WINAPI ServiceControlHandler(DWORD fdwControl);
42 /* Function to handle Ctrl + C etc... */
43 BOOL EventHandlerRoutine(DWORD dwCtrlType);
45 void Service_Create(LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies, int argc, char **argv)
47 pService = malloc(sizeof(AppService));
48 pService->pAppName = pAppName;
49 pService->pServiceName = pServiceName;
50 pService->pServiceDisplayName = pServiceDisplayName;
51 pService->pDependancies = pDependancies;
52 pService->hService = 0;
53 pService->ServiceTable[0].lpServiceName = pServiceName;
54 pService->ServiceTable[0].lpServiceProc = ServiceMain;
55 pService->ServiceTable[1].lpServiceName = NULL;
56 pService->ServiceTable[1].lpServiceProc = NULL;
57 pService->argc = argc;
58 pService->argv = argv;
65 /* Mark the service as stopping */
66 UpdateServiceStatus(SERVICE_STOP_PENDING);
68 /* Stop the service */
69 StopAppService(pAppHandle);
71 /* Service has now stopped */
72 UpdateServiceStatus(SERVICE_STOPPED);
80 void Service_Initialize()
84 /* Register ourselves with the control dispatcher */
85 StartServiceCtrlDispatcher(pService->ServiceTable);
89 void WINAPI ServiceMain(DWORD argc, LPTSTR argv[])
93 if (NotifyServiceController())
95 /* Set the status to pending */
96 UpdateServiceStatus(SERVICE_START_PENDING);
98 /* Lets attempt to start the service */
99 if (StartAppService(pAppHandle, pService->argc, pService->argv))
101 /* Service is now up and running */
102 UpdateServiceStatus(SERVICE_RUNNING);
104 /* Lets wait for our clients */
105 RunAppService(pAppHandle);
109 FailServiceStart(GetLastError(), 0);
116 BOOL NotifyServiceController()
118 if (pService == NULL)
126 pService->ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
127 pService->ServiceStatus.dwCurrentState = SERVICE_STOPPED;
128 pService->ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
129 pService->ServiceStatus.dwWin32ExitCode = 0;
130 pService->ServiceStatus.dwServiceSpecificExitCode = 0;
131 pService->ServiceStatus.dwCheckPoint = 0;
132 pService->ServiceStatus.dwWaitHint = 0;
133 pService->hService = RegisterServiceCtrlHandler(pService->pServiceName, ServiceControlHandler);
135 if (pService->hService)
136 UpdateServiceStatus(SERVICE_START_PENDING);
144 void WINAPI ServiceControlHandler(DWORD fdwControl)
146 if (pService != NULL)
150 case SERVICE_CONTROL_STOP:
151 /* Update the service status to be pending */
155 case SERVICE_CONTROL_INTERROGATE:
156 UpdateServiceStatus(pService->ServiceStatus.dwCurrentState);
165 BOOL UpdateServiceStatus(DWORD Status)
167 if (pService != NULL)
169 if (pService->hService)
171 pService->ServiceStatus.dwCurrentState = Status;
172 if ((Status == SERVICE_START_PENDING) || (Status == SERVICE_STOP_PENDING))
174 pService->ServiceStatus.dwCheckPoint ++;
175 pService->ServiceStatus.dwWaitHint = 5000; /* 5 sec.*/
179 pService->ServiceStatus.dwCheckPoint = 0;
180 pService->ServiceStatus.dwWaitHint = 0;
183 return(SetServiceStatus(pService->hService, &pService->ServiceStatus));
190 void FailServiceStart(DWORD Win32Code, DWORD PrivateCode)
192 if (pService != NULL)
194 pService->ServiceStatus.dwWin32ExitCode = Win32Code;
195 pService->ServiceStatus.dwServiceSpecificExitCode = PrivateCode;
196 UpdateServiceStatus(SERVICE_STOPPED);
200 void CmdInstallService(int argc, char *argv[], BOOL bAutoStart)
202 if (pService != NULL)
204 SC_HANDLE schService;
205 SC_HANDLE schSCManager;
209 if (GetModuleFileName(NULL, szPath, 512) == 0)
211 _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(pService->pServiceDisplayName), GetLastErrorText(pService->szErr, 256));
216 char cwdstr[_MAX_PATH];
218 if (!_getcwd(cwdstr, sizeof(cwdstr)))
219 strcpy (cwdstr, ".");
221 strcat (szPath, TEXT(" -runservice \""));
222 strcat (szPath, cwdstr);
223 strcat (szPath, "\"");
225 for (i = 1; i < argc; i++)
227 /* We will add the given command line arguments to the command */
228 /* We are not interested in the install and remove options */
229 if ((stricmp("-install", argv[i]) != 0) &&
230 (stricmp("-installa", argv[i]) != 0) &&
231 (stricmp("-remove", argv[i]) != 0))
233 strcat(szPath, TEXT(" "));
234 strcat(szPath, argv[i]);
238 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
239 NULL, /* database (NULL == default) */
240 SC_MANAGER_ALL_ACCESS); /* access required */
243 schService = CreateService(schSCManager, /* SCManager database */
244 TEXT(pService->pServiceName), /* name of service */
245 TEXT(pService->pServiceDisplayName), /* name to display */
246 SERVICE_ALL_ACCESS, /* desired access */
247 SERVICE_WIN32_OWN_PROCESS, /* service type */
248 bAutoStart ? SERVICE_AUTO_START :
249 SERVICE_DEMAND_START, /* start type */
250 SERVICE_ERROR_NORMAL, /* error control type */
251 szPath, /* service's binary */
252 NULL, /* no load ordering group */
253 NULL, /* no tag identifier */
254 TEXT(pService->pDependancies), /* dependencies */
255 NULL, /* LocalSystem account */
256 NULL); /* no password */
260 _tprintf(TEXT("%s installed.\n"), TEXT(pService->pServiceDisplayName));
261 CloseServiceHandle(schService);
265 _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(pService->szErr, 256));
268 CloseServiceHandle(schSCManager);
271 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
276 void CmdRemoveService()
278 if (pService != NULL)
280 SC_HANDLE schService;
281 SC_HANDLE schSCManager;
283 schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */
284 NULL, /* database (NULL == default) */
285 SC_MANAGER_ALL_ACCESS); /* access required */
288 schService = OpenService(schSCManager, TEXT(pService->pServiceName), SERVICE_ALL_ACCESS);
292 /* try to stop the service */
293 if (ControlService(schService, SERVICE_CONTROL_STOP, &pService->ServiceStatus))
295 _tprintf(TEXT("Stopping %s."), TEXT(pService->pServiceDisplayName));
298 while (QueryServiceStatus(schService, &pService->ServiceStatus))
300 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING)
309 if (pService->ServiceStatus.dwCurrentState == SERVICE_STOPPED)
310 _tprintf(TEXT("\n%s stopped.\n"), TEXT(pService->pServiceDisplayName));
312 _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(pService->pServiceDisplayName));
316 /* now remove the service */
317 if(DeleteService(schService))
318 _tprintf(TEXT("%s removed.\n"), TEXT(pService->pServiceDisplayName));
320 _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(pService->szErr,256));
322 CloseServiceHandle(schService);
325 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(pService->szErr,256));
327 CloseServiceHandle(schSCManager);
330 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(pService->szErr,256));
334 LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize)
337 LPTSTR lpszTemp = NULL;
339 dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
347 /* supplied buffer is not long enough */
348 if (!dwRet || ((long)dwSize < (long)dwRet + 14))
349 lpszBuf[0] = TEXT('\0');
352 lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); /* remove cr and newline character */
353 _stprintf(lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError());
357 LocalFree((HLOCAL)lpszTemp);
362 BOOL CheckServiceArguments(int argc, char *argv[])
366 /* Lets process the arguments */
367 for (i = 1; i < argc; i++)
369 if (stricmp("-install", argv[i]) == 0)
371 /* They want to install the service */
372 CmdInstallService(argc, argv, FALSE);
374 /* We don't carry on, after we have installed the service */
377 else if (stricmp("-installa", argv[i]) == 0)
379 /* They want to install the service */
380 CmdInstallService(argc, argv, TRUE);
382 /* We don't carry on, after we have installed the service */
385 else if (stricmp("-remove", argv[i]) == 0)
387 /* Here they want to remove it */
390 /* We don't carry on, after we have removed the service */
393 else if (stricmp ("-runservice", argv[i]) == 0)
395 /* We can carry on, if we reached here */
402 bRunAsService = FALSE;
406 BOOL SetupService(int argc, char *argv[], void *pHandle, LPTSTR pAppName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependancies)
408 BOOL bDeleteService = TRUE;
409 BOOL bResult = FALSE;
411 /* Save the handle for later use */
412 pAppHandle = pHandle;
414 /* Create our service class */
415 Service_Create(pAppName, pServiceName, pServiceDisplayName, pDependancies, argc, argv);
417 if (CheckServiceArguments(argc, argv))
421 /* No need to set the console control handler, as the service manager handles all this for us */
422 Service_Initialize();
423 bDeleteService = FALSE;
427 /* Set the console control handler for exiting the program */
428 SetConsoleCtrlHandler((PHANDLER_ROUTINE)EventHandlerRoutine, TRUE);
430 /* Now do the main work */
431 ServiceMain(argc, argv);
434 /* We have been successful initializing, so let the caller know */
440 /* Finished with the service now */
446 BOOL EventHandlerRoutine(DWORD dwCtrlType)
448 /* This routine dosn't seem to get called all the time, Why ??? */
451 case CTRL_C_EVENT: /* A CTRL+C signal was received, either from keyboard input or from a signal generated by the GenerateConsoleCtrlEvent function.*/
452 case CTRL_BREAK_EVENT: /* A CTRL+BREAK signal was received, either from keyboard input or from a signal generated by GenerateConsoleCtrlEvent.*/
453 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).*/
454 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.*/
455 case CTRL_SHUTDOWN_EVENT: /* A signal that the system sends to all console processes when the system */
456 /* We are basically shutting down, so call Service_Delete */
462 /* we are not handling this one, so return FALSE */