3 SERVICE_STATUS service_status;
\r
4 SERVICE_STATUS_HANDLE service_handle;
\r
5 HANDLE process_handle;
\r
8 static char service_name[SERVICE_NAME_LENGTH];
\r
9 char exe[EXE_LENGTH];
\r
10 char flags[CMD_LENGTH];
\r
13 unsigned long throttle_delay;
\r
14 HANDLE throttle_timer;
\r
15 LARGE_INTEGER throttle_duetime;
\r
17 static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions;
\r
18 static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 };
\r
20 static unsigned long throttle;
\r
22 static inline int throttle_milliseconds() {
\r
23 /* pow() operates on doubles. */
\r
24 int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
28 /* Connect to the service manager */
\r
29 SC_HANDLE open_service_manager() {
\r
30 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
\r
32 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
39 /* About to install the service */
\r
40 int pre_install_service(int argc, char **argv) {
\r
41 /* Show the dialogue box if we didn't give the */
\r
42 if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]);
\r
44 /* Arguments are optional */
\r
46 size_t flagslen = 0;
\r
49 for (i = 2; i < argc; i++) flagslen += strlen(argv[i]) + 1;
\r
50 if (! flagslen) flagslen = 1;
\r
52 flags = (char *) HeapAlloc(GetProcessHeap(), 0, flagslen);
\r
54 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "flags", "pre_install_service()", 0);
\r
57 ZeroMemory(flags, flagslen);
\r
60 This probably isn't UTF8-safe and should use std::string or something
\r
61 but it's been broken for the best part of a decade and due for a rewrite
\r
62 anyway so it'll do as a quick-'n'-dirty fix. Note that we don't free
\r
63 the flags buffer but as the program exits that isn't a big problem.
\r
65 for (i = 2; i < argc; i++) {
\r
66 size_t len = strlen(argv[i]);
\r
67 memmove(flags + s, argv[i], len);
\r
69 if (i < argc - 1) flags[s++] = ' ';
\r
72 return install_service(argv[0], argv[1], flags);
\r
75 /* About to remove the service */
\r
76 int pre_remove_service(int argc, char **argv) {
\r
77 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
78 if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]);
\r
79 if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]);
\r
80 fprintf(stderr, "To remove a service without confirmation: nssm remove <servicename> confirm\n");
\r
84 /* Install the service */
\r
85 int install_service(char *name, char *exe, char *flags) {
\r
86 /* Open service manager */
\r
87 SC_HANDLE services = open_service_manager();
\r
89 fprintf(stderr, "Error opening service manager!\n");
\r
93 /* Get path of this program */
\r
94 char path[MAX_PATH];
\r
95 GetModuleFileName(0, path, MAX_PATH);
\r
97 /* Construct command */
\r
98 char command[CMD_LENGTH];
\r
99 size_t runlen = strlen(NSSM_RUN);
\r
100 size_t pathlen = strlen(path);
\r
101 if (pathlen + runlen + 2 >= VALUE_LENGTH) {
\r
102 fprintf(stderr, "The full path to " NSSM " is too long!\n");
\r
105 if (_snprintf(command, sizeof(command), "\"%s\" %s", path, NSSM_RUN) < 0) {
\r
106 fprintf(stderr, "Out of memory for ImagePath!\n");
\r
110 /* Work out directory name */
\r
111 size_t len = strlen(exe);
\r
113 for (i = len; i && exe[i] != '\\' && exe[i] != '/'; i--);
\r
114 char dir[MAX_PATH];
\r
115 memmove(dir, exe, i);
\r
118 /* Create the service */
\r
119 SC_HANDLE service = CreateService(services, name, name, SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, command, 0, 0, 0, 0, 0);
\r
121 fprintf(stderr, "Error creating service!\n");
\r
122 CloseServiceHandle(services);
\r
126 /* Now we need to put the parameters into the registry */
\r
127 if (create_parameters(name, exe, flags, dir)) {
\r
128 fprintf(stderr, "Error setting startup parameters for the service!\n");
\r
129 DeleteService(service);
\r
130 CloseServiceHandle(services);
\r
135 CloseServiceHandle(service);
\r
136 CloseServiceHandle(services);
\r
138 printf("Service \"%s\" installed successfully!\n", name);
\r
142 /* Remove the service */
\r
143 int remove_service(char *name) {
\r
144 /* Open service manager */
\r
145 SC_HANDLE services = open_service_manager();
\r
147 fprintf(stderr, "Error opening service manager!\n");
\r
151 /* Try to open the service */
\r
152 SC_HANDLE service = OpenService(services, name, SC_MANAGER_ALL_ACCESS);
\r
154 fprintf(stderr, "Can't open service!");
\r
155 CloseServiceHandle(services);
\r
159 /* Try to delete the service */
\r
160 if (! DeleteService(service)) {
\r
161 fprintf(stderr, "Error deleting service!\n");
\r
162 CloseServiceHandle(service);
\r
163 CloseServiceHandle(services);
\r
168 CloseServiceHandle(service);
\r
169 CloseServiceHandle(services);
\r
171 printf("Service \"%s\" removed successfully!\n", name);
\r
175 /* Service initialisation */
\r
176 void WINAPI service_main(unsigned long argc, char **argv) {
\r
177 if (_snprintf(service_name, sizeof(service_name), "%s", argv[0]) < 0) {
\r
178 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service_name", "service_main()", 0);
\r
182 /* Initialise status */
\r
183 ZeroMemory(&service_status, sizeof(service_status));
\r
184 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
185 service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
186 service_status.dwWin32ExitCode = NO_ERROR;
\r
187 service_status.dwServiceSpecificExitCode = 0;
\r
188 service_status.dwCheckPoint = 0;
\r
189 service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
191 /* Signal we AREN'T running the server */
\r
192 process_handle = 0;
\r
195 /* Register control handler */
\r
196 service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);
\r
197 if (! service_handle) {
\r
198 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
202 service_status.dwCurrentState = SERVICE_START_PENDING;
\r
203 service_status.dwWaitHint = throttle_delay + NSSM_WAITHINT_MARGIN;
\r
204 SetServiceStatus(service_handle, &service_status);
\r
206 /* Try to create the exit action parameters; we don't care if it fails */
\r
207 create_exit_action(argv[0], exit_action_strings[0]);
\r
209 set_service_recovery(service_name);
\r
211 /* Used for signalling a resume if the service pauses when throttled. */
\r
212 throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
213 if (! throttle_timer) {
\r
214 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0);
\r
220 /* Make sure service recovery actions are taken where necessary */
\r
221 void set_service_recovery(char *service_name) {
\r
222 SC_HANDLE services = open_service_manager();
\r
223 if (! services) return;
\r
225 SC_HANDLE service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
\r
226 if (! service) return;
\r
229 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
230 ZeroMemory(&flag, sizeof(flag));
\r
231 flag.fFailureActionsOnNonCrashFailures = true;
\r
233 /* This functionality was added in Vista so the call may fail */
\r
234 ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag);
\r
237 int monitor_service() {
\r
238 /* Set service status to started */
\r
239 int ret = start_service();
\r
242 _snprintf(code, sizeof(code), "%d", ret);
\r
243 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
\r
246 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
\r
248 /* Monitor service service */
\r
249 if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
250 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0);
\r
256 /* Service control handler */
\r
257 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
259 case SERVICE_CONTROL_SHUTDOWN:
\r
260 case SERVICE_CONTROL_STOP:
\r
261 stop_service(0, true, true);
\r
264 case SERVICE_CONTROL_CONTINUE:
\r
265 if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
267 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
268 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
269 service_status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
270 service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN;
\r
271 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0);
\r
272 SetServiceStatus(service_handle, &service_status);
\r
275 case SERVICE_CONTROL_PAUSE:
\r
277 We don't accept pause messages but it isn't possible to register
\r
278 only for continue messages so we have to handle this case.
\r
280 return ERROR_CALL_NOT_IMPLEMENTED;
\r
283 /* Unknown control */
\r
284 return ERROR_CALL_NOT_IMPLEMENTED;
\r
287 /* Start the service */
\r
288 int start_service() {
\r
291 if (process_handle) return 0;
\r
293 /* Allocate a STARTUPINFO structure for a new process */
\r
295 ZeroMemory(&si, sizeof(si));
\r
296 si.cb = sizeof(si);
\r
298 /* Allocate a PROCESSINFO structure for the process */
\r
299 PROCESS_INFORMATION pi;
\r
300 ZeroMemory(&pi, sizeof(pi));
\r
302 /* Get startup parameters */
\r
304 int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay);
\r
306 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
\r
307 return stop_service(2, true, true);
\r
310 /* Launch executable with arguments */
\r
311 char cmd[CMD_LENGTH];
\r
312 if (_snprintf(cmd, sizeof(cmd), "\"%s\" %s", exe, flags) < 0) {
\r
313 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
\r
314 return stop_service(2, true, true);
\r
317 throttle_restart();
\r
319 if (! CreateProcess(0, cmd, 0, 0, false, 0, env, dir, &si, &pi)) {
\r
320 unsigned long error = GetLastError();
\r
321 if (error == ERROR_INVALID_PARAMETER && env) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service_name, exe, NSSM_REG_ENV, 0);
\r
322 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0);
\r
323 return stop_service(3, true, true);
\r
325 process_handle = pi.hProcess;
\r
326 pid = pi.dwProcessId;
\r
328 /* Signal successful start */
\r
329 service_status.dwCurrentState = SERVICE_RUNNING;
\r
330 SetServiceStatus(service_handle, &service_status);
\r
332 /* Wait for a clean startup. */
\r
333 if (WaitForSingleObject(process_handle, throttle_delay) == WAIT_TIMEOUT) throttle = 0;
\r
338 /* Stop the service */
\r
339 int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
\r
340 if (default_action && ! exitcode && ! graceful) {
\r
341 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_GRACEFUL_SUICIDE, service_name, exe, exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_REALLY] ,0);
\r
345 /* Signal we are stopping */
\r
347 service_status.dwCurrentState = SERVICE_STOP_PENDING;
\r
348 service_status.dwWaitHint = NSSM_KILL_WINDOW_GRACE_PERIOD + NSSM_KILL_THREADS_GRACE_PERIOD + NSSM_WAITHINT_MARGIN;
\r
349 SetServiceStatus(service_handle, &service_status);
\r
352 /* Nothing to do if server isn't running */
\r
354 /* Shut down server */
\r
355 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
\r
356 kill_process(service_name, process_handle, pid, 0);
\r
357 process_handle = 0;
\r
359 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
\r
361 end_service((void *) pid, true);
\r
363 /* Signal we stopped */
\r
365 service_status.dwCurrentState = SERVICE_STOPPED;
\r
367 service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
368 service_status.dwServiceSpecificExitCode = exitcode;
\r
371 service_status.dwWin32ExitCode = NO_ERROR;
\r
372 service_status.dwServiceSpecificExitCode = 0;
\r
374 SetServiceStatus(service_handle, &service_status);
\r
380 /* Callback function triggered when the server exits */
\r
381 void CALLBACK end_service(void *arg, unsigned char why) {
\r
382 if (stopping) return;
\r
386 pid = (unsigned long) arg;
\r
388 /* Check exit code */
\r
389 unsigned long exitcode = 0;
\r
390 GetExitCodeProcess(process_handle, &exitcode);
\r
393 kill_process_tree(service_name, pid, exitcode, pid);
\r
396 The why argument is true if our wait timed out or false otherwise.
\r
397 Our wait is infinite so why will never be true when called by the system.
\r
398 If it is indeed true, assume we were called from stop_service() because
\r
399 this is a controlled shutdown, and don't take any restart action.
\r
404 _snprintf(code, sizeof(code), "%d", exitcode);
\r
405 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
\r
407 /* What action should we take? */
\r
408 int action = NSSM_EXIT_RESTART;
\r
409 unsigned char action_string[ACTION_LEN];
\r
410 bool default_action;
\r
411 if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {
\r
412 for (int i = 0; exit_action_strings[i]; i++) {
\r
413 if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
420 process_handle = 0;
\r
423 /* Try to restart the service or return failure code to service manager */
\r
424 case NSSM_EXIT_RESTART:
\r
425 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
\r
426 while (monitor_service()) {
\r
427 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
\r
432 /* Do nothing, just like srvany would */
\r
433 case NSSM_EXIT_IGNORE:
\r
434 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
\r
438 /* Tell the service manager we are finished */
\r
439 case NSSM_EXIT_REALLY:
\r
440 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
\r
441 stop_service(exitcode, true, default_action);
\r
444 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
445 case NSSM_EXIT_UNCLEAN:
\r
446 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
\r
447 exit(stop_service(exitcode, false, default_action));
\r
452 void throttle_restart() {
\r
453 /* This can't be a restart if the service is already running. */
\r
454 if (! throttle++) return;
\r
456 int ms = throttle_milliseconds();
\r
458 if (throttle > 7) throttle = 8;
\r
460 char threshold[8], milliseconds[8];
\r
461 _snprintf(threshold, sizeof(threshold), "%d", throttle_delay);
\r
462 _snprintf(milliseconds, sizeof(milliseconds), "%d", ms);
\r
463 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0);
\r
465 if (throttle_timer) {
\r
466 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
467 throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
468 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
471 service_status.dwCurrentState = SERVICE_PAUSED;
\r
472 SetServiceStatus(service_handle, &service_status);
\r
474 if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE);
\r