1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-27 10:25:37 +03:00

Merge pull request #29142 from poettering/pidref

core: first step towards a pidfd focused future
This commit is contained in:
Luca Boccassi 2023-09-09 18:39:16 +01:00 committed by GitHub
commit 5cd4613f52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 564 additions and 271 deletions

21
TODO
View File

@ -122,9 +122,6 @@ Deprecations and removals:
* drop fd_is_mount_point() fallback mess once we can rely on
STATX_ATTR_MOUNT_ROOT to exist i.e. kernel baseline 5.8
* rework our PID tracking in services and so on, to be strictly based on pidfd,
once kernel baseline is 5.13.
* Remove /dev/mem ACPI FPDT parsing when /sys/firmware/acpi/fpdt is ubiquitous.
That requires distros to enable CONFIG_ACPI_FPDT, and have kernels v5.12 for
x86 and v6.2 for arm.
@ -136,6 +133,22 @@ Deprecations and removals:
Features:
* PidRef conversion work:
- 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()
- 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
* ddi must be listed as block device fstype
* measure some string via pcrphase whenever we end up booting into emergency
@ -1281,8 +1294,6 @@ Features:
* if /usr/bin/swapoff fails due to OOM, log a friendly explanatory message about it
* pid1: Move to tracking of main pid/control pid of units per pidfd
* pid1: support new clone3() fork-into-cgroup feature
* pid1: also remove PID files of a service when the service starts, not just

View File

@ -65,6 +65,7 @@ basic_sources = files(
'path-lookup.c',
'path-util.c',
'percent-util.c',
'pidref.c',
'prioq.c',
'proc-cmdline.c',
'process-util.c',

145
src/basic/pidref.c Normal file
View File

@ -0,0 +1,145 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "errno-util.h"
#include "fd-util.h"
#include "missing_syscall.h"
#include "parse-util.h"
#include "pidref.h"
#include "process-util.h"
int pidref_set_pid(PidRef *pidref, pid_t pid) {
int fd;
assert(pidref);
if (pid < 0)
return -ESRCH;
if (pid == 0)
pid = getpid_cached();
fd = pidfd_open(pid, 0);
if (fd < 0) {
/* Graceful fallback in case the kernel doesn't support pidfds or is out of fds */
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno) && !ERRNO_IS_RESOURCE(errno))
return -errno;
fd = -EBADF;
}
*pidref = (PidRef) {
.fd = fd,
.pid = pid,
};
return 0;
}
int pidref_set_pidstr(PidRef *pidref, const char *pid) {
pid_t nr;
int r;
assert(pidref);
r = parse_pid(pid, &nr);
if (r < 0)
return r;
return pidref_set_pid(pidref, nr);
}
int pidref_set_pidfd(PidRef *pidref, int fd) {
int r;
assert(pidref);
if (fd < 0)
return -EBADF;
int fd_copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
if (fd_copy < 0) {
pid_t pid;
if (!ERRNO_IS_RESOURCE(errno))
return -errno;
/* Graceful fallback if we are out of fds */
r = pidfd_get_pid(fd, &pid);
if (r < 0)
return r;
*pidref = (PidRef) {
.fd = -EBADF,
.pid = pid,
};
return 0;
}
return pidref_set_pidfd_consume(pidref, fd_copy);
}
int pidref_set_pidfd_take(PidRef *pidref, int fd) {
pid_t pid;
int r;
assert(pidref);
if (fd < 0)
return -EBADF;
r = pidfd_get_pid(fd, &pid);
if (r < 0)
return r;
*pidref = (PidRef) {
.fd = fd,
.pid = pid,
};
return 0;
}
int pidref_set_pidfd_consume(PidRef *pidref, int fd) {
int r;
r = pidref_set_pidfd_take(pidref, fd);
if (r < 0)
safe_close(fd);
return r;
}
void pidref_done(PidRef *pidref) {
assert(pidref);
*pidref = (PidRef) {
.fd = safe_close(pidref->fd),
};
}
int pidref_kill(PidRef *pidref, int sig) {
if (!pidref)
return -ESRCH;
if (pidref->fd >= 0)
return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
if (pidref->pid > 0)
return RET_NERRNO(kill(pidref->pid, sig));
return -ESRCH;
}
int pidref_kill_and_sigcont(PidRef *pidref, int sig) {
int r;
r = pidref_kill(pidref, sig);
if (r < 0)
return r;
if (!IN_SET(sig, SIGCONT, SIGKILL))
(void) pidref_kill(pidref, SIGCONT);
return 0;
}

29
src/basic/pidref.h Normal file
View File

@ -0,0 +1,29 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "macro.h"
/* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes continously. */
typedef struct PidRef {
pid_t pid; /* always valid */
int fd; /* only valid if pidfd are available in the kernel, and we manage to get an fd */
} PidRef;
#define PIDREF_NULL (PidRef) { .fd = -EBADF }
static inline bool pidref_is_set(const PidRef *pidref) {
return pidref && pidref->pid > 0;
}
int pidref_set_pid(PidRef *pidref, pid_t pid);
int pidref_set_pidstr(PidRef *pidref, const char *pid);
int pidref_set_pidfd(PidRef *pidref, int fd);
int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success*/
int pidref_set_pidfd_consume(PidRef *pidref, int fd); /* takes ownership of the passed pidfd in both success and failure */
void pidref_done(PidRef *pidref);
int pidref_kill(PidRef *pidref, int sig);
int pidref_kill_and_sigcont(PidRef *pidref, int sig);
#define TAKE_PIDREF(p) TAKE_GENERIC((p), PidRef, PIDREF_NULL)

View File

