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 pathlen = strlen(path);
\r
100 if (pathlen + 1 >= VALUE_LENGTH) {
\r
101 fprintf(stderr, "The full path to " NSSM " is too long!\n");
\r
104 if (_snprintf(command, sizeof(command), "\"%s\"", path) < 0) {
\r
105 fprintf(stderr, "Out of memory for ImagePath!\n");
\r
109 /* Work out directory name */
\r
110 size_t len = strlen(exe);
\r
112 for (i = len; i && exe[i] != '\\' && exe[i] != '/'; i--);
\r
113 char dir[MAX_PATH];
\r
114 memmove(dir, exe, i);
\r
117 /* Create the service */
\r
118 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
120 fprintf(stderr, "Error creating service!\n");
\r
121 CloseServiceHandle(services);
\r
125 /* Now we need to put the parameters into the registry */
\r
126 if (create_parameters(name, exe, flags, dir)) {
\r
127 fprintf(stderr, "Error setting startup parameters for the service!\n");
\r
128 DeleteService(service);
\r
129 CloseServiceHandle(services);
\r
134 CloseServiceHandle(service);
\r
135 CloseServiceHandle(services);
\r
137 printf("Service \"%s\" installed successfully!\n", name);
\r
141 /* Remove the service */
\r
142 int remove_service(char *name) {
\r
143 /* Open service manager */
\r
144 SC_HANDLE services = open_service_manager();
\r
146 fprintf(stderr, "Error opening service manager!\n");
\r
150 /* Try to open the service */
\r
151 SC_HANDLE service = OpenService(services, name, SC_MANAGER_ALL_ACCESS);
\r
153 fprintf(stderr, "Can't open service!");
\r
154 CloseServiceHandle(services);
\r
158 /* Try to delete the service */
\r
159 if (! DeleteService(service)) {
\r
160 fprintf(stderr, "Error deleting service!\n");
\r
161 CloseServiceHandle(service);
\r
162 CloseServiceHandle(services);
\r
167 CloseServiceHandle(service);
\r
168 CloseServiceHandle(services);
\r
170 printf("Service \"%s\" removed successfully!\n", name);
\r
174 /* Service initialisation */
\r
175 void WINAPI service_main(unsigned long argc, char **argv) {
\r
176 if (_snprintf(service_name, sizeof(service_name), "%s", argv[0]) < 0) {
\r
177 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service_name", "service_main()", 0);
\r
181 /* Initialise status */
\r
182 ZeroMemory(&service_status, sizeof(service_status));
\r
183 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
184 service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
185 service_status.dwWin32ExitCode = NO_ERROR;
\r
186 service_status.dwServiceSpecificExitCode = 0;
\r
187 service_status.dwCheckPoint = 0;
\r
188 service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
190 /* Signal we AREN'T running the server */
\r
191 process_handle = 0;
\r
194 /* Register control handler */
\r
195 service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);
\r
196 if (! service_handle) {
\r
197 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
201 log_service_control(service_name, 0, true);
\r
203 service_status.dwCurrentState = SERVICE_START_PENDING;
\r
204 service_status.dwWaitHint = throttle_delay + NSSM_WAITHINT_MARGIN;
\r
205 SetServiceStatus(service_handle, &service_status);
\r
207 /* Try to create the exit action parameters; we don't care if it fails */
\r
208 create_exit_action(argv[0], exit_action_strings[0]);
\r
210 set_service_recovery(service_name);
\r
212 /* Used for signalling a resume if the service pauses when throttled. */
\r
213 throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
214 if (! throttle_timer) {
\r
215 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0);
\r
221 /* Make sure service recovery actions are taken where necessary */
\r
222 void set_service_recovery(char *service_name) {
\r
223 SC_HANDLE services = open_service_manager();
\r
224 if (! services) return;
\r
226 SC_HANDLE service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
\r
227 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 if (! ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
235 unsigned long error = GetLastError();
\r
236 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
237 if (error != ERROR_INVALID_LEVEL) {
\r
238 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CHANGESERVICECONFIG2_FAILED, service_name, error_string(error), 0);
\r
243 int monitor_service() {
\r
244 /* Set service status to started */
\r
245 int ret = start_service();
\r
248 _snprintf(code, sizeof(code), "%d", ret);
\r
249 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
\r
252 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
\r
254 /* Monitor service service */
\r
255 if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
256 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0);
\r
262 char *service_control_text(unsigned long control) {
\r
264 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
265 case 0: return "START";
\r
266 case SERVICE_CONTROL_STOP: return "STOP";
\r
267 case SERVICE_CONTROL_SHUTDOWN: return "SHUTDOWN";
\r
268 case SERVICE_CONTROL_PAUSE: return "PAUSE";
\r
269 case SERVICE_CONTROL_CONTINUE: return "CONTINUE";
\r
270 case SERVICE_CONTROL_INTERROGATE: return "INTERROGATE";
\r
275 void log_service_control(char *service_name, unsigned long control, bool handled) {
\r
276 char *text = service_control_text(control);
\r
277 unsigned long event;
\r
280 /* "0x" + 8 x hex + NULL */
\r
281 text = (char *) HeapAlloc(GetProcessHeap(), 0, 11);
\r
283 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0);
\r
286 if (_snprintf(text, 11, "0x%08x", control) < 0) {
\r
287 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0);
\r
288 HeapFree(GetProcessHeap(), 0, text);
\r
292 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
294 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
295 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
297 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
299 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
300 HeapFree(GetProcessHeap(), 0, text);
\r
304 /* Service control handler */
\r
305 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
307 case SERVICE_CONTROL_SHUTDOWN:
\r
308 case SERVICE_CONTROL_STOP:
\r
309 log_service_control(service_name, control, true);
\r
310 stop_service(0, true, true);
\r
313 case SERVICE_CONTROL_CONTINUE:
\r
314 log_service_control(service_name, control, true);
\r
315 if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
317 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
318 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
319 service_status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
320 service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN;
\r
321 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0);
\r
322 SetServiceStatus(service_handle, &service_status);
\r
325 case SERVICE_CONTROL_PAUSE:
\r
327 We don't accept pause messages but it isn't possible to register
\r
328 only for continue messages so we have to handle this case.
\r
330 log_service_control(service_name, control, false);
\r
331 return ERROR_CALL_NOT_IMPLEMENTED;
\r
334 /* Unknown control */
\r
335 log_service_control(service_name, control, false);
\r
336 return ERROR_CALL_NOT_IMPLEMENTED;
\r
339 /* Start the service */
\r
340 int start_service() {
\r
343 if (process_handle) return 0;
\r
345 /* Allocate a STARTUPINFO structure for a new process */
\r
347 ZeroMemory(&si, sizeof(si));
\r
348 si.cb = sizeof(si);
\r
350 /* Allocate a PROCESSINFO structure for the process */
\r
351 PROCESS_INFORMATION pi;
\r
352 ZeroMemory(&pi, sizeof(pi));
\r
354 /* Get startup parameters */
\r
356 int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay);
\r
358 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
\r
359 return stop_service(2, true, true);
\r
362 /* Launch executable with arguments */
\r
363 char cmd[CMD_LENGTH];
\r
364 if (_snprintf(cmd, sizeof(cmd), "\"%s\" %s", exe, flags) < 0) {
\r
365 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
\r
366 return stop_service(2, true, true);
\r
369 throttle_restart();
\r
371 if (! CreateProcess(0, cmd, 0, 0, false, 0, env, dir, &si, &pi)) {
\r
372 unsigned long error = GetLastError();
\r
373 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
374 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0);
\r
375 return stop_service(3, true, true);
\r
377 process_handle = pi.hProcess;
\r
378 pid = pi.dwProcessId;
\r
380 /* Signal successful start */
\r
381 service_status.dwCurrentState = SERVICE_RUNNING;
\r
382 SetServiceStatus(service_handle, &service_status);
\r
384 /* Wait for a clean startup. */
\r
385 if (WaitForSingleObject(process_handle, throttle_delay) == WAIT_TIMEOUT) throttle = 0;
\r
390 /* Stop the service */
\r
391 int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
\r
392 if (default_action && ! exitcode && ! graceful) {
\r
393 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
397 /* Signal we are stopping */
\r
399 service_status.dwCurrentState = SERVICE_STOP_PENDING;
\r
400 service_status.dwWaitHint = NSSM_KILL_WINDOW_GRACE_PERIOD + NSSM_KILL_THREADS_GRACE_PERIOD + NSSM_WAITHINT_MARGIN;
\r
401 SetServiceStatus(service_handle, &service_status);
\r
404 /* Nothing to do if server isn't running */
\r
406 /* Shut down server */
\r
407 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
\r
408 kill_process(service_name, process_handle, pid, 0);
\r
409 process_handle = 0;
\r
411 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
\r
413 end_service((void *) pid, true);
\r
415 /* Signal we stopped */
\r
417 service_status.dwCurrentState = SERVICE_STOPPED;
\r
419 service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
420 service_status.dwServiceSpecificExitCode = exitcode;
\r
423 service_status.dwWin32ExitCode = NO_ERROR;
\r
424 service_status.dwServiceSpecificExitCode = 0;
\r
426 SetServiceStatus(service_handle, &service_status);
\r
432 /* Callback function triggered when the server exits */
\r
433 void CALLBACK end_service(void *arg, unsigned char why) {
\r
434 if (stopping) return;
\r
438 pid = (unsigned long) arg;
\r
440 /* Check exit code */
\r
441 unsigned long exitcode = 0;
\r
442 GetExitCodeProcess(process_handle, &exitcode);
\r
445 kill_process_tree(service_name, pid, exitcode, pid);
\r
448 The why argument is true if our wait timed out or false otherwise.
\r
449 Our wait is infinite so why will never be true when called by the system.
\r
450 If it is indeed true, assume we were called from stop_service() because
\r
451 this is a controlled shutdown, and don't take any restart action.
\r
456 _snprintf(code, sizeof(code), "%d", exitcode);
\r
457 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
\r
459 /* What action should we take? */
\r
460 int action = NSSM_EXIT_RESTART;
\r
461 unsigned char action_string[ACTION_LEN];
\r
462 bool default_action;
\r
463 if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {
\r
464 for (int i = 0; exit_action_strings[i]; i++) {
\r
465 if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
472 process_handle = 0;
\r
475 /* Try to restart the service or return failure code to service manager */
\r
476 case NSSM_EXIT_RESTART:
\r
477 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
\r
478 while (monitor_service()) {
\r
479 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
\r
484 /* Do nothing, just like srvany would */
\r
485 case NSSM_EXIT_IGNORE:
\r
486 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
\r
490 /* Tell the service manager we are finished */
\r
491 case NSSM_EXIT_REALLY:
\r
492 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
\r
493 stop_service(exitcode, true, default_action);
\r
496 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
497 case NSSM_EXIT_UNCLEAN:
\r
498 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
\r
499 exit(stop_service(exitcode, false, default_action));
\r
504 void throttle_restart() {
\r
505 /* This can't be a restart if the service is already running. */
\r
506 if (! throttle++) return;
\r
508 int ms = throttle_milliseconds();
\r
510 if (throttle > 7) throttle = 8;
\r
512 char threshold[8], milliseconds[8];
\r
513 _snprintf(threshold, sizeof(threshold), "%d", throttle_delay);
\r
514 _snprintf(milliseconds, sizeof(milliseconds), "%d", ms);
\r
515 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0);
\r
517 if (throttle_timer) {
\r
518 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
519 throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
520 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
523 service_status.dwCurrentState = SERVICE_PAUSED;
\r
524 SetServiceStatus(service_handle, &service_status);
\r
526 if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE);
\r