X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Fcore%2Fservice.c;h=8dc98813502eaeed39f8bef5288136013d45d084;hb=495e75ed5c8cce933947dae10a4a1b5f8067e432;hp=1b0af40b4113116dc8f86c14c025433c431d4071;hpb=1a1a691ba7763631213a5ddc53e0f799ca2ffb20;p=thirdparty%2Fsystemd.git diff --git a/src/core/service.c b/src/core/service.c index 1b0af40b411..8dc98813502 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -120,18 +120,19 @@ static void service_init(Unit *u) { assert(u); assert(u->load_state == UNIT_STUB); - s->timeout_start_usec = u->manager->default_timeout_start_usec; - s->timeout_stop_usec = u->manager->default_timeout_stop_usec; - s->timeout_abort_usec = u->manager->default_timeout_abort_usec; - s->timeout_abort_set = u->manager->default_timeout_abort_set; - s->restart_usec = u->manager->default_restart_usec; + s->timeout_start_usec = u->manager->defaults.timeout_start_usec; + s->timeout_stop_usec = u->manager->defaults.timeout_stop_usec; + s->timeout_abort_usec = u->manager->defaults.timeout_abort_usec; + s->timeout_abort_set = u->manager->defaults.timeout_abort_set; + s->restart_usec = u->manager->defaults.restart_usec; s->restart_max_delay_usec = USEC_INFINITY; s->runtime_max_usec = USEC_INFINITY; s->type = _SERVICE_TYPE_INVALID; s->socket_fd = -EBADF; s->stdin_fd = s->stdout_fd = s->stderr_fd = -EBADF; s->guess_main_pid = true; - + s->main_pid = PIDREF_NULL; + s->control_pid = PIDREF_NULL; s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; s->exec_context.keyring_mode = MANAGER_IS_SYSTEM(u->manager) ? @@ -151,19 +152,21 @@ static void service_init(Unit *u) { static void service_unwatch_control_pid(Service *s) { assert(s); - if (s->control_pid <= 0) + if (!pidref_is_set(&s->control_pid)) return; - unit_unwatch_pid(UNIT(s), TAKE_PID(s->control_pid)); + unit_unwatch_pidref(UNIT(s), &s->control_pid); + pidref_done(&s->control_pid); } static void service_unwatch_main_pid(Service *s) { assert(s); - if (s->main_pid <= 0) + if (!pidref_is_set(&s->main_pid)) return; - unit_unwatch_pid(UNIT(s), TAKE_PID(s->main_pid)); + unit_unwatch_pidref(UNIT(s), &s->main_pid); + pidref_done(&s->main_pid); } static void service_unwatch_pid_file(Service *s) { @@ -176,33 +179,53 @@ static void service_unwatch_pid_file(Service *s) { s->pid_file_pathspec = mfree(s->pid_file_pathspec); } -static int service_set_main_pid(Service *s, pid_t pid) { +static int service_set_main_pidref(Service *s, PidRef *pidref) { assert(s); - if (pid <= 1) + /* Takes ownership of the specified pidref on success, but not on failure. */ + + if (!pidref_is_set(pidref)) + return -ESRCH; + + if (pidref->pid <= 1) return -EINVAL; - if (pid == getpid_cached()) + if (pidref->pid == getpid_cached()) return -EINVAL; - if (s->main_pid == pid && s->main_pid_known) + if (pidref_equal(&s->main_pid, pidref) && s->main_pid_known) { + pidref_done(pidref); return 0; + } - if (s->main_pid != pid) { + if (!pidref_equal(&s->main_pid, pidref)) { service_unwatch_main_pid(s); - exec_status_start(&s->main_exec_status, pid); + exec_status_start(&s->main_exec_status, pidref->pid); } - s->main_pid = pid; + s->main_pid = TAKE_PIDREF(*pidref); s->main_pid_known = true; - s->main_pid_alien = pid_is_my_child(pid) == 0; + s->main_pid_alien = pid_is_my_child(s->main_pid.pid) == 0; if (s->main_pid_alien) - log_unit_warning(UNIT(s), "Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.", pid); + log_unit_warning(UNIT(s), "Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.", s->main_pid.pid); return 0; } +static int service_set_main_pid(Service *s, pid_t pid) { + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; + int r; + + assert(s); + + r = pidref_set_pid(&pidref, pid); + if (r < 0) + return r; + + return service_set_main_pidref(s, &pidref); +} + void service_release_socket_fd(Service *s) { assert(s); @@ -283,7 +306,6 @@ static void service_start_watchdog(Service *s) { usec_t service_restart_usec_next(Service *s) { unsigned n_restarts_next; - usec_t value; assert(s); @@ -297,26 +319,23 @@ usec_t service_restart_usec_next(Service *s) { s->restart_usec == 0 || s->restart_max_delay_usec == USEC_INFINITY || s->restart_usec >= s->restart_max_delay_usec) - value = s->restart_usec; - else if (n_restarts_next > s->restart_steps) - value = s->restart_max_delay_usec; - else { - /* Enforced in service_verify() and above */ - assert(s->restart_max_delay_usec > s->restart_usec); - - /* r_i / r_0 = (r_n / r_0) ^ (i / n) - * where, - * r_0 : initial restart usec (s->restart_usec), - * r_i : i-th restart usec (value), - * r_n : maximum restart usec (s->restart_max_delay_usec), - * i : index of the next step (n_restarts_next - 1) - * n : num maximum steps (s->restart_steps) */ - value = (usec_t) (s->restart_usec * powl((long double) s->restart_max_delay_usec / s->restart_usec, - (long double) (n_restarts_next - 1) / s->restart_steps)); - } - - log_unit_debug(UNIT(s), "Next restart interval calculated as: %s", FORMAT_TIMESPAN(value, 0)); - return value; + return s->restart_usec; + + if (n_restarts_next > s->restart_steps) + return s->restart_max_delay_usec; + + /* Enforced in service_verify() and above */ + assert(s->restart_max_delay_usec > s->restart_usec); + + /* r_i / r_0 = (r_n / r_0) ^ (i / n) + * where, + * r_0 : initial restart usec (s->restart_usec), + * r_i : i-th restart usec (value), + * r_n : maximum restart usec (s->restart_max_delay_usec), + * i : index of the next step (n_restarts_next - 1) + * n : num maximum steps (s->restart_steps) */ + return (usec_t) (s->restart_usec * powl((long double) s->restart_max_delay_usec / s->restart_usec, + (long double) (n_restarts_next - 1) / s->restart_steps)); } static void service_extend_event_source_timeout(Service *s, sd_event_source *source, usec_t extended) { @@ -446,8 +465,7 @@ static void service_done(Unit *u) { exit_status_set_free(&s->restart_force_status); exit_status_set_free(&s->success_status); - /* This will leak a process, but at least no memory or any of - * our resources */ + /* This will leak a process, but at least no memory or any of our resources */ service_unwatch_main_pid(s); service_unwatch_control_pid(s); service_unwatch_pid_file(s); @@ -602,33 +620,9 @@ static usec_t service_running_timeout(Service *s) { } static int service_arm_timer(Service *s, bool relative, usec_t usec) { - int r; - assert(s); - if (s->timer_event_source) { - r = (relative ? sd_event_source_set_time_relative : sd_event_source_set_time)(s->timer_event_source, usec); - if (r < 0) - return r; - - return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT); - } - - if (usec == USEC_INFINITY) - return 0; - - r = (relative ? sd_event_add_time_relative : sd_event_add_time)( - UNIT(s)->manager->event, - &s->timer_event_source, - CLOCK_MONOTONIC, - usec, 0, - service_dispatch_timer, s); - if (r < 0) - return r; - - (void) sd_event_source_set_description(s->timer_event_source, "service-timer"); - - return 0; + return unit_arm_timer(UNIT(s), &s->timer_event_source, relative, usec, service_dispatch_timer); } static int service_verify(Service *s) { @@ -777,10 +771,10 @@ static void service_fix_stdio(Service *s) { if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT && s->exec_context.std_output == EXEC_OUTPUT_INHERIT) - s->exec_context.std_error = UNIT(s)->manager->default_std_error; + s->exec_context.std_error = UNIT(s)->manager->defaults.std_error; if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT) - s->exec_context.std_output = UNIT(s)->manager->default_std_output; + s->exec_context.std_output = UNIT(s)->manager->defaults.std_output; } static int service_setup_bus_name(Service *s) { @@ -854,7 +848,7 @@ static int service_add_extras(Service *s) { * delegation is on, in that case it we assume the payload knows better what to do and can process * things in a more focused way. */ if (s->oom_policy < 0) - s->oom_policy = s->cgroup_context.delegate ? OOM_CONTINUE : UNIT(s)->manager->default_oom_policy; + s->oom_policy = s->cgroup_context.delegate ? OOM_CONTINUE : UNIT(s)->manager->defaults.oom_policy; /* Let the kernel do the killing if that's requested. */ s->cgroup_context.memory_oom_group = s->oom_policy == OOM_KILL; @@ -964,17 +958,17 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { prefix, oom_policy_to_string(s->oom_policy), prefix, signal_to_string(s->reload_signal)); - if (s->control_pid > 0) + if (pidref_is_set(&s->control_pid)) fprintf(f, "%sControl PID: "PID_FMT"\n", - prefix, s->control_pid); + prefix, s->control_pid.pid); - if (s->main_pid > 0) + if (pidref_is_set(&s->main_pid)) fprintf(f, "%sMain PID: "PID_FMT"\n" "%sMain PID Known: %s\n" "%sMain PID Alien: %s\n", - prefix, s->main_pid, + prefix, s->main_pid.pid, prefix, yes_no(s->main_pid_known), prefix, yes_no(s->main_pid_alien)); @@ -1070,28 +1064,28 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { cgroup_context_dump(UNIT(s), f, prefix); } -static int service_is_suitable_main_pid(Service *s, pid_t pid, int prio) { +static int service_is_suitable_main_pid(Service *s, PidRef *pid, int prio) { Unit *owner; assert(s); - assert(pid_is_valid(pid)); + assert(pidref_is_set(pid)); /* Checks whether the specified PID is suitable as main PID for this service. returns negative if not, 0 if the * PID is questionnable but should be accepted if the source of configuration is trusted. > 0 if the PID is * good */ - if (pid == getpid_cached() || pid == 1) - return log_unit_full_errno(UNIT(s), prio, SYNTHETIC_ERRNO(EPERM), "New main PID "PID_FMT" is the manager, refusing.", pid); + if (pid->pid == getpid_cached() || pid->pid == 1) + return log_unit_full_errno(UNIT(s), prio, SYNTHETIC_ERRNO(EPERM), "New main PID "PID_FMT" is the manager, refusing.", pid->pid); - if (pid == s->control_pid) - return log_unit_full_errno(UNIT(s), prio, SYNTHETIC_ERRNO(EPERM), "New main PID "PID_FMT" is the control process, refusing.", pid); + if (pidref_equal(pid, &s->control_pid)) + return log_unit_full_errno(UNIT(s), prio, SYNTHETIC_ERRNO(EPERM), "New main PID "PID_FMT" is the control process, refusing.", pid->pid); - if (!pid_is_alive(pid)) - return log_unit_full_errno(UNIT(s), prio, SYNTHETIC_ERRNO(ESRCH), "New main PID "PID_FMT" does not exist or is a zombie.", pid); + if (!pid_is_alive(pid->pid)) + return log_unit_full_errno(UNIT(s), prio, SYNTHETIC_ERRNO(ESRCH), "New main PID "PID_FMT" does not exist or is a zombie.", pid->pid); - owner = manager_get_unit_by_pid(UNIT(s)->manager, pid); + owner = manager_get_unit_by_pidref(UNIT(s)->manager, pid); if (owner == UNIT(s)) { - log_unit_debug(UNIT(s), "New main PID "PID_FMT" belongs to service, we are happy.", pid); + log_unit_debug(UNIT(s), "New main PID "PID_FMT" belongs to service, we are happy.", pid->pid); return 1; /* Yay, it's definitely a good PID */ } @@ -1099,11 +1093,11 @@ static int service_is_suitable_main_pid(Service *s, pid_t pid, int prio) { } static int service_load_pid_file(Service *s, bool may_warn) { + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; bool questionable_pid_file = false; _cleanup_free_ char *k = NULL; _cleanup_close_ int fd = -EBADF; int r, prio; - pid_t pid; assert(s); @@ -1133,14 +1127,14 @@ static int service_load_pid_file(Service *s, bool may_warn) { "Can't convert PID files %s O_PATH file descriptor to proper file descriptor: %m", s->pid_file); - r = parse_pid(k, &pid); + r = pidref_set_pidstr(&pidref, k); if (r < 0) return log_unit_full_errno(UNIT(s), prio, r, "Failed to parse PID from file %s: %m", s->pid_file); - if (s->main_pid_known && pid == s->main_pid) + if (s->main_pid_known && pidref_equal(&pidref, &s->main_pid)) return 0; - r = service_is_suitable_main_pid(s, pid, prio); + r = service_is_suitable_main_pid(s, &pidref, prio); if (r < 0) return r; if (r == 0) { @@ -1157,57 +1151,56 @@ static int service_load_pid_file(Service *s, bool may_warn) { if (st.st_uid != 0) return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(EPERM), - "New main PID "PID_FMT" does not belong to service, and PID file is not owned by root. Refusing.", pid); + "New main PID "PID_FMT" does not belong to service, and PID file is not owned by root. Refusing.", pidref.pid); - log_unit_debug(UNIT(s), "New main PID "PID_FMT" does not belong to service, but we'll accept it since PID file is owned by root.", pid); + log_unit_debug(UNIT(s), "New main PID "PID_FMT" does not belong to service, but we'll accept it since PID file is owned by root.", pidref.pid); } if (s->main_pid_known) { - log_unit_debug(UNIT(s), "Main PID changing: "PID_FMT" -> "PID_FMT, s->main_pid, pid); + log_unit_debug(UNIT(s), "Main PID changing: "PID_FMT" -> "PID_FMT, s->main_pid.pid, pidref.pid); service_unwatch_main_pid(s); s->main_pid_known = false; } else - log_unit_debug(UNIT(s), "Main PID loaded: "PID_FMT, pid); + log_unit_debug(UNIT(s), "Main PID loaded: "PID_FMT, pidref.pid); - r = service_set_main_pid(s, pid); + r = service_set_main_pidref(s, &pidref); if (r < 0) return r; - r = unit_watch_pid(UNIT(s), pid, false); + r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false); if (r < 0) /* FIXME: we need to do something here */ - return log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid); + return log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", s->main_pid.pid); return 1; } static void service_search_main_pid(Service *s) { - pid_t pid = 0; + _cleanup_(pidref_done) PidRef pid = PIDREF_NULL; int r; assert(s); - /* If we know it anyway, don't ever fall back to unreliable - * heuristics */ + /* If we know it anyway, don't ever fall back to unreliable heuristics */ if (s->main_pid_known) return; if (!s->guess_main_pid) return; - assert(s->main_pid <= 0); + assert(!pidref_is_set(&s->main_pid)); if (unit_search_main_pid(UNIT(s), &pid) < 0) return; - log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid); - if (service_set_main_pid(s, pid) < 0) + log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid.pid); + if (service_set_main_pidref(s, &pid) < 0) return; - r = unit_watch_pid(UNIT(s), pid, false); + r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false); if (r < 0) /* FIXME: we need to do something here */ - log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" from: %m", pid); + log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" from: %m", s->main_pid.pid); } static void service_set_state(Service *s, ServiceState state) { @@ -1336,28 +1329,28 @@ static int service_coldplug(Unit *u) { if (r < 0) return r; - if (s->main_pid > 0 && - pid_is_unwaited(s->main_pid) && + if (pidref_is_set(&s->main_pid) && + pid_is_unwaited(s->main_pid.pid) && (IN_SET(s->deserialized_state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) { - r = unit_watch_pid(UNIT(s), s->main_pid, false); + r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false); if (r < 0) return r; } - if (s->control_pid > 0 && - pid_is_unwaited(s->control_pid) && + if (pidref_is_set(&s->control_pid) && + pid_is_unwaited(s->control_pid.pid) && IN_SET(s->deserialized_state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_CLEANING)) { - r = unit_watch_pid(UNIT(s), s->control_pid, false); + r = unit_watch_pidref(UNIT(s), &s->control_pid, /* exclusive= */ false); if (r < 0) return r; } @@ -1620,7 +1613,7 @@ static int service_spawn_internal( ExecCommand *c, usec_t timeout, ExecFlags flags, - pid_t *ret_pid) { + PidRef *ret_pid) { _cleanup_(exec_params_clear) ExecParameters exec_params = { .flags = flags, @@ -1631,6 +1624,7 @@ static int service_spawn_internal( }; _cleanup_(sd_event_source_unrefp) sd_event_source *exec_fd_source = NULL; _cleanup_strv_free_ char **final_env = NULL, **our_env = NULL; + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; size_t n_env = 0; pid_t pid; int r; @@ -1699,8 +1693,8 @@ static int service_spawn_internal( return -ENOMEM; } - if (s->main_pid > 0) - if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0) + if (pidref_is_set(&s->main_pid)) + if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid.pid) < 0) return -ENOMEM; if (MANAGER_IS_USER(UNIT(s)->manager)) @@ -1830,12 +1824,15 @@ static int service_spawn_internal( s->exec_fd_event_source = TAKE_PTR(exec_fd_source); s->exec_fd_hot = false; - r = unit_watch_pid(UNIT(s), pid, true); + r = pidref_set_pid(&pidref, pid); if (r < 0) return r; - *ret_pid = pid; + r = unit_watch_pidref(UNIT(s), &pidref, /* exclusive= */ true); + if (r < 0) + return r; + *ret_pid = TAKE_PIDREF(pidref); return 0; } @@ -1844,19 +1841,16 @@ static int main_pid_good(Service *s) { /* Returns 0 if the pid is dead, > 0 if it is good, < 0 if we don't know */ - /* If we know the pid file, then let's just check if it is - * still valid */ + /* If we know the pid file, then let's just check if it is still valid */ if (s->main_pid_known) { - /* If it's an alien child let's check if it is still - * alive ... */ - if (s->main_pid_alien && s->main_pid > 0) - return pid_is_alive(s->main_pid); + /* If it's an alien child let's check if it is still alive ... */ + if (s->main_pid_alien && pidref_is_set(&s->main_pid)) + return pid_is_alive(s->main_pid.pid); - /* .. otherwise assume we'll get a SIGCHLD for it, - * which we really should wait for to collect exit - * status and code */ - return s->main_pid > 0; + /* .. otherwise assume we'll get a SIGCHLD for it, which we really should wait for to collect + * exit status and code */ + return pidref_is_set(&s->main_pid); } /* We don't know the pid */ @@ -1870,7 +1864,7 @@ static int control_pid_good(Service *s) { * make this function as similar as possible to main_pid_good() and cgroup_good(), we pretend that < 0 also * means: we can't figure it out. */ - return s->control_pid > 0; + return pidref_is_set(&s->control_pid); } static int cgroup_good(Service *s) { @@ -1999,6 +1993,8 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) } if (allow_restart) { + usec_t restart_usec_next; + /* We make two state changes here: one that maps to the high-level UNIT_INACTIVE/UNIT_FAILED * state (i.e. a state indicating deactivation), and then one that that maps to the * high-level UNIT_STARTING state (i.e. a state indicating activation). We do this so that @@ -2009,9 +2005,16 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) if (s->restart_mode != SERVICE_RESTART_MODE_DIRECT) service_set_state(s, restart_state); - r = service_arm_timer(s, /* relative= */ true, service_restart_usec_next(s)); - if (r < 0) - goto fail; + restart_usec_next = service_restart_usec_next(s); + + r = service_arm_timer(s, /* relative= */ true, restart_usec_next); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to install restart timer: %m"); + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, /* allow_restart= */ false); + return; + } + + log_unit_debug(UNIT(s), "Next restart interval calculated as: %s", FORMAT_TIMESPAN(restart_usec_next, 0)); service_set_state(s, SERVICE_AUTO_RESTART); } else { @@ -2053,12 +2056,6 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) /* Reset TTY ownership if necessary */ exec_context_revert_tty(&s->exec_context); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run install restart timer: %m"); - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false); } static void service_enter_stop_post(Service *s, ServiceResult f) { @@ -2074,24 +2071,22 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST]; if (s->control_command) { s->control_command_id = SERVICE_EXEC_STOP_POST; + pidref_done(&s->control_pid); r = service_spawn(s, s->control_command, s->timeout_stop_usec, EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_IS_CONTROL|EXEC_SETENV_RESULT|EXEC_CONTROL_CGROUP, &s->control_pid); - if (r < 0) - goto fail; + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'stop-post' task: %m"); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); + return; + } service_set_state(s, SERVICE_STOP_POST); } else service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-post' task: %m"); - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); } static int state_to_kill_operation(Service *s, ServiceState state) { @@ -2138,17 +2133,21 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f UNIT(s), &s->kill_context, kill_operation, - s->main_pid, - s->control_pid, + &s->main_pid, + &s->control_pid, s->main_pid_alien); - if (r < 0) + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); goto fail; + } if (r > 0) { r = service_arm_timer(s, /* relative= */ true, kill_operation == KILL_WATCHDOG ? service_timeout_abort_usec(s) : s->timeout_stop_usec); - if (r < 0) + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to install timer: %m"); goto fail; + } service_set_state(s, state); } else if (IN_SET(state, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM) && s->kill_context.send_sigkill) @@ -2158,25 +2157,30 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f else if (IN_SET(state, SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM) && s->kill_context.send_sigkill) service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS); else - service_enter_dead(s, SERVICE_SUCCESS, true); + service_enter_dead(s, SERVICE_SUCCESS, /* allow_restart= */ true); return; fail: - log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); - if (IN_SET(state, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL)) service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES); else - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, /* allow_restart= */ true); } static void service_enter_stop_by_notify(Service *s) { + int r; + assert(s); (void) unit_enqueue_rewatch_pids(UNIT(s)); - service_arm_timer(s, /* relative= */ true, s->timeout_stop_usec); + r = service_arm_timer(s, /* relative= */ true, s->timeout_stop_usec); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to install timer: %m"); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); + return; + } /* The service told us it's stopping, so it's as if we SIGTERM'd it. */ service_set_state(s, SERVICE_STOP_SIGTERM); @@ -2196,24 +2200,22 @@ static void service_enter_stop(Service *s, ServiceResult f) { s->control_command = s->exec_command[SERVICE_EXEC_STOP]; if (s->control_command) { s->control_command_id = SERVICE_EXEC_STOP; + pidref_done(&s->control_pid); r = service_spawn(s, s->control_command, s->timeout_stop_usec, EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_SETENV_RESULT|EXEC_CONTROL_CGROUP, &s->control_pid); - if (r < 0) - goto fail; + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'stop' task: %m"); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); + return; + } service_set_state(s, SERVICE_STOP); } else service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop' task: %m"); - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); } static bool service_good(Service *s) { @@ -2237,6 +2239,8 @@ static bool service_good(Service *s) { } static void service_enter_running(Service *s, ServiceResult f) { + int r; + assert(s); if (s->result == SERVICE_SUCCESS) @@ -2255,7 +2259,13 @@ static void service_enter_running(Service *s, ServiceResult f) { service_enter_stop_by_notify(s); else { service_set_state(s, SERVICE_RUNNING); - service_arm_timer(s, /* relative= */ false, service_running_timeout(s)); + + r = service_arm_timer(s, /* relative= */ false, service_running_timeout(s)); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to install timer: %m"); + service_enter_running(s, SERVICE_FAILURE_RESOURCES); + return; + } } } else if (s->remain_after_exit) @@ -2274,24 +2284,22 @@ static void service_enter_start_post(Service *s) { s->control_command = s->exec_command[SERVICE_EXEC_START_POST]; if (s->control_command) { s->control_command_id = SERVICE_EXEC_START_POST; + pidref_done(&s->control_pid); r = service_spawn(s, s->control_command, s->timeout_start_usec, EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_CONTROL_CGROUP, &s->control_pid); - if (r < 0) - goto fail; + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'start-post' task: %m"); + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); + return; + } service_set_state(s, SERVICE_START_POST); } else service_enter_running(s, SERVICE_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-post' task: %m"); - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); } static void service_kill_control_process(Service *s) { @@ -2299,17 +2307,17 @@ static void service_kill_control_process(Service *s) { assert(s); - if (s->control_pid <= 0) + if (!pidref_is_set(&s->control_pid)) return; - r = kill_and_sigcont(s->control_pid, SIGKILL); + r = pidref_kill_and_sigcont(&s->control_pid, SIGKILL); if (r < 0) { _cleanup_free_ char *comm = NULL; - (void) get_process_comm(s->control_pid, &comm); + (void) get_process_comm(s->control_pid.pid, &comm); log_unit_debug_errno(UNIT(s), r, "Failed to kill control process " PID_FMT " (%s), ignoring: %m", - s->control_pid, strna(comm)); + s->control_pid.pid, strna(comm)); } } @@ -2336,9 +2344,9 @@ static int service_adverse_to_leftover_processes(Service *s) { } static void service_enter_start(Service *s) { + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; ExecCommand *c; usec_t timeout; - pid_t pid; int r; assert(s); @@ -2392,15 +2400,17 @@ static void service_enter_start(Service *s) { c, timeout, EXEC_PASS_FDS|EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG|EXEC_WRITE_CREDENTIALS|EXEC_SETENV_MONITOR_RESULT, - &pid); - if (r < 0) + &pidref); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'start' task: %m"); goto fail; + } if (IN_SET(s->type, SERVICE_SIMPLE, SERVICE_IDLE)) { /* For simple services we immediately start * the START_POST binaries. */ - (void) service_set_main_pid(s, pid); + (void) service_set_main_pidref(s, &pidref); service_enter_start_post(s); } else if (s->type == SERVICE_FORKING) { @@ -2408,7 +2418,8 @@ static void service_enter_start(Service *s) { /* For forking services we wait until the start * process exited. */ - s->control_pid = pid; + pidref_done(&s->control_pid); + s->control_pid = TAKE_PIDREF(pidref); service_set_state(s, SERVICE_START); } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY, SERVICE_NOTIFY_RELOAD, SERVICE_EXEC)) { @@ -2418,7 +2429,7 @@ static void service_enter_start(Service *s) { /* For D-Bus services we know the main pid right away, but wait for the bus name to appear on the * bus. 'notify' and 'exec' services are similar. */ - (void) service_set_main_pid(s, pid); + (void) service_set_main_pidref(s, &pidref); service_set_state(s, SERVICE_START); } else assert_not_reached(); @@ -2426,7 +2437,6 @@ static void service_enter_start(Service *s) { return; fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'start' task: %m"); service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); } @@ -2451,8 +2461,10 @@ static void service_enter_start_pre(Service *s) { s->timeout_start_usec, EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN|EXEC_SETENV_MONITOR_RESULT|EXEC_WRITE_CREDENTIALS, &s->control_pid); - if (r < 0) + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'start-pre' task: %m"); goto fail; + } service_set_state(s, SERVICE_START_PRE); } else @@ -2461,8 +2473,7 @@ static void service_enter_start_pre(Service *s) { return; fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-pre' task: %m"); - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, /* allow_restart= */ true); } static void service_enter_condition(Service *s) { @@ -2480,6 +2491,7 @@ static void service_enter_condition(Service *s) { goto fail; s->control_command_id = SERVICE_EXEC_CONDITION; + pidref_done(&s->control_pid); r = service_spawn(s, s->control_command, @@ -2487,8 +2499,10 @@ static void service_enter_condition(Service *s) { EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN, &s->control_pid); - if (r < 0) + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'exec-condition' task: %m"); goto fail; + } service_set_state(s, SERVICE_CONDITION); } else @@ -2497,8 +2511,7 @@ static void service_enter_condition(Service *s) { return; fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'exec-condition' task: %m"); - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, /* allow_restart= */ true); } static void service_enter_restart(Service *s) { @@ -2516,8 +2529,11 @@ static void service_enter_restart(Service *s) { /* Any units that are bound to this service must also be restarted. We use JOB_START for ourselves * but then set JOB_RESTART_DEPENDENCIES which will enqueue JOB_RESTART for those dependency jobs. */ r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(s), JOB_RESTART_DEPENDENCIES, NULL, &error, NULL); - if (r < 0) - goto fail; + if (r < 0) { + log_unit_warning(UNIT(s), "Failed to schedule restart job: %s", bus_error_message(&error, r)); + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, /* allow_restart= */ false); + return; + } /* Count the jobs we enqueue for restarting. This counter is maintained as long as the unit isn't * fully stopped, i.e. as long as it remains up or remains in auto-start states. The user can reset @@ -2538,11 +2554,6 @@ static void service_enter_restart(Service *s) { /* Notify clients about changed restart counter */ unit_add_to_dbus_queue(UNIT(s)); - return; - -fail: - log_unit_warning(UNIT(s), "Failed to schedule restart job: %s", bus_error_message(&error, r)); - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false); } static void service_enter_reload_by_notify(Service *s) { @@ -2551,13 +2562,20 @@ static void service_enter_reload_by_notify(Service *s) { assert(s); - service_arm_timer(s, /* relative= */ true, s->timeout_start_usec); + r = service_arm_timer(s, /* relative= */ true, s->timeout_start_usec); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to install timer: %m"); + s->reload_result = SERVICE_FAILURE_RESOURCES; + service_enter_running(s, SERVICE_SUCCESS); + return; + } + service_set_state(s, SERVICE_RELOAD_NOTIFY); /* service_enter_reload_by_notify is never called during a reload, thus no loops are possible. */ r = manager_propagate_reload(UNIT(s)->manager, UNIT(s), JOB_FAIL, &error); if (r < 0) - log_unit_warning(UNIT(s), "Failed to schedule propagation of reload: %s", bus_error_message(&error, r)); + log_unit_warning(UNIT(s), "Failed to schedule propagation of reload, ignoring: %s", bus_error_message(&error, r)); } static void service_enter_reload(Service *s) { @@ -2571,8 +2589,8 @@ static void service_enter_reload(Service *s) { usec_t ts = now(CLOCK_MONOTONIC); - if (s->type == SERVICE_NOTIFY_RELOAD && s->main_pid > 0) { - r = kill_and_sigcont(s->main_pid, s->reload_signal); + if (s->type == SERVICE_NOTIFY_RELOAD && pidref_is_set(&s->main_pid)) { + r = pidref_kill_and_sigcont(&s->main_pid, s->reload_signal); if (r < 0) { log_unit_warning_errno(UNIT(s), r, "Failed to send reload signal: %m"); goto fail; @@ -2584,6 +2602,7 @@ static void service_enter_reload(Service *s) { s->control_command = s->exec_command[SERVICE_EXEC_RELOAD]; if (s->control_command) { s->control_command_id = SERVICE_EXEC_RELOAD; + pidref_done(&s->control_pid); r = service_spawn(s, s->control_command, @@ -2591,13 +2610,18 @@ static void service_enter_reload(Service *s) { EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_CONTROL_CGROUP, &s->control_pid); if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to run 'reload' task: %m"); + log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'reload' task: %m"); goto fail; } service_set_state(s, SERVICE_RELOAD); } else if (killed) { - service_arm_timer(s, /* relative= */ true, s->timeout_start_usec); + r = service_arm_timer(s, /* relative= */ true, s->timeout_start_usec); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to install timer: %m"); + goto fail; + } + service_set_state(s, SERVICE_RELOAD_SIGNAL); } else { service_enter_running(s, SERVICE_SUCCESS); @@ -2634,6 +2658,8 @@ static void service_run_next_control(Service *s) { else timeout = s->timeout_stop_usec; + pidref_done(&s->control_pid); + r = service_spawn(s, s->control_command, timeout, @@ -2644,27 +2670,23 @@ static void service_run_next_control(Service *s) { (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_START) ? EXEC_SETENV_MONITOR_RESULT : 0)| (IN_SET(s->control_command_id, SERVICE_EXEC_START_POST, SERVICE_EXEC_RELOAD, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_CONTROL_CGROUP : 0), &s->control_pid); - if (r < 0) - goto fail; - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run next control task: %m"); - - if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START_POST, SERVICE_STOP)) - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); - else if (s->state == SERVICE_STOP_POST) - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); - else if (s->state == SERVICE_RELOAD) { - s->reload_result = SERVICE_FAILURE_RESOURCES; - service_enter_running(s, SERVICE_SUCCESS); - } else - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to spawn next control task: %m"); + + if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START_POST, SERVICE_STOP)) + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); + else if (s->state == SERVICE_STOP_POST) + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, /* allow_restart= */ true); + else if (s->state == SERVICE_RELOAD) { + s->reload_result = SERVICE_FAILURE_RESOURCES; + service_enter_running(s, SERVICE_SUCCESS); + } else + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); + } } static void service_run_next_main(Service *s) { - pid_t pid; + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; int r; assert(s); @@ -2679,17 +2701,14 @@ static void service_run_next_main(Service *s) { s->main_command, s->timeout_start_usec, EXEC_PASS_FDS|EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG|EXEC_SETENV_MONITOR_RESULT|EXEC_WRITE_CREDENTIALS, - &pid); - if (r < 0) - goto fail; - - (void) service_set_main_pid(s, pid); - - return; + &pidref); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to spawn next main task: %m"); + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); + return; + } -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run next main task: %m"); - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); + (void) service_set_main_pidref(s, &pidref); } static int service_start(Unit *u) { @@ -2823,7 +2842,7 @@ static int service_reload(Unit *u) { return 1; } -_pure_ static bool service_can_reload(Unit *u) { +static bool service_can_reload(Unit *u) { Service *s = SERVICE(u); assert(s); @@ -2930,11 +2949,11 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { (void) serialize_item(f, "result", service_result_to_string(s->result)); (void) serialize_item(f, "reload-result", service_result_to_string(s->reload_result)); - if (s->control_pid > 0) - (void) serialize_item_format(f, "control-pid", PID_FMT, s->control_pid); + if (pidref_is_set(&s->control_pid)) + (void) serialize_item_format(f, "control-pid", PID_FMT, s->control_pid.pid); - if (s->main_pid_known && s->main_pid > 0) - (void) serialize_item_format(f, "main-pid", PID_FMT, s->main_pid); + if (s->main_pid_known && pidref_is_set(&s->main_pid)) + (void) serialize_item_format(f, "main-pid", PID_FMT, s->main_pid.pid); (void) serialize_bool(f, "main-pid-known", s->main_pid_known); (void) serialize_bool(f, "bus-name-good", s->bus_name_good); @@ -3168,12 +3187,10 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, s->reload_result = f; } else if (streq(key, "control-pid")) { - pid_t pid; - - if (parse_pid(value, &pid) < 0) - log_unit_debug(u, "Failed to parse control-pid value: %s", value); - else - s->control_pid = pid; + pidref_done(&s->control_pid); + r = pidref_set_pidstr(&s->control_pid, value); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to initialize control PID '%s' from serialization, ignoring.", value); } else if (streq(key, "main-pid")) { pid_t pid; @@ -3394,7 +3411,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, return 0; } -_pure_ static UnitActiveState service_active_state(Unit *u) { +static UnitActiveState service_active_state(Unit *u) { const UnitActiveState *table; assert(u); @@ -3453,18 +3470,17 @@ static int service_watch_pid_file(Service *s) { log_unit_debug(UNIT(s), "Setting watch for PID file %s", s->pid_file_pathspec->path); r = path_spec_watch(s->pid_file_pathspec, service_dispatch_inotify_io); - if (r < 0) - goto fail; + if (r < 0) { + log_unit_error_errno(UNIT(s), r, "Failed to set a watch for PID file %s: %m", s->pid_file_pathspec->path); + service_unwatch_pid_file(s); + return r; + } /* the pidfile might have appeared just before we set the watch */ log_unit_debug(UNIT(s), "Trying to read PID file %s in case it changed", s->pid_file_pathspec->path); service_retry_pid_file(s); return 0; -fail: - log_unit_error_errno(UNIT(s), r, "Failed to set a watch for PID file %s: %m", s->pid_file_pathspec->path); - service_unwatch_pid_file(s); - return r; } static int service_demand_pid_file(Service *s) { @@ -3723,7 +3739,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* Oneshot services and non-SERVICE_EXEC_START commands should not be * considered daemons as they are typically not long running. */ - if (s->type == SERVICE_ONESHOT || (s->control_pid == pid && s->control_command_id != SERVICE_EXEC_START)) + if (s->type == SERVICE_ONESHOT || (s->control_pid.pid == pid && s->control_command_id != SERVICE_EXEC_START)) clean_mode = EXIT_CLEAN_COMMAND; else clean_mode = EXIT_CLEAN_DAEMON; @@ -3739,7 +3755,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { else assert_not_reached(); - if (s->main_pid == pid) { + if (s->main_pid.pid == pid) { /* Clean up the exec_fd event source. We want to do this here, not later in * service_set_state(), because service_enter_stop_post() calls service_spawn(). * The source owns its end of the pipe, so this will close that too. */ @@ -3751,7 +3767,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { if (service_load_pid_file(s, false) > 0) return; - s->main_pid = 0; + pidref_done(&s->main_pid); exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status); if (s->main_command) { @@ -3882,11 +3898,11 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { service_enter_start_post(s); } - } else if (s->control_pid == pid) { + } else if (s->control_pid.pid == pid) { const char *kind; bool success; - s->control_pid = 0; + pidref_done(&s->control_pid); if (s->control_command) { exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); @@ -4314,23 +4330,23 @@ static bool service_notify_message_authorized(Service *s, pid_t pid, FDSet *fds) return false; } - if (notify_access == NOTIFY_MAIN && pid != s->main_pid) { - if (s->main_pid != 0) - log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid); + if (notify_access == NOTIFY_MAIN && pid != s->main_pid.pid) { + if (pidref_is_set(&s->main_pid)) + log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid.pid); else log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", pid); return false; } - if (notify_access == NOTIFY_EXEC && pid != s->main_pid && pid != s->control_pid) { - if (s->main_pid != 0 && s->control_pid != 0) + if (notify_access == NOTIFY_EXEC && pid != s->main_pid.pid && pid != s->control_pid.pid) { + if (pidref_is_set(&s->main_pid) && pidref_is_set(&s->control_pid)) log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT" and control PID "PID_FMT, - pid, s->main_pid, s->control_pid); - else if (s->main_pid != 0) - log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid); - else if (s->control_pid != 0) - log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for control PID "PID_FMT, pid, s->control_pid); + pid, s->main_pid.pid, s->control_pid.pid); + else if (pidref_is_set(&s->main_pid)) + log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid.pid); + else if (pidref_is_set(&s->control_pid)) + log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for control PID "PID_FMT, pid, s->control_pid.pid); else log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID and control PID which are currently not known", pid); @@ -4372,34 +4388,35 @@ static void service_notify_message( _cleanup_free_ char *cc = NULL; cc = strv_join(tags, ", "); - log_unit_debug(u, "Got notification message from PID "PID_FMT" (%s)", ucred->pid, isempty(cc) ? "n/a" : cc); + log_unit_debug(u, "Got notification message from PID "PID_FMT" (%s)", ucred->pid, empty_to_na(cc)); } /* Interpret MAINPID= */ e = strv_find_startswith(tags, "MAINPID="); if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY)) { - pid_t new_main_pid; + _cleanup_(pidref_done) PidRef new_main_pid = PIDREF_NULL; - if (parse_pid(e, &new_main_pid) < 0) - log_unit_warning(u, "Failed to parse MAINPID= field in notification message, ignoring: %s", e); - else if (!s->main_pid_known || new_main_pid != s->main_pid) { + r = pidref_set_pidstr(&new_main_pid, e); + if (r < 0) + log_unit_warning_errno(u, r, "Failed to parse MAINPID=%s field in notification message, ignoring: %m", e); + else if (!s->main_pid_known || !pidref_equal(&new_main_pid, &s->main_pid)) { - r = service_is_suitable_main_pid(s, new_main_pid, LOG_WARNING); + r = service_is_suitable_main_pid(s, &new_main_pid, LOG_WARNING); if (r == 0) { /* The new main PID is a bit suspicious, which is OK if the sender is privileged. */ if (ucred->uid == 0) { - log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, but we'll accept it as the request to change it came from a privileged process.", new_main_pid); + log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, but we'll accept it as the request to change it came from a privileged process.", new_main_pid.pid); r = 1; } else - log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid); + log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid.pid); } if (r > 0) { - (void) service_set_main_pid(s, new_main_pid); + (void) service_set_main_pidref(s, &new_main_pid); - r = unit_watch_pid(UNIT(s), new_main_pid, false); + r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false); if (r < 0) - log_unit_warning_errno(UNIT(s), r, "Failed to watch new main PID "PID_FMT" for service: %m", new_main_pid); + log_unit_warning_errno(UNIT(s), r, "Failed to watch new main PID "PID_FMT" for service: %m", s->main_pid.pid); notify_dbus = true; } @@ -4621,7 +4638,7 @@ static bool pick_up_pid_from_bus_name(Service *s) { /* If the service is running but we have no main PID yet, get it from the owner of the D-Bus name */ - return !pid_is_valid(s->main_pid) && + return !pidref_is_set(&s->main_pid) && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, @@ -4632,6 +4649,7 @@ static bool pick_up_pid_from_bus_name(Service *s) { } static int bus_name_pid_lookup_callback(sd_bus_message *reply, void *userdata, sd_bus_error *ret_error) { + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; const sd_bus_error *e; Unit *u = ASSERT_PTR(userdata); uint32_t pid; @@ -4659,15 +4677,16 @@ static int bus_name_pid_lookup_callback(sd_bus_message *reply, void *userdata, s return 1; } - if (!pid_is_valid(pid)) { - log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "GetConnectionUnixProcessID() returned invalid PID"); + r = pidref_set_pid(&pidref, pid); + if (r < 0) { + log_debug_errno(r, "GetConnectionUnixProcessID() returned invalid PID: %m"); return 1; } - log_unit_debug(u, "D-Bus name %s is now owned by process " PID_FMT, s->bus_name, (pid_t) pid); + log_unit_debug(u, "D-Bus name %s is now owned by process " PID_FMT, s->bus_name, pidref.pid); - (void) service_set_main_pid(s, pid); - (void) unit_watch_pid(UNIT(s), pid, false); + (void) service_set_main_pidref(s, &pidref); + (void) unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false); return 1; } @@ -4793,28 +4812,12 @@ static void service_reset_failed(Unit *u) { s->flush_n_restarts = false; } -static int service_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) { - Service *s = SERVICE(u); - - assert(s); - - return unit_kill_common(u, who, signo, code, value, s->main_pid, s->control_pid, error); +static PidRef* service_main_pid(Unit *u) { + return &ASSERT_PTR(SERVICE(u))->main_pid; } -static int service_main_pid(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - return s->main_pid; -} - -static int service_control_pid(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - return s->control_pid; +static PidRef* service_control_pid(Unit *u) { + return &ASSERT_PTR(SERVICE(u))->control_pid; } static bool service_needs_console(Unit *u) { @@ -4911,19 +4914,21 @@ static int service_clean(Unit *u, ExecCleanMask mask) { s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; r = service_arm_timer(s, /* relative= */ true, s->exec_context.timeout_clean_usec); - if (r < 0) + if (r < 0) { + log_unit_warning_errno(u, r, "Failed to install timer: %m"); goto fail; + } r = unit_fork_and_watch_rm_rf(u, l, &s->control_pid); - if (r < 0) + if (r < 0) { + log_unit_warning_errno(u, r, "Failed to spawn cleaning task: %m"); goto fail; + } service_set_state(s, SERVICE_CLEANING); - return 0; fail: - log_unit_warning_errno(u, r, "Failed to initiate cleaning: %m"); s->clean_result = SERVICE_FAILURE_RESOURCES; s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source); return r; @@ -5127,7 +5132,6 @@ const UnitVTable service_vtable = { .can_reload = service_can_reload, - .kill = service_kill, .clean = service_clean, .can_clean = service_can_clean,