Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: first step towards a pidfd focused future #29142

Merged
merged 9 commits into from Sep 9, 2023
21 changes: 16 additions & 5 deletions TODO
Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/basic/meson.build
Expand Up @@ -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',
Expand Down
145 changes: 145 additions & 0 deletions src/basic/pidref.c
@@ -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 changes: 29 additions & 0 deletions src/basic/pidref.h
@@ -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;
bluca marked this conversation as resolved.
Show resolved Hide resolved
}

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)
2 changes: 1 addition & 1 deletion src/core/dbus-mount.c
Expand Up @@ -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),
Expand Down
4 changes: 2 additions & 2 deletions src/core/dbus-service.c
Expand Up @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion src/core/dbus-swap.c
Expand Up @@ -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),
Expand Down