4 SERVICE_STATUS service_status;
\r
5 SERVICE_STATUS_HANDLE service_handle;
\r
6 HANDLE process_handle;
\r
9 static char service_name[SERVICE_NAME_LENGTH];
\r
10 char exe[EXE_LENGTH];
\r
11 char flags[CMD_LENGTH];
\r
15 unsigned long throttle_delay;
\r
16 unsigned long stop_method;
\r
17 HANDLE throttle_timer;
\r
18 LARGE_INTEGER throttle_duetime;
\r
19 FILETIME creation_time;
\r
21 static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions;
\r
22 static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 };
\r
24 static unsigned long throttle;
\r
26 static inline int throttle_milliseconds() {
\r
27 /* pow() operates on doubles. */
\r
28 int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
32 /* Connect to the service manager */
\r
33 SC_HANDLE open_service_manager() {
\r
34 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
\r
36 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
43 /* About to install the service */
\r
44 int pre_install_service(int argc, char **argv) {
\r
45 /* Show the dialogue box if we didn't give the service name and path */
\r
46 if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]);
\r
48 /* Arguments are optional */
\r
50 size_t flagslen = 0;
\r
53 for (i = 2; i < argc; i++) flagslen += strlen(argv[i]) + 1;
\r
54 if (! flagslen) flagslen = 1;
\r
56 flags = (char *) HeapAlloc(GetProcessHeap(), 0, flagslen);
\r
58 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "flags", "pre_install_service()", 0);
\r
61 ZeroMemory(flags, flagslen);
\r
64 This probably isn't UTF8-safe and should use std::string or something
\r
65 but it's been broken for the best part of a decade and due for a rewrite
\r
66 anyway so it'll do as a quick-'n'-dirty fix. Note that we don't free
\r
67 the flags buffer but as the program exits that isn't a big problem.
\r
69 for (i = 2; i < argc; i++) {
\r
70 size_t len = strlen(argv[i]);
\r
71 memmove(flags + s, argv[i], len);
\r
73 if (i < argc - 1) flags[s++] = ' ';
\r
76 return install_service(argv[0], argv[1], flags);
\r
79 /* About to remove the service */
\r
80 int pre_remove_service(int argc, char **argv) {
\r
81 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
82 if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]);
\r
83 if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]);
\r
84 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
88 /* Install the service */
\r
89 int install_service(char *name, char *exe, char *flags) {
\r
90 /* Open service manager */
\r
91 SC_HANDLE services = open_service_manager();
\r
93 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
97 /* Get path of this program */
\r
98 char path[MAX_PATH];
\r
99 GetModuleFileName(0, path, MAX_PATH);
\r
101 /* Construct command */
\r
102 char command[CMD_LENGTH];
\r
103 size_t pathlen = strlen(path);
\r
104 if (pathlen + 1 >= VALUE_LENGTH) {
\r
105 print_message(stderr, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);
\r
108 if (_snprintf_s(command, sizeof(command), _TRUNCATE, "\"%s\"", path) < 0) {
\r
109 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY_FOR_IMAGEPATH);
\r
113 /* Work out directory name */
\r
114 size_t len = strlen(exe);
\r
116 for (i = len; i && exe[i] != '\\' && exe[i] != '/'; i--);
\r
117 char dir[MAX_PATH];
\r
118 memmove(dir, exe, i);
\r
121 /* Create the service */
\r
122 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
124 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED);
\r
125 CloseServiceHandle(services);
\r
129 /* Now we need to put the parameters into the registry */
\r
130 if (create_parameters(name, exe, flags, dir)) {
\r
131 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
132 DeleteService(service);
\r
133 CloseServiceHandle(services);
\r
137 set_service_recovery(service, name);
\r
140 CloseServiceHandle(service);
\r
141 CloseServiceHandle(services);
\r
143 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, name);
\r
147 /* Remove the service */
\r
148 int remove_service(char *name) {
\r
149 /* Open service manager */
\r
150 SC_HANDLE services = open_service_manager();
\r
152 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
156 /* Try to open the service */
\r
157 SC_HANDLE service = OpenService(services, name, SC_MANAGER_ALL_ACCESS);
\r
159 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
\r
160 CloseServiceHandle(services);
\r
164 /* Try to delete the service */
\r
165 if (! DeleteService(service)) {
\r
166 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
167 CloseServiceHandle(service);
\r
168 CloseServiceHandle(services);
\r
173 CloseServiceHandle(service);
\r
174 CloseServiceHandle(services);
\r
176 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, name);
\r
180 /* Service initialisation */
\r
181 void WINAPI service_main(unsigned long argc, char **argv) {
\r
182 if (_snprintf_s(service_name, sizeof(service_name), _TRUNCATE, "%s", argv[0]) < 0) {
\r
183 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service_name", "service_main()", 0);
\r
187 /* Initialise status */
\r
188 ZeroMemory(&service_status, sizeof(service_status));
\r
189 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
190 service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
191 service_status.dwWin32ExitCode = NO_ERROR;
\r
192 service_status.dwServiceSpecificExitCode = 0;
\r
193 service_status.dwCheckPoint = 0;
\r
194 service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
196 /* Signal we AREN'T running the server */
\r
197 process_handle = 0;
\r
200 /* Register control handler */
\r
201 service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);
\r
202 if (! service_handle) {
\r
203 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
207 log_service_control(service_name, 0, true);
\r
209 service_status.dwCurrentState = SERVICE_START_PENDING;
\r
210 service_status.dwWaitHint = throttle_delay + NSSM_WAITHINT_MARGIN;
\r
211 SetServiceStatus(service_handle, &service_status);
\r
214 /* Try to create the exit action parameters; we don't care if it fails */
\r
215 create_exit_action(argv[0], exit_action_strings[0]);
\r
217 set_service_recovery(0, service_name);
\r
220 /* Used for signalling a resume if the service pauses when throttled. */
\r
221 throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
222 if (! throttle_timer) {
\r
223 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0);
\r
229 /* Make sure service recovery actions are taken where necessary */
\r
230 void set_service_recovery(SC_HANDLE service, char *service_name) {
\r
231 SC_HANDLE services = 0;
\r
234 services = open_service_manager();
\r
235 if (! services) return;
\r
237 service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
\r
238 if (! service) return;
\r
241 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
242 ZeroMemory(&flag, sizeof(flag));
\r
243 flag.fFailureActionsOnNonCrashFailures = true;
\r
245 /* This functionality was added in Vista so the call may fail */
\r
246 if (! ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
247 unsigned long error = GetLastError();
\r
248 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
249 if (error != ERROR_INVALID_LEVEL) {
\r
250 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CHANGESERVICECONFIG2_FAILED, service_name, error_string(error), 0);
\r
255 CloseServiceHandle(service);
\r
256 CloseServiceHandle(services);
\r
260 int monitor_service() {
\r
261 /* Set service status to started */
\r
262 int ret = start_service();
\r
265 _snprintf_s(code, sizeof(code), _TRUNCATE, "%d", ret);
\r
266 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
\r
269 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
\r
271 /* Monitor service service */
\r
272 if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
273 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0);
\r
279 char *service_control_text(unsigned long control) {
\r
281 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
282 case 0: return "START";
\r
283 case SERVICE_CONTROL_STOP: return "STOP";
\r
284 case SERVICE_CONTROL_SHUTDOWN: return "SHUTDOWN";
\r
285 case SERVICE_CONTROL_PAUSE: return "PAUSE";
\r
286 case SERVICE_CONTROL_CONTINUE: return "CONTINUE";
\r
287 case SERVICE_CONTROL_INTERROGATE: return "INTERROGATE";
\r
292 void log_service_control(char *service_name, unsigned long control, bool handled) {
\r
293 char *text = service_control_text(control);
\r
294 unsigned long event;
\r
297 /* "0x" + 8 x hex + NULL */
\r
298 text = (char *) HeapAlloc(GetProcessHeap(), 0, 11);
\r
300 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0);
\r
303 if (_snprintf_s(text, 11, _TRUNCATE, "0x%08x", control) < 0) {
\r
304 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0);
\r
305 HeapFree(GetProcessHeap(), 0, text);
\r
309 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
311 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
312 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
314 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
316 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
317 HeapFree(GetProcessHeap(), 0, text);
\r
321 /* Service control handler */
\r
322 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
324 case SERVICE_CONTROL_INTERROGATE:
\r
325 /* We always keep the service status up-to-date so this is a no-op. */
\r
328 case SERVICE_CONTROL_SHUTDOWN:
\r
329 case SERVICE_CONTROL_STOP:
\r
330 log_service_control(service_name, control, true);
\r
331 stop_service(0, true, true);
\r
334 case SERVICE_CONTROL_CONTINUE:
\r
335 log_service_control(service_name, control, true);
\r
336 if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
338 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
339 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
340 service_status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
341 service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN;
\r
342 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0);
\r
343 SetServiceStatus(service_handle, &service_status);
\r
346 case SERVICE_CONTROL_PAUSE:
\r
348 We don't accept pause messages but it isn't possible to register
\r
349 only for continue messages so we have to handle this case.
\r
351 log_service_control(service_name, control, false);
\r
352 return ERROR_CALL_NOT_IMPLEMENTED;
\r
355 /* Unknown control */
\r
356 log_service_control(service_name, control, false);
\r
357 return ERROR_CALL_NOT_IMPLEMENTED;
\r
360 /* Start the service */
\r
361 int start_service() {
\r
363 allow_restart = true;
\r
365 if (process_handle) return 0;
\r
367 /* Allocate a STARTUPINFO structure for a new process */
\r
369 ZeroMemory(&si, sizeof(si));
\r
370 si.cb = sizeof(si);
\r
372 /* Allocate a PROCESSINFO structure for the process */
\r
373 PROCESS_INFORMATION pi;
\r
374 ZeroMemory(&pi, sizeof(pi));
\r
376 /* Get startup parameters */
\r
378 int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay, &stop_method, &si);
\r
380 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
\r
381 return stop_service(2, true, true);
\r
384 /* Launch executable with arguments */
\r
385 char cmd[CMD_LENGTH];
\r
386 if (_snprintf_s(cmd, sizeof(cmd), _TRUNCATE, "\"%s\" %s", exe, flags) < 0) {
\r
387 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
\r
388 close_output_handles(&si);
\r
389 return stop_service(2, true, true);
\r
392 throttle_restart();
\r
394 bool inherit_handles = false;
\r
395 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
396 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, 0, env, dir, &si, &pi)) {
\r
397 unsigned long error = GetLastError();
\r
398 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
399 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0);
\r
400 close_output_handles(&si);
\r
401 return stop_service(3, true, true);
\r
403 process_handle = pi.hProcess;
\r
404 pid = pi.dwProcessId;
\r
406 if (get_process_creation_time(process_handle, &creation_time)) ZeroMemory(&creation_time, sizeof(creation_time));
\r
408 close_output_handles(&si);
\r
410 /* Wait for a clean startup. */
\r
411 if (WaitForSingleObject(process_handle, throttle_delay) == WAIT_TIMEOUT) throttle = 0;
\r
413 /* Signal successful start */
\r
414 service_status.dwCurrentState = SERVICE_RUNNING;
\r
415 SetServiceStatus(service_handle, &service_status);
\r
420 /* Stop the service */
\r
421 int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
\r
422 allow_restart = false;
\r
423 if (wait_handle) UnregisterWait(wait_handle);
\r
425 if (default_action && ! exitcode && ! graceful) {
\r
426 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
430 /* Signal we are stopping */
\r
432 service_status.dwCurrentState = SERVICE_STOP_PENDING;
\r
433 service_status.dwWaitHint = NSSM_KILL_WINDOW_GRACE_PERIOD + NSSM_KILL_THREADS_GRACE_PERIOD + NSSM_WAITHINT_MARGIN;
\r
434 SetServiceStatus(service_handle, &service_status);
\r
437 /* Nothing to do if service isn't running */
\r
439 /* Shut down service */
\r
440 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
\r
441 kill_process(service_name, stop_method, process_handle, pid, 0);
\r
443 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
\r
445 end_service((void *) pid, true);
\r
447 /* Signal we stopped */
\r
449 service_status.dwCurrentState = SERVICE_STOPPED;
\r
451 service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
452 service_status.dwServiceSpecificExitCode = exitcode;
\r
455 service_status.dwWin32ExitCode = NO_ERROR;
\r
456 service_status.dwServiceSpecificExitCode = 0;
\r
458 SetServiceStatus(service_handle, &service_status);
\r
464 /* Callback function triggered when the server exits */
\r
465 void CALLBACK end_service(void *arg, unsigned char why) {
\r
466 if (stopping) return;
\r
470 pid = (unsigned long) arg;
\r
472 /* Check exit code */
\r
473 unsigned long exitcode = 0;
\r
475 FILETIME exit_time;
\r
476 GetExitCodeProcess(process_handle, &exitcode);
\r
477 if (exitcode == STILL_ACTIVE || get_process_exit_time(process_handle, &exit_time)) GetSystemTimeAsFileTime(&exit_time);
\r
478 CloseHandle(process_handle);
\r
481 Log that the service ended BEFORE logging about killing the process
\r
482 tree. See below for the possible values of the why argument.
\r
485 _snprintf_s(code, sizeof(code), _TRUNCATE, "%d", exitcode);
\r
486 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
\r
490 kill_process_tree(service_name, stop_method, pid, exitcode, pid, &creation_time, &exit_time);
\r
493 The why argument is true if our wait timed out or false otherwise.
\r
494 Our wait is infinite so why will never be true when called by the system.
\r
495 If it is indeed true, assume we were called from stop_service() because
\r
496 this is a controlled shutdown, and don't take any restart action.
\r
499 if (! allow_restart) return;
\r
501 /* What action should we take? */
\r
502 int action = NSSM_EXIT_RESTART;
\r
503 unsigned char action_string[ACTION_LEN];
\r
504 bool default_action;
\r
505 if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {
\r
506 for (int i = 0; exit_action_strings[i]; i++) {
\r
507 if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
514 process_handle = 0;
\r
517 /* Try to restart the service or return failure code to service manager */
\r
518 case NSSM_EXIT_RESTART:
\r
519 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
\r
520 while (monitor_service()) {
\r
521 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
\r
526 /* Do nothing, just like srvany would */
\r
527 case NSSM_EXIT_IGNORE:
\r
528 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
\r
532 /* Tell the service manager we are finished */
\r
533 case NSSM_EXIT_REALLY:
\r
534 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
\r
535 stop_service(exitcode, true, default_action);
\r
538 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
539 case NSSM_EXIT_UNCLEAN:
\r
540 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
\r
541 exit(stop_service(exitcode, false, default_action));
\r
546 void throttle_restart() {
\r
547 /* This can't be a restart if the service is already running. */
\r
548 if (! throttle++) return;
\r
550 int ms = throttle_milliseconds();
\r
552 if (throttle > 7) throttle = 8;
\r
554 char threshold[8], milliseconds[8];
\r
555 _snprintf_s(threshold, sizeof(threshold), _TRUNCATE, "%d", throttle_delay);
\r
556 _snprintf_s(milliseconds, sizeof(milliseconds), _TRUNCATE, "%d", ms);
\r
557 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0);
\r
559 if (throttle_timer) {
\r
560 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
561 throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
562 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
565 service_status.dwCurrentState = SERVICE_PAUSED;
\r
566 SetServiceStatus(service_handle, &service_status);
\r
568 if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE);
\r