@ -89,7 +89,7 @@ const sd_bus_vtable bus_mount_vtable[] = {
SD_BUS_PROPERTY("Options","s", property_get_options, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Type", "s", property_get_type, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Mount, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Mount, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Mount, control_pid.pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Mount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LazyUnmount", "b", bus_property_get_bool, offsetof(Mount, lazy_unmount), SD_BUS_VTABLE_PROPERTY_CONST),

View File

@ -346,8 +346,8 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("RestartPreventExitStatus", "(aiai)", property_get_exit_status_set, offsetof(Service, restart_prevent_status), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RestartForceExitStatus", "(aiai)", property_get_exit_status_set, offsetof(Service, restart_force_status), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SuccessExitStatus", "(aiai)", property_get_exit_status_set, offsetof(Service, success_status), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MainPID", "u", bus_property_get_pid, offsetof(Service, main_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Service, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("MainPID", "u", bus_property_get_pid, offsetof(Service, main_pid.pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Service, control_pid.pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("BusName", "s", NULL, offsetof(Service, bus_name), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("FileDescriptorStoreMax", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store_max), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NFileDescriptorStore", "u", property_get_size_as_uint32, offsetof(Service, n_fd_store), 0),

View File

@ -42,7 +42,7 @@ const sd_bus_vtable bus_swap_vtable[] = {
SD_BUS_PROPERTY("Priority", "i", property_get_priority, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Options", "s", property_get_options, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Swap, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Swap, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Swap, control_pid.pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Swap, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),

View File

@ -185,6 +185,7 @@ static void mount_init(Unit *u) {
* already trying to comply its last one. */
m->exec_context.same_pgrp = true;
m->control_pid = PIDREF_NULL;
m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
u->ignore_on_isolate = true;
@ -223,10 +224,11 @@ static int mount_arm_timer(Mount *m, usec_t usec) {
static void mount_unwatch_control_pid(Mount *m) {
assert(m);
if (m->control_pid <= 0)
if (!pidref_is_set(&m->control_pid))
return;
unit_unwatch_pid(UNIT(m), TAKE_PID(m->control_pid));
unit_unwatch_pid(UNIT(m), m->control_pid.pid);
pidref_done(&m->control_pid);
}
static void mount_parameters_done(MountParameters *p) {
@ -799,11 +801,11 @@ static int mount_coldplug(Unit *u) {
if (m->deserialized_state == m->state)
return 0;
if (m->control_pid > 0 &&
pid_is_unwaited(m->control_pid) &&
if (pidref_is_set(&m->control_pid) &&
pid_is_unwaited(m->control_pid.pid) &&
MOUNT_STATE_WITH_PROCESS(m->deserialized_state)) {
r = unit_watch_pid(UNIT(m), m->control_pid, false);
r = unit_watch_pid(UNIT(m), m->control_pid.pid, /* exclusive= */ false);
if (r < 0)
return r;
@ -829,13 +831,13 @@ static void mount_catchup(Unit *u) {
switch (m->state) {
case MOUNT_DEAD:
case MOUNT_FAILED:
assert(m->control_pid == 0);
assert(!pidref_is_set(&m->control_pid));
(void) unit_acquire_invocation_id(u);
mount_cycle_clear(m);
mount_enter_mounted(m, MOUNT_SUCCESS);
break;
case MOUNT_MOUNTING:
assert(m->control_pid > 0);
assert(pidref_is_set(&m->control_pid));
mount_set_state(m, MOUNT_MOUNTING_DONE);
break;
default:
@ -844,11 +846,11 @@ static void mount_catchup(Unit *u) {
else
switch (m->state) {
case MOUNT_MOUNTING_DONE:
assert(m->control_pid > 0);
assert(pidref_is_set(&m->control_pid));
mount_set_state(m, MOUNT_MOUNTING);
break;
case MOUNT_MOUNTED:
assert(m->control_pid == 0);
assert(!pidref_is_set(&m->control_pid));
mount_enter_dead(m, MOUNT_SUCCESS);
break;
default:
@ -899,17 +901,17 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
prefix, yes_no(m->read_write_only),
prefix, FORMAT_TIMESPAN(m->timeout_usec, USEC_PER_SEC));
if (m->control_pid > 0)
if (pidref_is_set(&m->control_pid))
fprintf(f,
"%sControl PID: "PID_FMT"\n",
prefix, m->control_pid);
prefix, m->control_pid.pid);
exec_context_dump(&m->exec_context, f, prefix);
kill_context_dump(&m->kill_context, f, prefix);
cgroup_context_dump(UNIT(m), f, prefix);
}
static int mount_spawn(Mount *m, ExecCommand *c, pid_t *ret_pid) {
static int mount_spawn(Mount *m, ExecCommand *c, PidRef *ret_pid) {
_cleanup_(exec_params_clear) ExecParameters exec_params = {
.flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
@ -918,6 +920,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *ret_pid) {
.stderr_fd = -EBADF,
.exec_fd = -EBADF,
};
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
pid_t pid;
int r;
@ -947,11 +950,15 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *ret_pid) {
if (r < 0)
return r;
r = unit_watch_pid(UNIT(m), pid, true);
r = pidref_set_pid(&pidref, pid);
if (r < 0)
return r;
*ret_pid = pid;
r = unit_watch_pid(UNIT(m), pidref.pid, /* exclusive= */ true);
if (r < 0)
return r;
*ret_pid = TAKE_PIDREF(pidref);
return 0;
}
@ -1030,7 +1037,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
&m->kill_context,
state_to_kill_operation(state),
-1,
m->control_pid,
m->control_pid.pid,
false);
if (r < 0)
goto fail;
@ -1356,8 +1363,8 @@ static int mount_serialize(Unit *u, FILE *f, FDSet *fds) {
(void) serialize_item(f, "reload-result", mount_result_to_string(m->reload_result));
(void) serialize_item_format(f, "n-retry-umount", "%u", m->n_retry_umount);
if (m->control_pid > 0)
(void) serialize_item_format(f, "control-pid", PID_FMT, m->control_pid);
if (pidref_is_set(&m->control_pid))
(void) serialize_item_format(f, "control-pid", PID_FMT, m->control_pid.pid);
if (m->control_command_id >= 0)
(void) serialize_item(f, "control-command", mount_exec_command_to_string(m->control_command_id));
@ -1410,9 +1417,10 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
} else if (streq(key, "control-pid")) {
r = parse_pid(value, &m->control_pid);
pidref_done(&m->control_pid);
r = pidref_set_pidstr(&m->control_pid, value);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to parse control-pid value: %s", value);
log_debug_errno(r, "Failed to set control PID to '%s': %m", value);
} else if (streq(key, "control-command")) {
MountExecCommand id;
@ -1460,7 +1468,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
assert(m);
assert(pid >= 0);
if (pid != m->control_pid)
if (pid != m->control_pid.pid)
return;
/* So here's the thing, we really want to know before /usr/bin/mount or /usr/bin/umount exit whether
@ -1479,7 +1487,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
* /proc/self/mountinfo changes before our mount/umount exits. */
(void) mount_process_proc_self_mountinfo(u->manager);
m->control_pid = 0;
pidref_done(&m->control_pid);
if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = MOUNT_SUCCESS;
@ -2210,7 +2218,7 @@ static int mount_kill(Unit *u, KillWho who, int signo, int code, int value, sd_b
assert(m);
return unit_kill_common(u, who, signo, code, value, -1, m->control_pid, error);
return unit_kill_common(u, who, signo, code, value, -1, m->control_pid.pid, error);
}
static int mount_control_pid(Unit *u) {
@ -2218,12 +2226,13 @@ static int mount_control_pid(Unit *u) {
assert(m);
return m->control_pid;
return m->control_pid.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);
@ -2248,12 +2257,15 @@ static int mount_clean(Unit *u, ExecCleanMask mask) {
if (r < 0)
goto fail;
r = unit_fork_and_watch_rm_rf(u, l, &m->control_pid);
r = unit_fork_and_watch_rm_rf(u, l, &pid);
if (r < 0)
goto fail;
r = pidref_set_pid(&m->control_pid, pid);
if (r < 0)
goto fail;
mount_set_state(m, MOUNT_CLEANING);
return 0;
fail:

View File

@ -3,8 +3,9 @@
typedef struct Mount Mount;
#include "kill.h"
#include "dynamic-user.h"
#include "kill.h"
#include "pidref.h"
#include "unit.h"
typedef enum MountExecCommand {
@ -83,7 +84,7 @@ struct Mount {
ExecCommand* control_command;
MountExecCommand control_command_id;
pid_t control_pid;
PidRef control_pid;
sd_event_source *timer_event_source;

View File

@ -131,7 +131,8 @@ static void service_init(Unit *u) {
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_pid(UNIT(s), s->control_pid.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_pid(UNIT(s), s->main_pid.pid);
pidref_done(&s->main_pid);
}
static void service_unwatch_pid_file(Service *s) {
@ -177,6 +180,9 @@ static void service_unwatch_pid_file(Service *s) {
}
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)
@ -185,15 +191,19 @@ static int service_set_main_pid(Service *s, pid_t pid) {
if (pid == getpid_cached())
return -EINVAL;
if (s->main_pid == pid && s->main_pid_known)
if (s->main_pid.pid == pid && s->main_pid_known)
return 0;
if (s->main_pid != pid) {
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 = pid;
s->main_pid = TAKE_PIDREF(pidref);
s->main_pid_known = true;
s->main_pid_alien = pid_is_my_child(pid) == 0;
@ -446,8 +456,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);
@ -964,17 +973,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));
@ -1083,7 +1092,7 @@ static int service_is_suitable_main_pid(Service *s, pid_t pid, int prio) {
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 == s->control_pid)
if (pid == s->control_pid.pid)
return log_unit_full_errno(UNIT(s), prio, SYNTHETIC_ERRNO(EPERM), "New main PID "PID_FMT" is the control process, refusing.", pid);
if (!pid_is_alive(pid))
@ -1137,7 +1146,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
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 && pid == s->main_pid.pid)
return 0;
r = service_is_suitable_main_pid(s, pid, prio);
@ -1163,7 +1172,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
}
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, pid);
service_unwatch_main_pid(s);
s->main_pid_known = false;
@ -1174,7 +1183,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
if (r < 0)
return r;
r = unit_watch_pid(UNIT(s), pid, false);
r = unit_watch_pid(UNIT(s), 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);
@ -1187,15 +1196,14 @@ static void service_search_main_pid(Service *s) {
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;
@ -1204,7 +1212,7 @@ static void service_search_main_pid(Service *s) {
if (service_set_main_pid(s, pid) < 0)
return;
r = unit_watch_pid(UNIT(s), pid, false);
r = unit_watch_pid(UNIT(s), 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);
@ -1336,28 +1344,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_pid(UNIT(s), s->main_pid.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_pid(UNIT(s), s->control_pid.pid, /* exclusive= */ false);
if (r < 0)
return r;
}
@ -1620,7 +1628,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 +1639,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 +1708,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 +1839,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_pid(UNIT(s), pidref.pid, /* exclusive= */ true);
if (r < 0)
return r;
*ret_pid = TAKE_PIDREF(pidref);
return 0;
}
@ -1844,19 +1856,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 +1879,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) {
@ -2074,6 +2083,7 @@ 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,
@ -2138,8 +2148,8 @@ 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.pid,
s->control_pid.pid,
s->main_pid_alien);
if (r < 0)
goto fail;
@ -2196,6 +2206,7 @@ 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,
@ -2274,6 +2285,7 @@ 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,
@ -2299,17 +2311,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 +2348,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,7 +2404,7 @@ 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);
&pidref);
if (r < 0)
goto fail;
@ -2400,7 +2412,7 @@ static void service_enter_start(Service *s) {
/* For simple services we immediately start
* the START_POST binaries. */
(void) service_set_main_pid(s, pid);
(void) service_set_main_pid(s, pidref.pid);
service_enter_start_post(s);
} else if (s->type == SERVICE_FORKING) {
@ -2408,7 +2420,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 +2431,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_pid(s, pidref.pid);
service_set_state(s, SERVICE_START);
} else
assert_not_reached();
@ -2480,6 +2493,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,
@ -2571,8 +2585,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 +2598,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,
@ -2634,6 +2649,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,
@ -2664,7 +2681,7 @@ fail:
}
static void service_run_next_main(Service *s) {
pid_t pid;
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
int r;
assert(s);
@ -2679,11 +2696,11 @@ 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);
&pidref);
if (r < 0)
goto fail;
(void) service_set_main_pid(s, pid);
(void) service_set_main_pid(s, pidref.pid);
return;
@ -2930,11 +2947,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 +3185,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;
@ -3723,7 +3738,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 +3754,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 +3766,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 +3897,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 +4329,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);
@ -4382,7 +4397,7 @@ static void service_notify_message(
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) {
else if (!s->main_pid_known || new_main_pid != s->main_pid.pid) {
r = service_is_suitable_main_pid(s, new_main_pid, LOG_WARNING);
if (r == 0) {
@ -4397,7 +4412,7 @@ static void service_notify_message(
if (r > 0) {
(void) service_set_main_pid(s, new_main_pid);
r = unit_watch_pid(UNIT(s), new_main_pid, false);
r = unit_watch_pid(UNIT(s), new_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);
@ -4621,7 +4636,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,
@ -4667,7 +4682,7 @@ static int bus_name_pid_lookup_callback(sd_bus_message *reply, void *userdata, s
log_unit_debug(u, "D-Bus name %s is now owned by process " PID_FMT, s->bus_name, (pid_t) pid);
(void) service_set_main_pid(s, pid);
(void) unit_watch_pid(UNIT(s), pid, false);
(void) unit_watch_pid(UNIT(s), pid, /* exclusive= */ false);
return 1;
}
@ -4798,7 +4813,7 @@ static int service_kill(Unit *u, KillWho who, int signo, int code, int value, sd
assert(s);
return unit_kill_common(u, who, signo, code, value, s->main_pid, s->control_pid, error);
return unit_kill_common(u, who, signo, code, value, s->main_pid.pid, s->control_pid.pid, error);
}
static int service_main_pid(Unit *u) {
@ -4806,7 +4821,7 @@ static int service_main_pid(Unit *u) {
assert(s);
return s->main_pid;
return s->main_pid.pid;
}
static int service_control_pid(Unit *u) {
@ -4814,7 +4829,7 @@ static int service_control_pid(Unit *u) {
assert(s);
return s->control_pid;
return s->control_pid.pid;
}
static bool service_needs_console(Unit *u) {
@ -4874,6 +4889,7 @@ 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);
@ -4914,12 +4930,15 @@ static int service_clean(Unit *u, ExecCleanMask mask) {
if (r < 0)
goto fail;
r = unit_fork_and_watch_rm_rf(u, l, &s->control_pid);
r = unit_fork_and_watch_rm_rf(u, l, &pid);
if (r < 0)
goto fail;
r = pidref_set_pid(&s->control_pid, pid);
if (r < 0)
goto fail;
service_set_state(s, SERVICE_CLEANING);
return 0;
fail:

View File

@ -8,6 +8,7 @@ typedef struct ServiceFDStore ServiceFDStore;
#include "kill.h"
#include "open-file.h"
#include "path.h"
#include "pidref.h"
#include "ratelimit.h"
#include "socket.h"
#include "unit.h"
@ -167,7 +168,7 @@ struct Service {
/* Runtime data of the execution context */
ExecRuntime *exec_runtime;
pid_t main_pid, control_pid;
PidRef main_pid, control_pid;
/* if we are a socket activated service instance, store information of the connection/peer/socket */
int socket_fd;

View File

@ -97,6 +97,7 @@ static void socket_init(Unit *u) {
s->exec_context.std_output = u->manager->default_std_output;
s->exec_context.std_error = u->manager->default_std_error;
s->control_pid = PIDREF_NULL;
s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
s->trigger_limit.interval = USEC_INFINITY;
@ -106,10 +107,11 @@ static void socket_init(Unit *u) {
static void socket_unwatch_control_pid(Socket *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_pid(UNIT(s), s->control_pid.pid);
pidref_done(&s->control_pid);
}
static void socket_cleanup_fd_list(SocketPort *p) {
@ -616,10 +618,10 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
"%sTimestamping: %s\n",
prefix, socket_timestamping_to_string(s->timestamping));
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->bind_to_device)
fprintf(f,
@ -1854,8 +1856,8 @@ static int socket_coldplug(Unit *u) {
if (s->deserialized_state == s->state)
return 0;
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,
SOCKET_START_PRE,
SOCKET_START_CHOWN,
@ -1868,7 +1870,7 @@ static int socket_coldplug(Unit *u) {
SOCKET_FINAL_SIGKILL,
SOCKET_CLEANING)) {
r = unit_watch_pid(UNIT(s), s->control_pid, false);
r = unit_watch_pid(UNIT(s), s->control_pid.pid, /* exclusive= */ false);
if (r < 0)
return r;
@ -1914,7 +1916,7 @@ static int socket_coldplug(Unit *u) {
return 0;
}
static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
static int socket_spawn(Socket *s, ExecCommand *c, PidRef *ret_pid) {
_cleanup_(exec_params_clear) ExecParameters exec_params = {
.flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
@ -1923,12 +1925,13 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
.stderr_fd = -EBADF,
.exec_fd = -EBADF,
};
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
pid_t pid;
int r;
assert(s);
assert(c);
assert(_pid);
assert(ret_pid);
r = unit_prepare_exec(UNIT(s));
if (r < 0)
@ -1952,22 +1955,28 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
if (r < 0)
return r;
r = unit_watch_pid(UNIT(s), pid, true);
r = pidref_set_pid(&pidref, pid);
if (r < 0)
return r;
*_pid = pid;
r = unit_watch_pid(UNIT(s), pidref.pid, /* exclusive= */ true);
if (r < 0)
return r;
*ret_pid = TAKE_PIDREF(pidref);
return 0;
}
static int socket_chown(Socket *s, pid_t *_pid) {
static int socket_chown(Socket *s, PidRef *ret_pid) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
pid_t pid;
int r;
assert(s);
r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;
return r;
/* We have to resolve the user names out-of-process, hence
* let's fork here. It's messy, but well, what can we do? */
@ -2021,16 +2030,16 @@ static int socket_chown(Socket *s, pid_t *_pid) {
_exit(EXIT_SUCCESS);
}
r = unit_watch_pid(UNIT(s), pid, true);
r = pidref_set_pid(&pidref, pid);
if (r < 0)
goto fail;
return r;
*_pid = pid;
r = unit_watch_pid(UNIT(s), pidref.pid, /* exclusive= */ true);
if (r < 0)
return r;
*ret_pid = TAKE_PIDREF(pidref);
return 0;
fail:
s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
return r;
}
static void socket_enter_dead(Socket *s, SocketResult f) {
@ -2069,6 +2078,8 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) {
s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST];
if (s->control_command) {
pidref_done(&s->control_pid);
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0)
goto fail;
@ -2107,7 +2118,7 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
&s->kill_context,
state_to_kill_operation(s, state),
-1,
s->control_pid,
s->control_pid.pid,
false);
if (r < 0)
goto fail;
@ -2150,6 +2161,8 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) {
s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE];
if (s->control_command) {
pidref_done(&s->control_pid);
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0)
goto fail;
@ -2196,6 +2209,8 @@ static void socket_enter_start_post(Socket *s) {
s->control_command = s->exec_command[SOCKET_EXEC_START_POST];
if (s->control_command) {
pidref_done(&s->control_pid);
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-post' task: %m");
@ -2257,6 +2272,8 @@ static void socket_enter_start_pre(Socket *s) {
s->control_command = s->exec_command[SOCKET_EXEC_START_PRE];
if (s->control_command) {
pidref_done(&s->control_pid);
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-pre' task: %m");
@ -2436,6 +2453,8 @@ static void socket_run_next(Socket *s) {
s->control_command = s->control_command->command_next;
pidref_done(&s->control_pid);
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0)
goto fail;
@ -2562,8 +2581,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
(void) serialize_item_format(f, "n-accepted", "%u", s->n_accepted);
(void) serialize_item_format(f, "n-refused", "%u", s->n_refused);
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->control_command_id >= 0)
(void) serialize_item(f, "control-command", socket_exec_command_to_string(s->control_command_id));
@ -2644,12 +2663,10 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
else
s->n_refused += k;
} 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_debug_errno(r, "Failed to pin control PID '%s', ignoring: %m", value);
} else if (streq(key, "control-command")) {
SocketExecCommand id;
@ -3089,10 +3106,10 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
assert(s);
assert(pid >= 0);
if (pid != s->control_pid)
if (pid != s->control_pid.pid)
return;
s->control_pid = 0;
pidref_done(&s->control_pid);
if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = SOCKET_SUCCESS;
@ -3366,7 +3383,7 @@ static void socket_trigger_notify(Unit *u, Unit *other) {
}
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, 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) {
@ -3402,12 +3419,13 @@ static int socket_control_pid(Unit *u) {
assert(s);
return s->control_pid;
return s->control_pid.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);
@ -3432,12 +3450,15 @@ static int socket_clean(Unit *u, ExecCleanMask mask) {
if (r < 0)
goto fail;
r = unit_fork_and_watch_rm_rf(u, l, &s->control_pid);
r = unit_fork_and_watch_rm_rf(u, l, &pid);
if (r < 0)
goto fail;
r = pidref_set_pid(&s->control_pid, pid);
if (r < 0)
goto fail;
socket_set_state(s, SOCKET_CLEANING);
return 0;
fail:

View File

@ -5,6 +5,7 @@ typedef struct Socket Socket;
typedef struct SocketPeer SocketPeer;
#include "mount.h"
#include "pidref.h"
#include "socket-util.h"
#include "unit.h"
@ -103,7 +104,7 @@ struct Socket {
ExecCommand* control_command;
SocketExecCommand control_command_id;
pid_t control_pid;
PidRef control_pid;
mode_t directory_mode;
mode_t socket_mode;

View File

@ -144,6 +144,7 @@ static void swap_init(Unit *u) {
s->exec_context.std_output = u->manager->default_std_output;
s->exec_context.std_error = u->manager->default_std_error;
s->control_pid = PIDREF_NULL;
s->control_command_id = _SWAP_EXEC_COMMAND_INVALID;
u->ignore_on_isolate = true;
@ -152,10 +153,11 @@ static void swap_init(Unit *u) {
static void swap_unwatch_control_pid(Swap *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_pid(UNIT(s), s->control_pid.pid);
pidref_done(&s->control_pid);
}
static void swap_done(Unit *u) {
@ -578,11 +580,11 @@ static int swap_coldplug(Unit *u) {
if (new_state == s->state)
return 0;
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) &&
SWAP_STATE_WITH_PROCESS(new_state)) {
r = unit_watch_pid(UNIT(s), s->control_pid, false);
r = unit_watch_pid(UNIT(s), s->control_pid.pid, /* exclusive= */ false);
if (r < 0)
return r;
@ -642,17 +644,17 @@ static void swap_dump(Unit *u, FILE *f, const char *prefix) {
"%sTimeoutSec: %s\n",
prefix, FORMAT_TIMESPAN(s->timeout_usec, USEC_PER_SEC));
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);
exec_context_dump(&s->exec_context, f, prefix);
kill_context_dump(&s->kill_context, f, prefix);
cgroup_context_dump(UNIT(s), f, prefix);
}
static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
static int swap_spawn(Swap *s, ExecCommand *c, PidRef *ret_pid) {
_cleanup_(exec_params_clear) ExecParameters exec_params = {
.flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
@ -661,12 +663,13 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
.stderr_fd = -EBADF,
.exec_fd = -EBADF,
};
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
pid_t pid;
int r;
assert(s);
assert(c);
assert(_pid);
assert(ret_pid);
r = unit_prepare_exec(UNIT(s));
if (r < 0)
@ -674,11 +677,11 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
r = swap_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;
return r;
r = unit_set_exec_params(UNIT(s), &exec_params);
if (r < 0)
goto fail;
return r;
r = exec_spawn(UNIT(s),
c,
@ -688,20 +691,18 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
&s->cgroup_context,
&pid);
if (r < 0)
goto fail;
return r;
r = unit_watch_pid(UNIT(s), pid, true);
r = pidref_set_pid(&pidref, pid);
if (r < 0)
goto fail;
return r;
*_pid = pid;
r = unit_watch_pid(UNIT(s), pidref.pid, /* exclusive= */ true);
if (r < 0)
return r;
*ret_pid = TAKE_PIDREF(pidref);
return 0;
fail:
s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
return r;
}
static void swap_enter_dead(Swap *s, SwapResult f) {
@ -766,7 +767,7 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
&s->kill_context,
state_to_kill_operation(s, state),
-1,
s->control_pid,
s->control_pid.pid,
false);
if (r < 0)
goto fail;
@ -973,8 +974,8 @@ static int swap_serialize(Unit *u, FILE *f, FDSet *fds) {
(void) serialize_item(f, "state", swap_state_to_string(s->state));
(void) serialize_item(f, "result", swap_result_to_string(s->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->control_command_id >= 0)
(void) serialize_item(f, "control-command", swap_exec_command_to_string(s->control_command_id));
@ -984,6 +985,7 @@ static int swap_serialize(Unit *u, FILE *f, FDSet *fds) {
static int swap_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
Swap *s = SWAP(u);
int r;
assert(s);
assert(fds);
@ -1005,12 +1007,11 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
else if (f != SWAP_SUCCESS)
s->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_debug_errno(r, "Failed to pin control PID '%s', ignoring: %m", value);
} else if (streq(key, "control-command")) {
SwapExecCommand id;
@ -1035,14 +1036,14 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
assert(s);
assert(pid >= 0);
if (pid != s->control_pid)
if (pid != s->control_pid.pid)
return;
/* Let's scan /proc/swaps before we process SIGCHLD. For the reasoning see the similar code in
* mount.c */
(void) swap_process_proc_swaps(u->manager);
s->control_pid = 0;
pidref_done(&s->control_pid);
if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = SWAP_SUCCESS;
@ -1475,7 +1476,7 @@ static void swap_reset_failed(Unit *u) {
}
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, 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) {
@ -1519,12 +1520,13 @@ static int swap_control_pid(Unit *u) {
assert(s);
return s->control_pid;
return s->control_pid.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);
@ -1549,12 +1551,15 @@ static int swap_clean(Unit *u, ExecCleanMask mask) {
if (r < 0)
goto fail;
r = unit_fork_and_watch_rm_rf(u, l, &s->control_pid);
r = unit_fork_and_watch_rm_rf(u, l, &pid);
if (r < 0)
goto fail;
r = pidref_set_pid(&s->control_pid, pid);
if (r < 0)
goto fail;
swap_set_state(s, SWAP_CLEANING);
return 0;
fail:

View File

@ -6,6 +6,8 @@
***/
#include "sd-device.h"
#include "pidref.h"
#include "unit.h"
typedef struct Swap Swap;
@ -73,7 +75,7 @@ struct Swap {
ExecCommand* control_command;
SwapExecCommand control_command_id;
pid_t control_pid;
PidRef control_pid;
sd_event_source *timer_event_source;

View File

@ -239,7 +239,7 @@ int manager_handle_action(
manager_is_inhibited(m, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0, &offending)) {
_cleanup_free_ char *comm = NULL, *u = NULL;
(void) get_process_comm(offending->pid, &comm);
(void) get_process_comm(offending->pid.pid, &comm);
u = uid_to_name(offending->uid);
/* If this is just a recheck of the lid switch then don't warn about anything */
@ -248,7 +248,7 @@ int manager_handle_action(
handle_action_to_string(handle),
inhibit_what_to_string(inhibit_operation),
offending->uid, strna(u),
offending->pid, strna(comm));
offending->pid.pid, strna(comm));
return is_edge ? -EPERM : 0;
}

View File

@ -665,7 +665,7 @@ static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bu
strempty(inhibitor->why),
strempty(inhibit_mode_to_string(inhibitor->mode)),
(uint32_t) inhibitor->uid,
(uint32_t) inhibitor->pid);
(uint32_t) inhibitor->pid.pid);
if (r < 0)
return r;
}
@ -1625,12 +1625,12 @@ int manager_dispatch_delayed(Manager *manager, bool timeout) {
if (!timeout)
return 0;
(void) get_process_comm(offending->pid, &comm);
(void) get_process_comm(offending->pid.pid, &comm);
u = uid_to_name(offending->uid);
log_notice("Delay lock is active (UID "UID_FMT"/%s, PID "PID_FMT"/%s) but inhibitor timeout is reached.",
offending->uid, strna(u),
offending->pid, strna(comm));
offending->pid.pid, strna(comm));
}
/* Actually do the operation */
@ -3207,6 +3207,7 @@ static int method_set_wall_message(
static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
const char *who, *why, *what, *mode;
_cleanup_free_ char *id = NULL;
_cleanup_close_ int fifo_fd = -EBADF;
@ -3279,6 +3280,10 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
if (r < 0)
return r;
r = pidref_set_pid(&pidref, pid);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed pin source process "PID_FMT": %m", pid);
if (hashmap_size(m->inhibitors) >= m->inhibitors_max)
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED,
"Maximum number of inhibitors (%" PRIu64 ") reached, refusing further inhibitors.",
@ -3299,7 +3304,7 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
i->what = w;
i->mode = mm;
i->pid = pid;
i->pid = TAKE_PIDREF(pidref);
i->uid = uid;
i->why = strdup(why);
i->who = strdup(who);

View File

@ -47,6 +47,7 @@ int inhibitor_new(Inhibitor **ret, Manager *m, const char* id) {
.mode = _INHIBIT_MODE_INVALID,
.uid = UID_INVALID,
.fifo_fd = -EBADF,
.pid = PIDREF_NULL,
};
i->state_file = path_join("/run/systemd/inhibit", id);
@ -81,6 +82,8 @@ Inhibitor* inhibitor_free(Inhibitor *i) {
free(i->state_file);
free(i->fifo_path);
pidref_done(&i->pid);
return mfree(i);
}
@ -110,7 +113,7 @@ static int inhibitor_save(Inhibitor *i) {
inhibit_what_to_string(i->what),
inhibit_mode_to_string(i->mode),
i->uid,
i->pid);
i->pid.pid);
if (i->who) {
_cleanup_free_ char *cc = NULL;
@ -177,7 +180,7 @@ int inhibitor_start(Inhibitor *i) {
log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.",
strna(i->who), strna(i->why),
i->pid, i->uid,
i->pid.pid, i->uid,
inhibit_mode_to_string(i->mode));
i->started = true;
@ -195,7 +198,7 @@ void inhibitor_stop(Inhibitor *i) {
if (i->started)
log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.",
strna(i->who), strna(i->why),
i->pid, i->uid,
i->pid.pid, i->uid,
inhibit_mode_to_string(i->mode));
inhibitor_remove_fifo(i);
@ -242,7 +245,8 @@ int inhibitor_load(Inhibitor *i) {
}
if (pid) {
r = parse_pid(pid, &i->pid);
pidref_done(&i->pid);
r = pidref_set_pidstr(&i->pid, pid);
if (r < 0)
log_debug_errno(r, "Failed to parse PID of inhibitor: %s", pid);
}
@ -417,7 +421,7 @@ bool manager_is_inhibited(
if (i->mode != mm)
continue;
if (ignore_inactive && pid_is_active(m, i->pid) <= 0)
if (ignore_inactive && pid_is_active(m, i->pid.pid) <= 0)
continue;
if (ignore_uid && i->uid == uid)

View File

@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "pidref.h"
typedef struct Inhibitor Inhibitor;
typedef enum InhibitWhat {
@ -40,7 +42,7 @@ struct Inhibitor {
char *why;
InhibitMode mode;
pid_t pid;
PidRef pid;
uid_t uid;
dual_timestamp since;

View File

@ -877,7 +877,7 @@ static const sd_bus_vtable session_vtable[] = {
SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Desktop", "s", NULL, offsetof(Session, desktop), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader.pid), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST),

View File

@ -66,6 +66,7 @@ int session_new(Session **ret, Manager *m, const char *id) {
.vtfd = -EBADF,
.audit_id = AUDIT_SESSION_INVALID,
.tty_validity = _TTY_VALIDITY_INVALID,
.leader = PIDREF_NULL,
};
s->state_file = path_join("/run/systemd/sessions", id);
@ -128,8 +129,10 @@ Session* session_free(Session *s) {
free(s->scope);
}
if (pid_is_valid(s->leader))
(void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader), s);
if (pidref_is_set(&s->leader)) {
(void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader.pid), s);
pidref_done(&s->leader);
}
free(s->scope_job);
@ -168,6 +171,7 @@ void session_set_user(Session *s, User *u) {
}
int session_set_leader(Session *s, pid_t pid) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
int r;
assert(s);
@ -175,18 +179,24 @@ int session_set_leader(Session *s, pid_t pid) {
if (!pid_is_valid(pid))
return -EINVAL;
if (s->leader == pid)
if (s->leader.pid == pid)
return 0;
r = hashmap_put(s->manager->sessions_by_leader, PID_TO_PTR(pid), s);
r = pidref_set_pid(&pidref, pid);
if (r < 0)
return r;
if (pid_is_valid(s->leader))
(void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader), s);
r = hashmap_put(s->manager->sessions_by_leader, PID_TO_PTR(pidref.pid), s);
if (r < 0)
return r;
s->leader = pid;
(void) audit_session_from_pid(pid, &s->audit_id);
if (pidref_is_set(&s->leader)) {
(void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader.pid), s);
pidref_done(&s->leader);
}
s->leader = TAKE_PIDREF(pidref);
(void) audit_session_from_pid(s->leader.pid, &s->audit_id);
return 1;
}
@ -323,8 +333,8 @@ int session_save(Session *s) {
if (!s->vtnr)
fprintf(f, "POSITION=%u\n", s->position);
if (pid_is_valid(s->leader))
fprintf(f, "LEADER="PID_FMT"\n", s->leader);
if (pidref_is_set(&s->leader))
fprintf(f, "LEADER="PID_FMT"\n", s->leader.pid);
if (audit_session_is_valid(s->audit_id))
fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id);
@ -674,7 +684,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er
r = manager_start_scope(
s->manager,
scope,
s->leader,
s->leader.pid,
s->user->slice,
description,
/* These two have StopWhenUnneeded= set, hence add a dep towards them */
@ -779,7 +789,7 @@ int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) {
"MESSAGE_ID=" SD_MESSAGE_SESSION_START_STR,
"SESSION_ID=%s", s->id,
"USER_ID=%s", s->user->user_record->user_name,
"LEADER="PID_FMT, s->leader,
"LEADER="PID_FMT, s->leader.pid,
LOG_MESSAGE("New session %s of user %s.", s->id, s->user->user_record->user_name));
if (!dual_timestamp_is_set(&s->timestamp))
@ -849,7 +859,7 @@ static int session_stop_scope(Session *s, bool force) {
log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
"SESSION_ID=%s", s->id,
"USER_ID=%s", s->user->user_record->user_name,
"LEADER="PID_FMT, s->leader,
"LEADER="PID_FMT, s->leader.pid,
LOG_MESSAGE("Session %s logged out. Waiting for processes to exit.", s->id));
}
@ -907,7 +917,7 @@ int session_finalize(Session *s) {
"MESSAGE_ID=" SD_MESSAGE_SESSION_STOP_STR,
"SESSION_ID=%s", s->id,
"USER_ID=%s", s->user->user_record->user_name,
"LEADER="PID_FMT, s->leader,
"LEADER="PID_FMT, s->leader.pid,
LOG_MESSAGE("Removed session %s.", s->id));
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
@ -1036,8 +1046,8 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) {
/* For sessions with a leader but no explicitly configured tty, let's check the controlling tty of
* the leader */
if (pid_is_valid(s->leader)) {
r = get_process_ctty_atime(s->leader, &atime);
if (pidref_is_set(&s->leader)) {
r = get_process_ctty_atime(s->leader.pid, &atime);
if (r >= 0)
goto found_atime;
}

View File

@ -7,6 +7,7 @@ typedef enum KillWho KillWho;
#include "list.h"
#include "login-util.h"
#include "logind-user.h"
#include "pidref.h"
#include "string-util.h"
typedef enum SessionState {
@ -87,7 +88,7 @@ struct Session {
unsigned vtnr;
int vtfd;
pid_t leader;
PidRef leader;
uint32_t audit_id;
int fifo_fd;

View File

@ -233,7 +233,7 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
if (r < 0)
return r;
p = procfs_file_alloca(m->leader, "ns/net");
p = procfs_file_alloca(m->leader.pid, "ns/net");
r = readlink_malloc(p, &them);
if (r < 0)
return r;
@ -241,7 +241,7 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd
if (streq(us, them))
return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL);
r = namespace_open(m->leader.pid, NULL, NULL, &netns_fd, NULL, NULL);
if (r < 0)
return r;
@ -375,7 +375,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
_cleanup_fclose_ FILE *f = NULL;
pid_t child;
r = namespace_open(m->leader, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd);
r = namespace_open(m->leader.pid, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd);
if (r < 0)
return r;
@ -496,7 +496,7 @@ static int container_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
if (r < 0)
return r;
if (asprintf(&address, "x-machine-unix:pid=%" PID_PRI, m->leader) < 0)
if (asprintf(&address, "x-machine-unix:pid=%" PID_PRI, m->leader.pid) < 0)
return -ENOMEM;
bus->address = address;
@ -880,10 +880,13 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied.");
propagate_directory = strjoina("/run/systemd/nspawn/propagate/", m->name);
r = bind_mount_in_namespace(m->leader,
propagate_directory,
"/run/host/incoming/",
src, dest, read_only, make_file_or_directory);
r = bind_mount_in_namespace(
m->leader.pid,
propagate_directory,
"/run/host/incoming/",
src, dest,
read_only,
make_file_or_directory);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to mount %s on %s in machine's namespace: %m", src, dest);
@ -997,7 +1000,7 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
q = procfs_file_alloca(m->leader, "ns/mnt");
q = procfs_file_alloca(m->leader.pid, "ns/mnt");
mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (mntfd < 0) {
r = log_error_errno(errno, "Failed to open mount namespace of leader: %m");
@ -1093,7 +1096,7 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
_cleanup_close_pair_ int pair[2] = PIPE_EBADF;
pid_t child;
r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
r = namespace_open(m->leader.pid, NULL, &mntns_fd, NULL, NULL, &root_fd);
if (r < 0)
return r;
@ -1266,7 +1269,7 @@ static const sd_bus_vtable machine_vtable[] = {
SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader.pid), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),

View File

@ -49,10 +49,14 @@ int machine_new(Manager *manager, MachineClass class, const char *name, Machine
* means as much as "we don't know yet", and that we'll figure
* it out later when loading the state file. */
m = new0(Machine, 1);
m = new(Machine, 1);
if (!m)
return -ENOMEM;
*m = (Machine) {
.leader = PIDREF_NULL,
};
m->name = strdup(name);
if (!m->name)
return -ENOMEM;
@ -94,8 +98,10 @@ Machine* machine_free(Machine *m) {
if (m->manager->host_machine == m)
m->manager->host_machine = NULL;
if (m->leader > 0)
(void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
if (pidref_is_set(&m->leader)) {
(void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader.pid), m);
pidref_done(&m->leader);
}
sd_bus_message_unref(m->create_message);
@ -175,8 +181,8 @@ int machine_save(Machine *m) {
if (!sd_id128_is_null(m->id))
fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
if (m->leader != 0)
fprintf(f, "LEADER="PID_FMT"\n", m->leader);
if (pidref_is_set(&m->leader))
fprintf(f, "LEADER="PID_FMT"\n", m->leader.pid);
if (m->class != _MACHINE_CLASS_INVALID)
fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
@ -272,10 +278,14 @@ int machine_load(Machine *m) {
return log_error_errno(r, "Failed to read %s: %m", m->state_file);
if (id)
sd_id128_from_string(id, &m->id);
(void) sd_id128_from_string(id, &m->id);
if (leader)
parse_pid(leader, &m->leader);
if (leader) {
pidref_done(&m->leader);
r = pidref_set_pidstr(&m->leader, leader);
if (r < 0)
log_debug_errno(r, "Failed to set leader PID to '%s', ignoring: %m", leader);
}
if (class) {
MachineClass c;
@ -337,7 +347,7 @@ static int machine_start_scope(
int r;
assert(machine);
assert(machine->leader > 0);
assert(pidref_is_set(&machine->leader));
assert(!machine->unit);
escaped = unit_name_escape(machine->name);
@ -374,7 +384,7 @@ static int machine_start_scope(
return r;
r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)(sv)",
"PIDs", "au", 1, machine->leader,
"PIDs", "au", 1, machine->leader.pid,
"Delegate", "b", 1,
"CollectMode", "s", "inactive-or-failed",
"AddRef", "b", 1,
@ -440,7 +450,7 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
if (m->started)
return 0;
r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader.pid), m);
if (r < 0)
return r;
@ -452,7 +462,7 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_MACHINE_START_STR,
"NAME=%s", m->name,
"LEADER="PID_FMT, m->leader,
"LEADER="PID_FMT, m->leader.pid,
LOG_MESSAGE("New machine %s.", m->name));
if (!dual_timestamp_is_set(&m->timestamp))
@ -503,7 +513,7 @@ int machine_finalize(Machine *m) {
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_MACHINE_STOP_STR,
"NAME=%s", m->name,
"LEADER="PID_FMT, m->leader,
"LEADER="PID_FMT, m->leader.pid,
LOG_MESSAGE("Machine %s terminated.", m->name));
m->stopping = true; /* The machine is supposed to be going away. Don't try to kill it. */
@ -573,7 +583,7 @@ int machine_kill(Machine *m, KillWho who, int signo) {
return -ESRCH;
if (who == KILL_LEADER) /* If we shall simply kill the leader, do so directly */
return RET_NERRNO(kill(m->leader, signo));
return pidref_kill(&m->leader, signo);
/* Otherwise, make PID 1 do it for us, for the entire cgroup */
return manager_kill_unit(m->manager, m->unit, signo, NULL);
@ -585,14 +595,13 @@ int machine_openpt(Machine *m, int flags, char **ret_slave) {
switch (m->class) {
case MACHINE_HOST:
return openpt_allocate(flags, ret_slave);
case MACHINE_CONTAINER:
if (m->leader <= 0)
if (!pidref_is_set(&m->leader))
return -EINVAL;
return openpt_allocate_in_namespace(m->leader, flags, ret_slave);
return openpt_allocate_in_namespace(m->leader.pid, flags, ret_slave);
default:
return -EOPNOTSUPP;
@ -608,10 +617,10 @@ int machine_open_terminal(Machine *m, const char *path, int mode) {
return open_terminal(path, mode);
case MACHINE_CONTAINER:
if (m->leader <= 0)
if (!pidref_is_set(&m->leader))
return -EINVAL;
return open_terminal_in_namespace(m->leader, path, mode);
return open_terminal_in_namespace(m->leader.pid, path, mode);
default:
return -EOPNOTSUPP;
@ -664,7 +673,7 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
if (m->class != MACHINE_CONTAINER)
return -EOPNOTSUPP;
xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader);
xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader.pid);
f = fopen(p, "re");
if (!f) {
if (errno == ENOENT) {
@ -702,7 +711,7 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
fclose(f);
xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader);
xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader.pid);
f = fopen(p, "re");
if (!f)
return -errno;
@ -756,7 +765,7 @@ static int machine_owns_uid_internal(
if (machine->class != MACHINE_CONTAINER)
goto negative;
p = procfs_file_alloca(machine->leader, map_file);
p = procfs_file_alloca(machine->leader.pid, map_file);
f = fopen(p, "re");
if (!f) {
log_debug_errno(errno, "Failed to open %s, ignoring.", p);
@ -830,7 +839,7 @@ static int machine_translate_uid_internal(
/* Translates a machine UID into a host UID */
p = procfs_file_alloca(machine->leader, map_file);
p = procfs_file_alloca(machine->leader.pid, map_file);
f = fopen(p, "re");
if (!f)
return -errno;

View File

@ -7,6 +7,7 @@ typedef enum KillWho KillWho;
#include "list.h"
#include "machined.h"
#include "operation.h"
#include "pidref.h"
#include "time-util.h"
typedef enum MachineState {
@ -47,7 +48,7 @@ struct Machine {
char *unit;
char *scope_job;
pid_t leader;
PidRef leader;
dual_timestamp timestamp;

View File

@ -219,6 +219,7 @@ static int method_list_machines(sd_bus_message *message, void *userdata, sd_bus_
}
static int method_create_or_register_machine(Manager *manager, sd_bus_message *message, bool read_network, Machine **_m, sd_bus_error *error) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
const char *name, *service, *class, *root_directory;
const int32_t *netif = NULL;
MachineClass c;
@ -294,6 +295,10 @@ static int method_create_or_register_machine(Manager *manager, sd_bus_message *m
return r;
}
r = pidref_set_pid(&pidref, leader);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to pin process " PID_FMT ": %m", pidref.pid);
if (hashmap_get(manager->machines, name))
return sd_bus_error_setf(error, BUS_ERROR_MACHINE_EXISTS, "Machine '%s' already exists", name);
@ -301,7 +306,7 @@ static int method_create_or_register_machine(Manager *manager, sd_bus_message *m
if (r < 0)
return r;
m->leader = leader;
m->leader = TAKE_PIDREF(pidref);
m->class = c;
m->id = id;
@ -388,11 +393,11 @@ static int method_register_machine_internal(sd_bus_message *message, bool read_n
if (r < 0)
return r;
r = cg_pid_get_unit(m->leader, &m->unit);
r = cg_pid_get_unit(m->leader.pid, &m->unit);
if (r < 0) {
r = sd_bus_error_set_errnof(error, r,
"Failed to determine unit of process "PID_FMT" : %m",
m->leader);
m->leader.pid);
goto fail;
}

View File

@ -107,6 +107,7 @@ static Manager* manager_unref(Manager *m) {
}
static int manager_add_host_machine(Manager *m) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
_cleanup_free_ char *rd = NULL, *unit = NULL;
sd_id128_t mid;
Machine *t;
@ -127,11 +128,15 @@ static int manager_add_host_machine(Manager *m) {
if (!unit)
return log_oom();
r = pidref_set_pid(&pidref, 1);
if (r < 0)
return log_error_errno(r, "Failed to open reference to PID 1: %m");
r = machine_new(m, MACHINE_HOST, ".host", &t);
if (r < 0)
return log_error_errno(r, "Failed to create machine: %m");
t->leader = 1;
t->leader = TAKE_PIDREF(pidref);
t->id = mid;
t->root_directory = TAKE_PTR(rd);