X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fservice.c;h=85175793428f77b8d7ac8261a40dc11490329e82;hp=e4dfa40314afb7cd0e8b8e5d39d13e11ababaf63;hb=430c18ed7f576fd9041b0a02e7c4210bdd020a25;hpb=521e05c7cec9148ed046a1f2f525c3395eb9092a diff --git a/src/service.c b/src/service.c index e4dfa4031..851757934 100644 --- a/src/service.c +++ b/src/service.c @@ -34,6 +34,7 @@ #include "dbus-service.h" #include "special.h" #include "bus-errors.h" +#include "exit-status.h" #define COMMENTS "#;\n" #define NEWLINES "\n\r" @@ -117,8 +118,11 @@ static void service_init(Unit *u) { s->sysv_start_priority = -1; #endif s->socket_fd = -1; + s->guess_main_pid = true; exec_context_init(&s->exec_context); + s->exec_context.std_output = u->meta.manager->default_std_output; + s->exec_context.std_error = u->meta.manager->default_std_error; RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5); @@ -210,6 +214,7 @@ static void service_done(Unit *u) { exec_context_done(&s->exec_context); exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX); s->control_command = NULL; + s->main_command = NULL; /* This will leak a process, but at least no memory or any of * our resources */ @@ -330,7 +335,7 @@ static int sysv_translate_facility(const char *name, const char *filename, char /* Facilities starting with $ are most likely targets */ r = unit_name_build(n, NULL, ".target"); } else if (filename && streq(name, filename)) - /* Names equalling the file name of the services are redundant */ + /* Names equaling the file name of the services are redundant */ return 0; else /* Everything else we assume to be normal service names */ @@ -814,7 +819,7 @@ static int service_load_sysv_path(Service *s, const char *path) { s->restart = SERVICE_RESTART_NO; s->exec_context.std_output = (s->meta.manager->sysv_console || s->exec_context.std_input == EXEC_INPUT_TTY) - ? EXEC_OUTPUT_TTY : EXEC_OUTPUT_NULL; + ? EXEC_OUTPUT_TTY : s->meta.manager->default_std_output; s->exec_context.kill_mode = KILL_PROCESS_GROUP; /* We use the long description only if @@ -1028,6 +1033,12 @@ static int service_verify(Service *s) { return -EINVAL; } + if (s->type == SERVICE_ONESHOT && + s->exec_command[SERVICE_EXEC_RELOAD]) { + log_error("%s has an ExecReload setting, which is not allowed for Type=oneshot services. Refusing.", s->meta.id); + return -EINVAL; + } + if (s->type == SERVICE_DBUS && !s->bus_name) { log_error("%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", s->meta.id); return -EINVAL; @@ -1143,6 +1154,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { "%sPermissionsStartOnly: %s\n" "%sRootDirectoryStartOnly: %s\n" "%sRemainAfterExit: %s\n" + "%sGuessMainPID: %s\n" "%sType: %s\n" "%sRestart: %s\n" "%sNotifyAccess: %s\n", @@ -1150,6 +1162,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { prefix, yes_no(s->permissions_start_only), prefix, yes_no(s->root_directory_start_only), prefix, yes_no(s->remain_after_exit), + prefix, yes_no(s->guess_main_pid), prefix, service_type_to_string(s->type), prefix, service_restart_to_string(s->restart), prefix, notify_access_to_string(s->notify_access)); @@ -1228,11 +1241,6 @@ static int service_load_pid_file(Service *s) { assert(s); - if (s->main_pid_known) - return 0; - - assert(s->main_pid <= 0); - if (!s->pid_file) return -ENOENT; @@ -1267,9 +1275,14 @@ static int service_search_main_pid(Service *s) { assert(s); + /* If we know it anyway, don't ever fallback to unreliable + * heuristics */ if (s->main_pid_known) return 0; + if (!s->guess_main_pid) + return 0; + assert(s->main_pid <= 0); if ((pid = cgroup_bonding_search_main_pid_list(s->meta.cgroup_bondings)) <= 0) @@ -1393,8 +1406,10 @@ static void service_set_state(Service *s, ServiceState state) { state != SERVICE_RELOAD && state != SERVICE_STOP && state != SERVICE_STOP_SIGTERM && - state != SERVICE_STOP_SIGKILL) + state != SERVICE_STOP_SIGKILL) { service_unwatch_main_pid(s); + s->main_command = NULL; + } if (state != SERVICE_START_PRE && state != SERVICE_START && @@ -1446,7 +1461,8 @@ static void service_set_state(Service *s, ServiceState state) { if (old_state != state) log_debug("%s changed %s -> %s", s->meta.id, service_state_to_string(old_state), service_state_to_string(state)); - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]); + unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], !s->reload_failure); + s->reload_failure = false; } static int service_coldplug(Unit *u) { @@ -1774,8 +1790,9 @@ static void service_enter_stop_post(Service *s, bool success) { service_unwatch_control_pid(s); - s->control_command_id = SERVICE_EXEC_STOP_POST; if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST])) { + s->control_command_id = SERVICE_EXEC_STOP_POST; + if ((r = service_spawn(s, s->control_command, true, @@ -1813,9 +1830,9 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) { int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL; if (s->main_pid > 0) { - if (kill(s->exec_context.kill_mode == KILL_PROCESS_GROUP ? - -s->main_pid : - s->main_pid, sig) < 0 && errno != ESRCH) + if (kill_and_sigcont(s->exec_context.kill_mode == KILL_PROCESS_GROUP ? + -s->main_pid : + s->main_pid, sig) < 0 && errno != ESRCH) log_warning("Failed to kill main process %li: %m", (long) s->main_pid); else @@ -1823,9 +1840,9 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) { } if (s->control_pid > 0) { - if (kill(s->exec_context.kill_mode == KILL_PROCESS_GROUP ? - -s->control_pid : - s->control_pid, sig) < 0 && errno != ESRCH) + if (kill_and_sigcont(s->exec_context.kill_mode == KILL_PROCESS_GROUP ? + -s->control_pid : + s->control_pid, sig) < 0 && errno != ESRCH) log_warning("Failed to kill control process %li: %m", (long) s->control_pid); else @@ -1848,7 +1865,7 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) { if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) goto fail; - if ((r = cgroup_bonding_kill_list(s->meta.cgroup_bondings, sig, pid_set)) < 0) { + if ((r = cgroup_bonding_kill_list(s->meta.cgroup_bondings, sig, true, pid_set)) < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) log_warning("Failed to kill control group: %s", strerror(-r)); } else if (r > 0) @@ -1893,8 +1910,9 @@ static void service_enter_stop(Service *s, bool success) { service_unwatch_control_pid(s); - s->control_command_id = SERVICE_EXEC_STOP; if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP])) { + s->control_command_id = SERVICE_EXEC_STOP; + if ((r = service_spawn(s, s->control_command, true, @@ -1942,8 +1960,9 @@ static void service_enter_start_post(Service *s) { service_unwatch_control_pid(s); - s->control_command_id = SERVICE_EXEC_START_POST; if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) { + s->control_command_id = SERVICE_EXEC_START_POST; + if ((r = service_spawn(s, s->control_command, true, @@ -1969,6 +1988,7 @@ fail: static void service_enter_start(Service *s) { pid_t pid; int r; + ExecCommand *c; assert(s); @@ -1980,11 +2000,20 @@ static void service_enter_start(Service *s) { else service_unwatch_main_pid(s); - s->control_command_id = SERVICE_EXEC_START; - s->control_command = s->exec_command[SERVICE_EXEC_START]; + if (s->type == SERVICE_FORKING) { + s->control_command_id = SERVICE_EXEC_START; + c = s->control_command = s->exec_command[SERVICE_EXEC_START]; + + s->main_command = NULL; + } else { + s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; + s->control_command = NULL; + + c = s->main_command = s->exec_command[SERVICE_EXEC_START]; + } if ((r = service_spawn(s, - s->control_command, + c, s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY, true, true, @@ -2039,8 +2068,9 @@ static void service_enter_start_pre(Service *s) { service_unwatch_control_pid(s); - s->control_command_id = SERVICE_EXEC_START_PRE; if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE])) { + s->control_command_id = SERVICE_EXEC_START_PRE; + if ((r = service_spawn(s, s->control_command, true, @@ -2099,8 +2129,9 @@ static void service_enter_reload(Service *s) { service_unwatch_control_pid(s); - s->control_command_id = SERVICE_EXEC_RELOAD; if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD])) { + s->control_command_id = SERVICE_EXEC_RELOAD; + if ((r = service_spawn(s, s->control_command, true, @@ -2120,7 +2151,8 @@ static void service_enter_reload(Service *s) { fail: log_warning("%s failed to run 'reload' task: %s", s->meta.id, strerror(-r)); - service_enter_stop(s, false); + s->reload_failure = true; + service_enter_running(s, true); } static void service_run_next_control(Service *s, bool success) { @@ -2161,7 +2193,10 @@ fail: service_enter_signal(s, SERVICE_STOP_SIGTERM, false); else if (s->state == SERVICE_STOP_POST) service_enter_dead(s, false, true); - else + else if (s->state == SERVICE_RELOAD) { + s->reload_failure = true; + service_enter_running(s, true); + } else service_enter_stop(s, false); } @@ -2170,20 +2205,18 @@ static void service_run_next_main(Service *s, bool success) { int r; assert(s); - assert(s->control_command); - assert(s->control_command->command_next); + assert(s->main_command); + assert(s->main_command->command_next); + assert(s->type == SERVICE_ONESHOT); if (!success) s->failure = true; - assert(s->control_command_id == SERVICE_EXEC_START); - assert(s->type == SERVICE_ONESHOT); - - s->control_command = s->control_command->command_next; + s->main_command = s->main_command->command_next; service_unwatch_main_pid(s); if ((r = service_spawn(s, - s->control_command, + s->main_command, false, true, true, @@ -2463,16 +2496,26 @@ static const char *service_sub_state_to_string(Unit *u) { return service_state_to_string(SERVICE(u)->state); } -#ifdef HAVE_SYSV_COMPAT static bool service_check_gc(Unit *u) { Service *s = SERVICE(u); assert(s); - return !!s->sysv_path; -} + /* Never clean up services that still have a process around, + * even if the service is formally dead. */ + if (cgroup_good(s) > 0 || + main_pid_good(s) > 0 || + control_pid_good(s) > 0) + return true; + +#ifdef HAVE_SYSV_COMPAT + if (s->sysv_path) + return true; #endif + return false; +} + static bool service_check_snapshot(Unit *u) { Service *s = SERVICE(u); @@ -2498,10 +2541,14 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->main_pid = 0; exec_status_exit(&s->main_exec_status, pid, code, status, s->exec_context.utmp_id); - if (s->type != SERVICE_FORKING && s->control_command) { - s->control_command->exec_status = s->main_exec_status; + /* If this is not a forking service than the main + * process got started and hence we copy the exit + * status so that it is recorded both as main and as + * control process exit status */ + if (s->main_command) { + s->main_command->exec_status = s->main_exec_status; - if (s->control_command->ignore) + if (s->main_command->ignore) success = true; } @@ -2509,8 +2556,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { "%s: main process exited, code=%s, status=%i", u->meta.id, sigchld_code_to_string(code), status); s->failure = s->failure || !success; - if (s->control_command && - s->control_command->command_next && + if (s->main_command && + s->main_command->command_next && success) { /* There is another command to * @@ -2523,9 +2570,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* The service exited, so the service is officially * gone. */ - - s->control_command = NULL; - s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; + s->main_command = NULL; switch (s->state) { @@ -2644,14 +2689,18 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { log_warning("%s: failed to load PID file %s: %s", s->meta.id, s->pid_file, strerror(-r)); } - /* Fall through */ + s->reload_failure = !success; + service_enter_running(s, true); + break; case SERVICE_RELOAD: - if (success) - service_enter_running(s, true); - else - service_enter_stop(s, false); + if (success) { + service_load_pid_file(s); + service_search_main_pid(s); + } + s->reload_failure = !success; + service_enter_running(s, true); break; case SERVICE_STOP: @@ -2701,23 +2750,34 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { break; case SERVICE_START_POST: - case SERVICE_RELOAD: log_warning("%s operation timed out. Stopping.", u->meta.id); service_enter_stop(s, false); break; + case SERVICE_RELOAD: + log_warning("%s operation timed out. Stopping.", u->meta.id); + s->reload_failure = true; + service_enter_running(s, true); + break; + case SERVICE_STOP: log_warning("%s stopping timed out. Terminating.", u->meta.id); service_enter_signal(s, SERVICE_STOP_SIGTERM, false); break; case SERVICE_STOP_SIGTERM: - log_warning("%s stopping timed out. Killing.", u->meta.id); - service_enter_signal(s, SERVICE_STOP_SIGKILL, false); + if (s->exec_context.send_sigkill) { + log_warning("%s stopping timed out. Killing.", u->meta.id); + service_enter_signal(s, SERVICE_STOP_SIGKILL, false); + } else { + log_warning("%s stopping timed out. Skipping SIGKILL.", u->meta.id); + service_enter_stop_post(s, false); + } + break; case SERVICE_STOP_SIGKILL: - /* Uh, wie sent a SIGKILL and it is still not gone? + /* Uh, we sent a SIGKILL and it is still not gone? * Must be something we cannot kill, so let's just be * weirded out and continue */ @@ -2731,8 +2791,14 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { break; case SERVICE_FINAL_SIGTERM: - log_warning("%s stopping timed out (2). Killing.", u->meta.id); - service_enter_signal(s, SERVICE_FINAL_SIGKILL, false); + if (s->exec_context.send_sigkill) { + log_warning("%s stopping timed out (2). Killing.", u->meta.id); + service_enter_signal(s, SERVICE_FINAL_SIGKILL, false); + } else { + log_warning("%s stopping timed out (2). Skipping SIGKILL. Entering failed mode.", u->meta.id); + service_enter_dead(s, false, true); + } + break; case SERVICE_FINAL_SIGKILL: @@ -2986,7 +3052,7 @@ static int service_enumerate(Manager *m) { /* We honour K links only for halt/reboot. For the normal * runlevels we assume the stop jobs will be implicitly added - * by the core logic. Also, we don't really distuingish here + * by the core logic. Also, we don't really distinguish here * between the runlevels 0 and 6 and just add them to the * special shutdown target. On SUSE the boot.d/ runlevel is * also used for shutdown, so we add links for that too to the @@ -3164,7 +3230,7 @@ static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusErro goto finish; } - if ((q = cgroup_bonding_kill_list(s->meta.cgroup_bondings, signo, pid_set)) < 0) + if ((q = cgroup_bonding_kill_list(s->meta.cgroup_bondings, signo, false, pid_set)) < 0) if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) r = q; } @@ -3261,9 +3327,7 @@ const UnitVTable service_vtable = { .active_state = service_active_state, .sub_state_to_string = service_sub_state_to_string, -#ifdef HAVE_SYSV_COMPAT .check_gc = service_check_gc, -#endif .check_snapshot = service_check_snapshot, .sigchld_event = service_sigchld_event,