Merge pull request #29152 from poettering/pidref-more

pid1: port more code over to PidRef (i.e. pidfd based operation)
This commit is contained in:
Luca Boccassi 2023-09-19 01:12:09 +01:00 committed by GitHub
commit 498ef07dd8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 220 additions and 255 deletions

7
TODO
View file

@ -174,14 +174,9 @@ Features:
- pid_is_unwaited() → pidref_is_unwaited()
- pid_is_alive() → pidref_is_alive()
- unit_watch_pid() → unit_watch_pidref()
- unit_kill_common()
- unit_kill_context()
- service_set_main_pid()
- actually wait for POLLIN on piref's pidfd in service logic
- unit_main_pid() + unit_control_pid()
- actually wait for POLLIN on pidref's pidfd in service logic
- exec_spawn()
- serialization of control/main pid in service, socket, mount, swap units
- unit_fork_and_watch_rm_rf()
- cg_pid_get_unit()
- openpt_allocate_in_namespace()
- scope dbus PIDs property needs to gain PIDFDs companion

View file

@ -6,6 +6,7 @@
#include "parse-util.h"
#include "pidref.h"
#include "process-util.h"
#include "signal-util.h"
int pidref_set_pid(PidRef *pidref, pid_t pid) {
int fd;
@ -143,3 +144,30 @@ int pidref_kill_and_sigcont(PidRef *pidref, int sig) {
return 0;
}
int pidref_sigqueue(PidRef *pidref, int sig, int value) {
if (!pidref)
return -ESRCH;
if (pidref->fd >= 0) {
siginfo_t si;
/* We can't use structured initialization here, since the structure contains various unions
* and these fields lie in overlapping (carefully aligned) unions that LLVM is allergic to
* allow assignments to */
zero(si);
si.si_signo = sig;
si.si_code = SI_QUEUE;
si.si_pid = getpid_cached();
si.si_uid = getuid();
si.si_value.sival_int = value;
return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, &si, 0));
}
if (pidref->pid > 0)
return RET_NERRNO(sigqueue(pidref->pid, sig, (const union sigval) { .sival_int = value }));
return -ESRCH;
}

View file

@ -25,5 +25,6 @@ void pidref_done(PidRef *pidref);
int pidref_kill(PidRef *pidref, int sig);
int pidref_kill_and_sigcont(PidRef *pidref, int sig);
int pidref_sigqueue(PidRef *pidfref, int sig, int value);
#define TAKE_PIDREF(p) TAKE_GENERIC((p), PidRef, PIDREF_NULL)

View file

