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
14 unsigned long throttle_delay;
\r
15 unsigned long stop_method;
\r
16 HANDLE throttle_timer;
\r
17 LARGE_INTEGER throttle_duetime;
\r
18 FILETIME creation_time;
\r
20 static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions;
\r
21 static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 };
\r
23 static unsigned long throttle;
\r
25 static inline int throttle_milliseconds() {
\r
26 /* pow() operates on doubles. */
\r
27 int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
31 /* Connect to the service manager */
\r
32 SC_HANDLE open_service_manager() {
\r
33 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
\r
35 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
42 /* About to install the service */
\r
43 int pre_install_service(int argc, char **argv) {
\r
44 /* Show the dialogue box if we didn't give the service name and path */
\r
45 if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]);
\r
47 /* Arguments are optional */
\r
49 size_t flagslen = 0;
\r
52 for (i = 2; i < argc; i++) flagslen += strlen(argv[i]) + 1;
\r
53 if (! flagslen) flagslen = 1;
\r
55 flags = (char *) HeapAlloc(GetProcessHeap(), 0, flagslen);
\r
57 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "flags", "pre_install_service()", 0);
\r
60 ZeroMemory(flags, flagslen);
\r
63 This probably isn't UTF8-safe and should use std::string or something
\r
64 but it's been broken for the best part of a decade and due for a rewrite
\r
65 anyway so it'll do as a quick-'n'-dirty fix. Note that we don't free
\r
66 the flags buffer but as the program exits that isn't a big problem.
\r
68 for (i = 2; i < argc; i++) {
\r
69 size_t len = strlen(argv[i]);
\r
70 memmove(flags + s, argv[i], len);
\r
72 if (i < argc - 1) flags[s++] = ' ';
\r
75 return install_service(argv[0], argv[1], flags);
\r
78 /* About to remove the service */
\r
79 int pre_remove_service(int argc, char **argv) {
\r
80 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
81 if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]);
\r
82 if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]);
\r
83 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
87 /* Install the service */
\r
88 int install_service(char *name, char *exe, char *flags) {
\r
89 /* Open service manager */
\r
90 SC_HANDLE services = open_service_manager();
\r
92 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
96 /* Get path of this program */
\r
97 char path[MAX_PATH];
\r
98 GetModuleFileName(0, path, MAX_PATH);
\r
100 /* Construct command */
\r
101 char command[CMD_LENGTH];
\r
102 size_t pathlen = strlen(path);
\r
103 if (pathlen + 1 >= VALUE_LENGTH) {
\r
104 print_message(stderr, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);
\r
107 if (_snprintf(command, sizeof(command), "\"%s\"", path) < 0) {
\r
108 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY_FOR_IMAGEPATH);
\r
112 /* Work out directory name */
\r
113 size_t len = strlen(exe);
\r
115 for (i = len; i && exe[i] != '\\' && exe[i] != '/'; i--);
\r
116 char dir[MAX_PATH];
\r
117 memmove(dir, exe, i);
\r
120 /* Create the service */
\r
121 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
123 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED);
\r
124 CloseServiceHandle(services);
\r
128 /* Now we need to put the parameters into the registry */
\r
129 if (create_parameters(name, exe, flags, dir)) {
\r
130 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
131 DeleteService(service);
\r
132 CloseServiceHandle(services);
\r
136 set_service_recovery(service, name);
\r
139 CloseServiceHandle(service);
\r
140 CloseServiceHandle(services);
\r
142 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, name);
\r
146 /* Remove the service */
\r
147 int remove_service(char *name) {
\r
148 /* Open service manager */
\r
149 SC_HANDLE services = open_service_manager();
\r
151 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
155 /* Try to open the service */
\r
156 SC_HANDLE service = OpenService(services, name, SC_MANAGER_ALL_ACCESS);
\r
158 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
\r
159 CloseServiceHandle(services);
\r
163 /* Try to delete the service */
\r
164 if (! DeleteService(service)) {
\r
165 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
166 CloseServiceHandle(service);
\r
167 CloseServiceHandle(services);
\r
172 CloseServiceHandle(service);
\r
173 CloseServiceHandle(services);
\r
175 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, name);
\r
179 /* Service initialisation */
\r
180 void WINAPI service_main(unsigned long argc, char **argv) {
\r
181 if (_snprintf(service_name, sizeof(service_name), "%s", argv[0]) < 0) {
\r
182 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service_name", "service_main()", 0);
\r
186 /* Initialise status */
\r
187 ZeroMemory(&service_status, sizeof(service_status));
\r
188 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
189 service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
190 service_status.dwWin32ExitCode = NO_ERROR;
\r
191 service_status.dwServiceSpecificExitCode = 0;
\r
192 service_status.dwCheckPoint = 0;
\r
193 service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
195 /* Signal we AREN'T running the server */
\r
196 process_handle = 0;
\r
199 /* Register control handler */
\r
200 service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);
\r
201 if (! service_handle) {
\r
202 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
206 log_service_control(service_name, 0, true);
\r
208 service_status.dwCurrentState = SERVICE_START_PENDING;
\r
209 service_status.dwWaitHint = throttle_delay + NSSM_WAITHINT_MARGIN;
\r
210 SetServiceStatus(service_handle, &service_status);
\r
213 /* Try to create the exit action parameters; we don't care if it fails */
\r
214 create_exit_action(argv[0], exit_action_strings[0]);
\r
216 set_service_recovery(0, service_name);
\r
219 /* Used for signalling a resume if the service pauses when throttled. */
\r
220 throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
221 if (! throttle_timer) {
\r
222 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0);
\r
228 /* Make sure service recovery actions are taken where necessary */
\r
229 void set_service_recovery(SC_HANDLE service, char *service_name) {
\r
230 SC_HANDLE services = 0;
\r
233 services = open_service_manager();
\r
234 if (! services) return;
\r
236 service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
\r
237 if (! service) return;
\r
240 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
241 ZeroMemory(&flag, sizeof(flag));
\r
242 flag.fFailureActionsOnNonCrashFailures = true;
\r
244 /* This functionality was added in Vista so the call may fail */
\r
245 if (! ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
246 unsigned long error = GetLastError();
\r
247 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
248 if (error != ERROR_INVALID_LEVEL) {
\r
249 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CHANGESERVICECONFIG2_FAILED, service_name, error_string(error), 0);
\r
254 CloseServiceHandle(service);
\r
255 CloseServiceHandle(services);
\r
259 int monitor_service() {
\r
260 /* Set service status to started */
\r
261 int ret = start_service();
\r
264 _snprintf(code, sizeof(code), "%d", ret);
\r
265 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
\r
268 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
\r
270 /* Monitor service service */
\r
271 if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
272 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0);
\r
278 char *service_control_text(unsigned long control) {
\r
280 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
281 case 0: return "START";
\r
282 case SERVICE_CONTROL_STOP: return "STOP";
\r
283 case SERVICE_CONTROL_SHUTDOWN: return "SHUTDOWN";
\r
284 case SERVICE_CONTROL_PAUSE: return "PAUSE";
\r
285 case SERVICE_CONTROL_CONTINUE: return "CONTINUE";
\r
286 case SERVICE_CONTROL_INTERROGATE: return "INTERROGATE";
\r
291 void log_service_control(char *service_name, unsigned long control, bool handled) {
\r
292 char *text = service_control_text(control);
\r
293 unsigned long event;
\r
296 /* "0x" + 8 x hex + NULL */
\r
297 text = (char *) HeapAlloc(GetProcessHeap(), 0, 11);
\r
299 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0);
\r
302 if (_snprintf(text, 11, "0x%08x", control) < 0) {
\r
303 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0);
\r
304 HeapFree(GetProcessHeap(), 0, text);
\r
308 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
310 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
311 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
313 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
315 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
316 HeapFree(GetProcessHeap(), 0, text);
\r
320 /* Service control handler */
\r
321 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
323 case SERVICE_CONTROL_INTERROGATE:
\r
324 /* We always keep the service status up-to-date so this is a no-op. */
\r
327 case SERVICE_CONTROL_SHUTDOWN:
\r
328 case SERVICE_CONTROL_STOP:
\r
329 log_service_control(service_name, control, true);
\r
330 stop_service(0, true, true);
\r
333 case SERVICE_CONTROL_CONTINUE:
\r
334 log_service_control(service_name, control, true);
\r
335 if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
337 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
338 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
339 service_status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
340 service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN;
\r
341 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0);
\r
342 SetServiceStatus(service_handle, &service_status);
\r
345 case SERVICE_CONTROL_PAUSE:
\r
347 We don't accept pause messages but it isn't possible to register
\r
348 only for continue messages so we have to handle this case.
\r
350 log_service_control(service_name, control, false);
\r
351 return ERROR_CALL_NOT_IMPLEMENTED;
\r
354 /* Unknown control */
\r
355 log_service_control(service_name, control, false);
\r
356 return ERROR_CALL_NOT_IMPLEMENTED;
\r
359 /* Start the service */
\r
360 int start_service() {
\r
363 if (process_handle) return 0;
\r
365 /* Allocate a STARTUPINFO structure for a new process */
\r
367 ZeroMemory(&si, sizeof(si));
\r
368 si.cb = sizeof(si);
\r
370 /* Allocate a PROCESSINFO structure for the process */
\r
371 PROCESS_INFORMATION pi;
\r
372 ZeroMemory(&pi, sizeof(pi));
\r
374 /* Get startup parameters */
\r
376 int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay, &stop_method, &si);
\r
378 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
\r
379 return stop_service(2, true, true);
\r
382 /* Launch executable with arguments */
\r
383 char cmd[CMD_LENGTH];
\r
384 if (_snprintf(cmd, sizeof(cmd), "\"%s\" %s", exe, flags) < 0) {
\r
385 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
\r
386 close_output_handles(&si);
\r
387 return stop_service(2, true, true);
\r
390 throttle_restart();
\r
392 bool inherit_handles = (si.dwFlags & STARTF_USESTDHANDLES);
\r
393 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, 0, env, dir, &si, &pi)) {
\r
394 unsigned long error = GetLastError();
\r
395 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
396 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0);
\r
397 close_output_handles(&si);
\r
398 return stop_service(3, true, true);
\r
400 process_handle = pi.hProcess;
\r
401 pid = pi.dwProcessId;
\r
403 if (get_process_creation_time(process_handle, &creation_time)) ZeroMemory(&creation_time, sizeof(creation_time));
\r
405 close_output_handles(&si);
\r
407 /* Signal successful start */
\r
408 service_status.dwCurrentState = SERVICE_RUNNING;
\r
409 SetServiceStatus(service_handle, &service_status);
\r
411 /* Wait for a clean startup. */
\r
412 if (WaitForSingleObject(process_handle, throttle_delay) == WAIT_TIMEOUT) throttle = 0;
\r
417 /* Stop the service */
\r
418 int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
\r
419 if (default_action && ! exitcode && ! graceful) {
\r
420 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
424 /* Signal we are stopping */
\r
426 service_status.dwCurrentState = SERVICE_STOP_PENDING;
\r
427 service_status.dwWaitHint = NSSM_KILL_WINDOW_GRACE_PERIOD + NSSM_KILL_THREADS_GRACE_PERIOD + NSSM_WAITHINT_MARGIN;
\r
428 SetServiceStatus(service_handle, &service_status);
\r
431 /* Nothing to do if service isn't running */
\r
433 /* Shut down service */
\r
434 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
\r
435 kill_process(service_name, stop_method, process_handle, pid, 0);
\r
437 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
\r
439 end_service((void *) pid, true);
\r
441 /* Signal we stopped */
\r
443 service_status.dwCurrentState = SERVICE_STOPPED;
\r
445 service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
446 service_status.dwServiceSpecificExitCode = exitcode;
\r
449 service_status.dwWin32ExitCode = NO_ERROR;
\r
450 service_status.dwServiceSpecificExitCode = 0;
\r
452 SetServiceStatus(service_handle, &service_status);
\r
458 /* Callback function triggered when the server exits */
\r
459 void CALLBACK end_service(void *arg, unsigned char why) {
\r
460 if (stopping) return;
\r
464 pid = (unsigned long) arg;
\r
466 /* Check exit code */
\r
467 unsigned long exitcode = 0;
\r
469 FILETIME exit_time;
\r
470 GetExitCodeProcess(process_handle, &exitcode);
\r
471 if (exitcode == STILL_ACTIVE || get_process_exit_time(process_handle, &exit_time)) GetSystemTimeAsFileTime(&exit_time);
\r
472 CloseHandle(process_handle);
\r
475 Log that the service ended BEFORE logging about killing the process
\r
476 tree. See below for the possible values of the why argument.
\r
479 _snprintf(code, sizeof(code), "%d", exitcode);
\r
480 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
\r
484 kill_process_tree(service_name, stop_method, pid, exitcode, pid, &creation_time, &exit_time);
\r
487 The why argument is true if our wait timed out or false otherwise.
\r
488 Our wait is infinite so why will never be true when called by the system.
\r
489 If it is indeed true, assume we were called from stop_service() because
\r
490 this is a controlled shutdown, and don't take any restart action.
\r
494 /* What action should we take? */
\r
495 int action = NSSM_EXIT_RESTART;
\r
496 unsigned char action_string[ACTION_LEN];
\r
497 bool default_action;
\r
498 if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {
\r
499 for (int i = 0; exit_action_strings[i]; i++) {
\r
500 if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
507 process_handle = 0;
\r
510 /* Try to restart the service or return failure code to service manager */
\r
511 case NSSM_EXIT_RESTART:
\r
512 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
\r
513 while (monitor_service()) {
\r
514 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
\r
519 /* Do nothing, just like srvany would */
\r
520 case NSSM_EXIT_IGNORE:
\r
521 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
\r
525 /* Tell the service manager we are finished */
\r
526 case NSSM_EXIT_REALLY:
\r
527 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
\r
528 stop_service(exitcode, true, default_action);
\r
531 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
532 case NSSM_EXIT_UNCLEAN:
\r
533 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
\r
534 exit(stop_service(exitcode, false, default_action));
\r
539 void throttle_restart() {
\r
540 /* This can't be a restart if the service is already running. */
\r
541 if (! throttle++) return;
\r
543 int ms = throttle_milliseconds();
\r
545 if (throttle > 7) throttle = 8;
\r
547 char threshold[8], milliseconds[8];
\r
548 _snprintf(threshold, sizeof(threshold), "%d", throttle_delay);
\r
549 _snprintf(milliseconds, sizeof(milliseconds), "%d", ms);
\r
550 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0);
\r
552 if (throttle_timer) {
\r
553 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
554 throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
555 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
558 service_status.dwCurrentState = SERVICE_PAUSED;
\r
559 SetServiceStatus(service_handle, &service_status);
\r
561 if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE);
\r