@ -134,7 +134,6 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
int read_only, make_file_or_directory;
Unit *u = ASSERT_PTR(userdata);
ExecContext *c;
pid_t unit_pid;
int r;
assert(message);
@ -192,14 +191,14 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
if (!exec_needs_mount_namespace(c, NULL, unit_get_exec_runtime(u)))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit not running in private mount namespace, cannot activate bind mount");
unit_pid = unit_main_pid(u);
if (unit_pid == 0 || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
PidRef* unit_pid = unit_main_pid(u);
if (!pidref_is_set(unit_pid) || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not running");
propagate_directory = strjoina("/run/systemd/propagate/", u->id);
if (is_image)
r = mount_image_in_namespace(
unit_pid,
unit_pid->pid,
propagate_directory,
"/run/systemd/incoming/",
src, dest,
@ -209,7 +208,7 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
c->mount_image_policy ?: &image_policy_service);
else
r = bind_mount_in_namespace(
unit_pid,
unit_pid->pid,
propagate_directory,
"/run/systemd/incoming/",
src, dest,

View file

@ -1331,7 +1331,6 @@ int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bu
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_set_free_ Set *pids = NULL;
Unit *u = userdata;
pid_t pid;
int r;
assert(message);
@ -1359,16 +1358,16 @@ int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bu
}
/* The main and control pids might live outside of the cgroup, hence fetch them separately */
pid = unit_main_pid(u);
if (pid > 0) {
r = append_process(reply, NULL, pid, pids);
PidRef *pid = unit_main_pid(u);
if (pidref_is_set(pid)) {
r = append_process(reply, NULL, pid->pid, pids);
if (r < 0)
return r;
}
pid = unit_control_pid(u);
if (pid > 0) {
r = append_process(reply, NULL, pid, pids);
if (pidref_is_set(pid)) {
r = append_process(reply, NULL, pid->pid, pids);
if (r < 0)
return r;
}

View file

@ -1036,9 +1036,9 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
UNIT(m),
&m->kill_context,
state_to_kill_operation(state),
-1,
m->control_pid.pid,
false);
/* main_pid= */ NULL,
&m->control_pid,
/* main_pid_alien= */ false);
if (r < 0)
goto fail;
@ -2213,26 +2213,13 @@ static void mount_reset_failed(Unit *u) {
m->clean_result = MOUNT_SUCCESS;
}
static int mount_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) {
Mount *m = MOUNT(u);
assert(m);
return unit_kill_common(u, who, signo, code, value, -1, m->control_pid.pid, error);
}
static int mount_control_pid(Unit *u) {
Mount *m = MOUNT(u);
assert(m);
return m->control_pid.pid;
static PidRef* mount_control_pid(Unit *u) {
return &ASSERT_PTR(MOUNT(u))->control_pid;
}
static int mount_clean(Unit *u, ExecCleanMask mask) {
_cleanup_strv_free_ char **l = NULL;
Mount *m = MOUNT(u);
pid_t pid;
int r;
assert(m);
@ -2257,11 +2244,7 @@ static int mount_clean(Unit *u, ExecCleanMask mask) {
if (r < 0)
goto fail;
r = unit_fork_and_watch_rm_rf(u, l, &pid);
if (r < 0)
goto fail;
r = pidref_set_pid(&m->control_pid, pid);
r = unit_fork_and_watch_rm_rf(u, l, &m->control_pid);
if (r < 0)
goto fail;
@ -2358,7 +2341,6 @@ const UnitVTable mount_vtable = {
.stop = mount_stop,
.reload = mount_reload,
.kill = mount_kill,
.clean = mount_clean,
.can_clean = mount_can_clean,

View file

@ -345,7 +345,9 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
state != SCOPE_STOP_SIGTERM ? KILL_KILL :
s->was_abandoned ? KILL_TERMINATE_AND_LOG :
KILL_TERMINATE,
-1, -1, false);
/* main_pid= */ NULL,
/* control_pid= */ NULL,
/* main_pid_alien= */ false);
if (r < 0)
goto fail;
}
@ -370,8 +372,8 @@ fail:
}
static int scope_enter_start_chown(Scope *s) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
Unit *u = UNIT(s);
pid_t pid;
int r;
assert(s);
@ -381,7 +383,7 @@ static int scope_enter_start_chown(Scope *s) {
if (r < 0)
return r;
r = unit_fork_helper_process(u, "(sd-chown-cgroup)", &pid);
r = unit_fork_helper_process(u, "(sd-chown-cgroup)", &pidref);
if (r < 0)
goto fail;
@ -418,7 +420,7 @@ static int scope_enter_start_chown(Scope *s) {
_exit(EXIT_SUCCESS);
}
r = unit_watch_pid(UNIT(s), pid, true);
r = unit_watch_pid(UNIT(s), pidref.pid, /* exclusive= */ true);
if (r < 0)
goto fail;
@ -531,10 +533,6 @@ static void scope_reset_failed(Unit *u) {
s->result = SCOPE_SUCCESS;
}
static int scope_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) {
return unit_kill_common(u, who, signo, code, value, -1, -1, error);
}
static int scope_get_timeout(Unit *u, usec_t *timeout) {
Scope *s = SCOPE(u);
usec_t t;
@ -828,8 +826,6 @@ const UnitVTable scope_vtable = {
.start = scope_start,
.stop = scope_stop,
.kill = scope_kill,
.freeze = unit_freeze_vtable_common,
.thaw = unit_thaw_vtable_common,

View file

@ -179,38 +179,51 @@ static void service_unwatch_pid_file(Service *s) {
s->pid_file_pathspec = mfree(s->pid_file_pathspec);
}
static int service_set_main_pidref(Service *s, PidRef *pidref) {
assert(s);
/* 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 (pidref->pid == getpid_cached())
return -EINVAL;
if (s->main_pid.pid == pidref->pid && s->main_pid_known) {
pidref_done(pidref);
return 0;
}
if (s->main_pid.pid != pidref->pid) {
service_unwatch_main_pid(s);
exec_status_start(&s->main_exec_status, pidref->pid);
}
s->main_pid = TAKE_PIDREF(*pidref);
s->main_pid_known = true;
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.", 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);
if (pid <= 1)
return -EINVAL;
if (pid == getpid_cached())
return -EINVAL;
if (s->main_pid.pid == pid && s->main_pid_known)
return 0;
r = pidref_set_pid(&pidref, pid);
if (r < 0)
return r;
if (s->main_pid.pid != pid) {
service_unwatch_main_pid(s);
exec_status_start(&s->main_exec_status, pid);
}
s->main_pid = TAKE_PIDREF(pidref);
s->main_pid_known = true;
s->main_pid_alien = pid_is_my_child(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);
return 0;
return service_set_main_pidref(s, &pidref);
}
void service_release_socket_fd(Service *s) {
@ -1108,6 +1121,7 @@ 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;
@ -1149,7 +1163,11 @@ static int service_load_pid_file(Service *s, bool may_warn) {
if (s->main_pid_known && pid == s->main_pid.pid)
return 0;
r = service_is_suitable_main_pid(s, pid, prio);
r = pidref_set_pid(&pidref, pid);
if (r < 0)
return log_unit_full_errno(UNIT(s), prio, r, "Failed to pin PID " PID_FMT ": %m", pid);
r = service_is_suitable_main_pid(s, pidref.pid, prio);
if (r < 0)
return r;
if (r == 0) {
@ -1166,26 +1184,26 @@ 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, 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, /* exclusive= */ false);
r = unit_watch_pid(UNIT(s), s->main_pid.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;
}
@ -1212,10 +1230,10 @@ static void service_search_main_pid(Service *s) {
if (service_set_main_pid(s, pid) < 0)
return;
r = unit_watch_pid(UNIT(s), pid, /* exclusive= */ false);
r = unit_watch_pid(UNIT(s), s->main_pid.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) {
@ -2148,8 +2166,8 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
UNIT(s),
&s->kill_context,
kill_operation,
s->main_pid.pid,
s->control_pid.pid,
&s->main_pid,
&s->control_pid,
s->main_pid_alien);
if (r < 0)
goto fail;
@ -2412,7 +2430,7 @@ static void service_enter_start(Service *s) {
/* For simple services we immediately start
* the START_POST binaries. */
(void) service_set_main_pid(s, pidref.pid);
(void) service_set_main_pidref(s, &pidref);
service_enter_start_post(s);
} else if (s->type == SERVICE_FORKING) {
@ -2431,7 +2449,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, pidref.pid);
(void) service_set_main_pidref(s, &pidref);
service_set_state(s, SERVICE_START);
} else
assert_not_reached();
@ -2700,8 +2718,7 @@ static void service_run_next_main(Service *s) {
if (r < 0)
goto fail;
(void) service_set_main_pid(s, pidref.pid);
(void) service_set_main_pidref(s, &pidref);
return;
fail:
@ -4808,28 +4825,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.pid, s->control_pid.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.pid;
}
static int service_control_pid(Unit *u) {
Service *s = SERVICE(u);
assert(s);
return s->control_pid.pid;
static PidRef* service_control_pid(Unit *u) {
return &ASSERT_PTR(SERVICE(u))->control_pid;
}
static bool service_needs_console(Unit *u) {
@ -4889,7 +4890,6 @@ static int service_clean(Unit *u, ExecCleanMask mask) {
_cleanup_strv_free_ char **l = NULL;
bool may_clean_fdstore = false;
Service *s = SERVICE(u);
pid_t pid;
int r;
assert(s);
@ -4930,11 +4930,7 @@ static int service_clean(Unit *u, ExecCleanMask mask) {
if (r < 0)
goto fail;
r = unit_fork_and_watch_rm_rf(u, l, &pid);
if (r < 0)
goto fail;
r = pidref_set_pid(&s->control_pid, pid);
r = unit_fork_and_watch_rm_rf(u, l, &s->control_pid);
if (r < 0)
goto fail;
@ -5146,7 +5142,6 @@ const UnitVTable service_vtable = {
.can_reload = service_can_reload,
.kill = service_kill,
.clean = service_clean,
.can_clean = service_can_clean,

View file

@ -247,10 +247,6 @@ static int slice_stop(Unit *u) {
return 1;
}
static int slice_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) {
return unit_kill_common(u, who, signo, code, value, -1, -1, error);
}
static int slice_serialize(Unit *u, FILE *f, FDSet *fds) {
Slice *s = SLICE(u);
@ -436,8 +432,6 @@ const UnitVTable slice_vtable = {
.start = slice_start,
.stop = slice_stop,
.kill = slice_kill,
.freeze = slice_freeze,
.thaw = slice_thaw,
.can_freeze = slice_can_freeze,

View file

@ -1503,9 +1503,9 @@ static int socket_address_listen_in_cgroup(
const SocketAddress *address,
const char *label) {
_cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
_cleanup_close_pair_ int pair[2] = PIPE_EBADF;
int fd, r;
pid_t pid;
assert(s);
assert(address);
@ -1597,7 +1597,7 @@ static int socket_address_listen_in_cgroup(
fd = receive_one_fd(pair[0], 0);
/* We synchronously wait for the helper, as it shouldn't be slow */
r = wait_for_terminate_and_check("(sd-listen)", pid, WAIT_LOG_ABNORMAL);
r = wait_for_terminate_and_check("(sd-listen)", pid.pid, WAIT_LOG_ABNORMAL);
if (r < 0) {
safe_close(fd);
return r;
@ -1968,8 +1968,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, PidRef *ret_pid) {
}
static int socket_chown(Socket *s, PidRef *ret_pid) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
pid_t pid;
_cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
int r;
assert(s);
@ -2030,15 +2029,11 @@ static int socket_chown(Socket *s, PidRef *ret_pid) {
_exit(EXIT_SUCCESS);
}
r = pidref_set_pid(&pidref, pid);
r = unit_watch_pid(UNIT(s), pid.pid, /* exclusive= */ true);
if (r < 0)
return r;
r = unit_watch_pid(UNIT(s), pidref.pid, /* exclusive= */ true);
if (r < 0)
return r;
*ret_pid = TAKE_PIDREF(pidref);
*ret_pid = TAKE_PIDREF(pid);
return 0;
}
@ -2117,9 +2112,9 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
UNIT(s),
&s->kill_context,
state_to_kill_operation(s, state),
-1,
s->control_pid.pid,
false);
/* main_pid= */ NULL,
&s->control_pid,
/* main_pid_alien= */ false);
if (r < 0)
goto fail;
@ -2982,9 +2977,9 @@ static int socket_accept_do(Socket *s, int fd) {
}
static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) {
_cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
_cleanup_close_pair_ int pair[2] = PIPE_EBADF;
int cfd, r;
pid_t pid;
assert(s);
assert(p);
@ -3034,7 +3029,7 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) {
cfd = receive_one_fd(pair[0], 0);
/* We synchronously wait for the helper, as it shouldn't be slow */
r = wait_for_terminate_and_check("(sd-accept)", pid, WAIT_LOG_ABNORMAL);
r = wait_for_terminate_and_check("(sd-accept)", pid.pid, WAIT_LOG_ABNORMAL);
if (r < 0) {
safe_close(cfd);
return r;
@ -3382,10 +3377,6 @@ static void socket_trigger_notify(Unit *u, Unit *other) {
socket_set_state(s, SOCKET_RUNNING);
}
static int socket_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) {
return unit_kill_common(u, who, signo, code, value, -1, SOCKET(u)->control_pid.pid, error);
}
static int socket_get_timeout(Unit *u, usec_t *timeout) {
Socket *s = SOCKET(u);
usec_t t;
@ -3414,18 +3405,13 @@ char *socket_fdname(Socket *s) {
return s->fdname ?: UNIT(s)->id;
}
static int socket_control_pid(Unit *u) {
Socket *s = SOCKET(u);
assert(s);
return s->control_pid.pid;
static PidRef *socket_control_pid(Unit *u) {
return &ASSERT_PTR(SOCKET(u))->control_pid;
}
static int socket_clean(Unit *u, ExecCleanMask mask) {
_cleanup_strv_free_ char **l = NULL;
Socket *s = SOCKET(u);
pid_t pid;
int r;
assert(s);
@ -3450,11 +3436,7 @@ static int socket_clean(Unit *u, ExecCleanMask mask) {
if (r < 0)
goto fail;
r = unit_fork_and_watch_rm_rf(u, l, &pid);
if (r < 0)
goto fail;
r = pidref_set_pid(&s->control_pid, pid);
r = unit_fork_and_watch_rm_rf(u, l, &s->control_pid);
if (r < 0)
goto fail;
@ -3576,7 +3558,6 @@ const UnitVTable socket_vtable = {
.start = socket_start,
.stop = socket_stop,
.kill = socket_kill,
.clean = socket_clean,
.can_clean = socket_can_clean,

View file

@ -763,12 +763,13 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
if (s->result == SWAP_SUCCESS)
s->result = f;
r = unit_kill_context(UNIT(s),
&s->kill_context,
state_to_kill_operation(s, state),
-1,
s->control_pid.pid,
false);
r = unit_kill_context(
UNIT(s),
&s->kill_context,
state_to_kill_operation(s, state),
/* main_pid= */ NULL,
&s->control_pid,
/* main_pid_alien= */ false);
if (r < 0)
goto fail;
@ -1475,10 +1476,6 @@ static void swap_reset_failed(Unit *u) {
s->clean_result = SWAP_SUCCESS;
}
static int swap_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) {
return unit_kill_common(u, who, signo, code, value, -1, SWAP(u)->control_pid.pid, error);
}
static int swap_get_timeout(Unit *u, usec_t *timeout) {
Swap *s = SWAP(u);
usec_t t;
@ -1515,18 +1512,13 @@ static bool swap_supported(void) {
return supported;
}
static int swap_control_pid(Unit *u) {
Swap *s = SWAP(u);
assert(s);
return s->control_pid.pid;
static PidRef* swap_control_pid(Unit *u) {
return &ASSERT_PTR(SWAP(u))->control_pid;
}
static int swap_clean(Unit *u, ExecCleanMask mask) {
_cleanup_strv_free_ char **l = NULL;
Swap *s = SWAP(u);
pid_t pid;
int r;
assert(s);
@ -1551,11 +1543,7 @@ static int swap_clean(Unit *u, ExecCleanMask mask) {
if (r < 0)
goto fail;
r = unit_fork_and_watch_rm_rf(u, l, &pid);
if (r < 0)
goto fail;
r = pidref_set_pid(&s->control_pid, pid);
r = unit_fork_and_watch_rm_rf(u, l, &s->control_pid);
if (r < 0)
goto fail;
@ -1637,7 +1625,6 @@ const UnitVTable swap_vtable = {
.start = swap_start,
.stop = swap_stop,
.kill = swap_kill,
.clean = swap_clean,
.can_clean = swap_can_clean,

View file

@ -2943,7 +2943,7 @@ void unit_unwatch_all_pids(Unit *u) {
}
static void unit_tidy_watch_pids(Unit *u) {
pid_t except1, except2;
PidRef *except1, *except2;
void *e;
assert(u);
@ -2956,7 +2956,8 @@ static void unit_tidy_watch_pids(Unit *u) {
SET_FOREACH(e, u->pids) {
pid_t pid = PTR_TO_PID(e);
if (pid == except1 || pid == except2)
if ((pidref_is_set(except1) && pid == except1->pid) ||
(pidref_is_set(except2) && pid == except2->pid))
continue;
if (!pid_is_unwaited(pid))
@ -3958,18 +3959,6 @@ bool unit_will_restart(Unit *u) {
return UNIT_VTABLE(u)->will_restart(u);
}
int unit_kill(Unit *u, KillWho w, int signo, int code, int value, sd_bus_error *error) {
assert(u);
assert(w >= 0 && w < _KILL_WHO_MAX);
assert(SIGNAL_VALID(signo));
assert(IN_SET(code, SI_USER, SI_QUEUE));
if (!UNIT_VTABLE(u)->kill)
return -EOPNOTSUPP;
return UNIT_VTABLE(u)->kill(u, w, signo, code, value, error);
}
void unit_notify_cgroup_oom(Unit *u, bool managed_oom) {
assert(u);
@ -4012,35 +4001,34 @@ static int kill_common_log(pid_t pid, int signo, void *userdata) {
return 1;
}
static int kill_or_sigqueue(pid_t pid, int signo, int code, int value) {
assert(pid > 0);
static int kill_or_sigqueue(PidRef* pidref, int signo, int code, int value) {
assert(pidref_is_set(pidref));
assert(SIGNAL_VALID(signo));
switch (code) {
case SI_USER:
log_debug("Killing " PID_FMT " with signal SIG%s.", pid, signal_to_string(signo));
return RET_NERRNO(kill(pid, signo));
log_debug("Killing " PID_FMT " with signal SIG%s.", pidref->pid, signal_to_string(signo));
return pidref_kill(pidref, signo);
case SI_QUEUE:
log_debug("Enqueuing value %i to " PID_FMT " on signal SIG%s.", value, pid, signal_to_string(signo));
return RET_NERRNO(sigqueue(pid, signo, (const union sigval) { .sival_int = value }));
log_debug("Enqueuing value %i to " PID_FMT " on signal SIG%s.", value, pidref->pid, signal_to_string(signo));
return pidref_sigqueue(pidref, signo, value);
default:
assert_not_reached();
}
}
int unit_kill_common(
int unit_kill(
Unit *u,
KillWho who,
int signo,
int code,
int value,
pid_t main_pid,
pid_t control_pid,
sd_bus_error *error) {
PidRef *main_pid, *control_pid;
bool killed = false;
int ret = 0, r;
@ -4054,24 +4042,30 @@ int unit_kill_common(
assert(SIGNAL_VALID(signo));
assert(IN_SET(code, SI_USER, SI_QUEUE));
main_pid = unit_main_pid(u);
control_pid = unit_control_pid(u);
if (!UNIT_HAS_CGROUP_CONTEXT(u) && !main_pid && !control_pid)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit type does not support process killing.");
if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL)) {
if (main_pid < 0)
if (!main_pid)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type));
if (main_pid == 0)
if (!pidref_is_set(main_pid))
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
}
if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL)) {
if (control_pid < 0)
if (!control_pid)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type));
if (control_pid == 0)
if (!pidref_is_set(control_pid))
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
}
if (control_pid > 0 &&
if (pidref_is_set(control_pid) &&
IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL, KILL_ALL, KILL_ALL_FAIL)) {
_cleanup_free_ char *comm = NULL;
(void) get_process_comm(control_pid, &comm);
(void) get_process_comm(control_pid->pid, &comm);
r = kill_or_sigqueue(control_pid, signo, code, value);
if (r < 0) {
@ -4081,23 +4075,23 @@ int unit_kill_common(
sd_bus_error_set_errnof(
error, r,
"Failed to send signal SIG%s to control process " PID_FMT " (%s): %m",
signal_to_string(signo), control_pid, strna(comm));
signal_to_string(signo), control_pid->pid, strna(comm));
log_unit_warning_errno(
u, r,
"Failed to send signal SIG%s to control process " PID_FMT " (%s) on client request: %m",
signal_to_string(signo), control_pid, strna(comm));
signal_to_string(signo), control_pid->pid, strna(comm));
} else {
log_unit_info(u, "Sent signal SIG%s to control process " PID_FMT " (%s) on client request.",
signal_to_string(signo), control_pid, strna(comm));
signal_to_string(signo), control_pid->pid, strna(comm));
killed = true;
}
}
if (main_pid > 0 &&
if (pidref_is_set(main_pid) &&
IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL, KILL_ALL, KILL_ALL_FAIL)) {
_cleanup_free_ char *comm = NULL;
(void) get_process_comm(main_pid, &comm);
(void) get_process_comm(main_pid->pid, &comm);
r = kill_or_sigqueue(main_pid, signo, code, value);
if (r < 0) {
@ -4107,17 +4101,17 @@ int unit_kill_common(
sd_bus_error_set_errnof(
error, r,
"Failed to send signal SIG%s to main process " PID_FMT " (%s): %m",
signal_to_string(signo), main_pid, strna(comm));
signal_to_string(signo), main_pid->pid, strna(comm));
}
log_unit_warning_errno(
u, r,
"Failed to send signal SIG%s to main process " PID_FMT " (%s) on client request: %m",
signal_to_string(signo), main_pid, strna(comm));
signal_to_string(signo), main_pid->pid, strna(comm));
} else {
log_unit_info(u, "Sent signal SIG%s to main process " PID_FMT " (%s) on client request.",
signal_to_string(signo), main_pid, strna(comm));
signal_to_string(signo), main_pid->pid, strna(comm));
killed = true;
}
}
@ -4129,7 +4123,7 @@ int unit_kill_common(
_cleanup_set_free_ Set *pid_set = NULL;
/* Exclude the main/control pids from being killed via the cgroup */
pid_set = unit_pid_set(main_pid, control_pid);
pid_set = unit_pid_set(main_pid ? main_pid->pid : 0, control_pid ? control_pid->pid : 0);
if (!pid_set)
return log_oom();
@ -4775,8 +4769,8 @@ int unit_kill_context(
Unit *u,
KillContext *c,
KillOperation k,
pid_t main_pid,
pid_t control_pid,
PidRef* main_pid,
PidRef* control_pid,
bool main_pid_alien) {
bool wait_for_exit = false, send_sighup;
@ -4803,40 +4797,40 @@ int unit_kill_context(
IN_SET(k, KILL_TERMINATE, KILL_TERMINATE_AND_LOG) &&
sig != SIGHUP;
if (main_pid > 0) {
if (pidref_is_set(main_pid)) {
if (log_func)
log_func(main_pid, sig, u);
log_func(main_pid->pid, sig, u);
r = kill_and_sigcont(main_pid, sig);
r = pidref_kill_and_sigcont(main_pid, sig);
if (r < 0 && r != -ESRCH) {
_cleanup_free_ char *comm = NULL;
(void) get_process_comm(main_pid, &comm);
(void) get_process_comm(main_pid->pid, &comm);
log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s), ignoring: %m", main_pid, strna(comm));
log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s), ignoring: %m", main_pid->pid, strna(comm));
} else {
if (!main_pid_alien)
wait_for_exit = true;
if (r != -ESRCH && send_sighup)
(void) kill(main_pid, SIGHUP);
(void) pidref_kill(main_pid, SIGHUP);
}
}
if (control_pid > 0) {
if (pidref_is_set(control_pid)) {
if (log_func)
log_func(control_pid, sig, u);
log_func(control_pid->pid, sig, u);
r = kill_and_sigcont(control_pid, sig);
r = pidref_kill_and_sigcont(control_pid, sig);
if (r < 0 && r != -ESRCH) {
_cleanup_free_ char *comm = NULL;
(void) get_process_comm(control_pid, &comm);
(void) get_process_comm(control_pid->pid, &comm);
log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s), ignoring: %m", control_pid, strna(comm));
log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s), ignoring: %m", control_pid->pid, strna(comm));
} else {
wait_for_exit = true;
if (r != -ESRCH && send_sighup)
(void) kill(control_pid, SIGHUP);
(void) pidref_kill(control_pid, SIGHUP);
}
}
@ -4845,7 +4839,7 @@ int unit_kill_context(
_cleanup_set_free_ Set *pid_set = NULL;
/* Exclude the main/control pids from being killed via the cgroup */
pid_set = unit_pid_set(main_pid, control_pid);
pid_set = unit_pid_set(main_pid ? main_pid->pid : 0, control_pid ? control_pid->pid : 0);
if (!pid_set)
return -ENOMEM;
@ -4874,7 +4868,7 @@ int unit_kill_context(
if (send_sighup) {
set_free(pid_set);
pid_set = unit_pid_set(main_pid, control_pid);
pid_set = unit_pid_set(main_pid ? main_pid->pid : 0, control_pid ? control_pid->pid : 0);
if (!pid_set)
return -ENOMEM;
@ -5120,22 +5114,22 @@ bool unit_is_pristine(Unit *u) {
!u->merged_into;
}
pid_t unit_control_pid(Unit *u) {
PidRef* unit_control_pid(Unit *u) {
assert(u);
if (UNIT_VTABLE(u)->control_pid)
return UNIT_VTABLE(u)->control_pid(u);
return 0;
return NULL;
}
pid_t unit_main_pid(Unit *u) {
PidRef* unit_main_pid(Unit *u) {
assert(u);
if (UNIT_VTABLE(u)->main_pid)
return UNIT_VTABLE(u)->main_pid(u);
return 0;
return NULL;
}
static void unit_unref_uid_internal(
@ -5325,7 +5319,8 @@ int unit_set_exec_params(Unit *u, ExecParameters *p) {
return 0;
}
int unit_fork_helper_process(Unit *u, const char *name, pid_t *ret) {
int unit_fork_helper_process(Unit *u, const char *name, PidRef *ret) {
pid_t pid;
int r;
assert(u);
@ -5336,9 +5331,24 @@ int unit_fork_helper_process(Unit *u, const char *name, pid_t *ret) {
(void) unit_realize_cgroup(u);
r = safe_fork(name, FORK_REOPEN_LOG|FORK_DEATHSIG, ret);
if (r != 0)
r = safe_fork(name, FORK_REOPEN_LOG|FORK_DEATHSIG, &pid);
if (r < 0)
return r;
if (r > 0) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
int q;
/* Parent */
q = pidref_set_pid(&pidref, pid);
if (q < 0)
return q;
*ret = TAKE_PIDREF(pidref);
return r;
}
/* Child */
(void) default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE);
(void) ignore_signals(SIGPIPE);
@ -5354,8 +5364,8 @@ int unit_fork_helper_process(Unit *u, const char *name, pid_t *ret) {
return 0;
}
int unit_fork_and_watch_rm_rf(Unit *u, char **paths, pid_t *ret_pid) {
pid_t pid;
int unit_fork_and_watch_rm_rf(Unit *u, char **paths, PidRef *ret_pid) {
_cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
int r;
assert(u);
@ -5378,11 +5388,11 @@ int unit_fork_and_watch_rm_rf(Unit *u, char **paths, pid_t *ret_pid) {
_exit(ret);
}
r = unit_watch_pid(u, pid, true);
r = unit_watch_pid(u, pid.pid, /* exclusive= */ true);
if (r < 0)
return r;
*ret_pid = pid;
*ret_pid = TAKE_PIDREF(pid);
return 0;
}

View file

@ -9,14 +9,15 @@
#include "sd-id128.h"
#include "bpf-program.h"
#include "cgroup.h"
#include "condition.h"
#include "emergency-action.h"
#include "install.h"
#include "list.h"
#include "show-status.h"
#include "pidref.h"
#include "set.h"
#include "show-status.h"
#include "unit-file.h"
#include "cgroup.h"
typedef struct UnitRef UnitRef;
@ -624,8 +625,6 @@ typedef struct UnitVTable {
int (*stop)(Unit *u);
int (*reload)(Unit *u);
int (*kill)(Unit *u, KillWho w, int signo, int code, int value, sd_bus_error *error);
/* Clear out the various runtime/state/cache/logs/configuration data */
int (*clean)(Unit *u, ExecCleanMask m);
@ -719,10 +718,10 @@ typedef struct UnitVTable {
usec_t (*get_timeout_start_usec)(Unit *u);
/* Returns the main PID if there is any defined, or 0. */
pid_t (*main_pid)(Unit *u);
PidRef* (*main_pid)(Unit *u);
/* Returns the main PID if there is any defined, or 0. */
pid_t (*control_pid)(Unit *u);
/* Returns the control PID if there is any defined, or 0. */
PidRef* (*control_pid)(Unit *u);
/* Returns true if the unit currently needs access to the console */
bool (*needs_console)(Unit *u);
@ -913,7 +912,6 @@ int unit_stop(Unit *u);
int unit_reload(Unit *u);
int unit_kill(Unit *u, KillWho w, int signo, int code, int value, sd_bus_error *error);
int unit_kill_common(Unit *u, KillWho who, int signo, int code, int value, pid_t main_pid, pid_t control_pid, sd_bus_error *error);
void unit_notify_cgroup_oom(Unit *u, bool managed_oom);
@ -992,7 +990,7 @@ char* unit_concat_strv(char **l, UnitWriteFlags flags);
int unit_write_setting(Unit *u, UnitWriteFlags flags, const char *name, const char *data);
int unit_write_settingf(Unit *u, UnitWriteFlags mode, const char *name, const char *format, ...) _printf_(4,5);
int unit_kill_context(Unit *u, KillContext *c, KillOperation k, pid_t main_pid, pid_t control_pid, bool main_pid_alien);
int unit_kill_context(Unit *u, KillContext *c, KillOperation k, PidRef *main_pid, PidRef *control_pid, bool main_pid_alien);
int unit_make_transient(Unit *u);
@ -1006,8 +1004,8 @@ bool unit_is_unneeded(Unit *u);
bool unit_is_upheld_by_active(Unit *u, Unit **ret_culprit);
bool unit_is_bound_by_inactive(Unit *u, Unit **ret_culprit);
pid_t unit_control_pid(Unit *u);
pid_t unit_main_pid(Unit *u);
PidRef* unit_control_pid(Unit *u);
PidRef* unit_main_pid(Unit *u);
void unit_warn_if_dir_nonempty(Unit *u, const char* where);
int unit_fail_if_noncanonical(Unit *u, const char* where);
@ -1026,8 +1024,8 @@ bool unit_shall_confirm_spawn(Unit *u);
int unit_set_exec_params(Unit *s, ExecParameters *p);
int unit_fork_helper_process(Unit *u, const char *name, pid_t *ret);
int unit_fork_and_watch_rm_rf(Unit *u, char **paths, pid_t *ret_pid);
int unit_fork_helper_process(Unit *u, const char *name, PidRef *ret);
int unit_fork_and_watch_rm_rf(Unit *u, char **paths, PidRef *ret);
void unit_remove_dependencies(Unit *u, UnitDependencyMask mask);