workaround for waitpid
Make a Github release / release (push) Skipped

This commit is contained in:
2026-10-02 00:12:54 +03:00
parent 3f08204f92
commit 9e79e97458
5 changed files with 42 additions and 9 deletions
+11 -3
View File
@@ -17,10 +17,11 @@ because upstream code was refactored since the fallback was removed.
| `mount_option_supported()` | `fsopen()` (5.2) | **revert** of part of `c2198d0c3f`: return `-EAGAIN` ("don't know") when the new mount API is unavailable. | `src/basic/mountpoint-util.c` |
| `close_all_fds*()` (every fork/exec, `systemd-executor`) | `close_range()` (5.9) | **revert** of `c471cca093` and `d58c826c54`: fall back to iterating `/proc/self/fd` (or the frugal loop). | `src/basic/fd-util.c`, `src/basic/fd-util.h`, `src/test/test-fd-util.c` |
| `pidref_set_pid()` (every process PID1 tracks, `safe_fork()`, `posix_spawn_wrapper()`) | `pidfd_open()` (5.3) | **revert** (adjusted) of part of `6e14c46bac`: ENOSYS ⇒ PidRef without fd (plain PID). | `src/basic/pidref.c` |
| `pidfd_open()` shim (all pidfd users), `pidfd_waitid()` | `waitid(P_PIDFD)` (5.4) | **new**: vendor kernels (Android 4.19) backport `pidfd_open()` but not `waitid(P_PIDFD)`, so every wait failed with EINVAL (PID1 froze after generators). The shim probes once on a pidfd of self that `pidfd_send_signal()` and `poll()` work (without pidfd poll support a live process reports readable); if not, `pidfd_open()` returns ENOSYS so the plain-PID fallbacks above apply. Waits go through `pidfd_waitid()`, which probes `waitid(P_PIDFD)` (ECHILD = supported, EINVAL = not) and otherwise uses `waitid(P_PID)` with the known PID (safe for children: the PID cannot be reused before reaping). Keeping pidfds is required by logind, which since v258 refuses sessions whose leader has no pidfd (`NoSessionPIDFD`). | `src/libc/pidfd.c`, `src/include/override/sys/pidfd.h`, `src/basic/pidref.c`, `src/libsystemd/sd-event/sd-event.c` |
| `pidref_wait_for_terminate_full()` with timeout | pidfd (5.3) | **new**: without pidfd poll `waitid(P_PID, …, WNOHANG)` every 10 ms instead of returning `-ENOMEDIUM`. | `src/basic/pidref.c` |
| `pidfd_check_pidfs()`, `pidfd_info_mask_is_supported()`, `pidfd_get_inode_id_self_cached()` | pidfd (5.3) | **revert**/new: ENOSYS ⇒ "not supported" instead of hard error. | `src/basic/pidfd-util.c` |
| sd-event child sources (`sd_event_add_child()`, used by udevd, logind, …) | pidfd (5.3), `waitid(P_PIDFD)` (5.4) | **new** (upstream removed the non-pidfd path in `6e14c46bac` and later made the code pidfd-only): if `pidfd_open()` is unsupported the source works on the PID via `waitid(P_PID)` + SIGCHLD signalfd. SIGCHLD is blocked automatically in that case (upstream no longer requires callers to block it). `send_child_signal()` falls back to `kill()`/`rt_sigqueueinfo()`; `set_child_pidfd_own()` returns `-EOPNOTSUPP` without pidfd. | `src/libsystemd/sd-event/sd-event.c` |
| `pam_systemd` session creation | pidfd (5.3) | **revert** of part of `6e14c46bac`: fall back to `CreateSession` (PID based). | `src/login/pam_systemd.c` |
| `pam_systemd` session creation | pidfd (5.3) | **revert** of part of `6e14c46bac`: fall back to `CreateSession` (PID based). Ineffective on its own: logind (Varlink and D-Bus) rejects leaders without pidfd, so sessions need a kernel with `pidfd_open()` + pidfd poll. | `src/login/pam_systemd.c` |
| Unit cgroup-empty tracking (`unit_check_cgroup_events()`) | `frozen` key in `cgroup.events` (5.2) | **new**: if the key is missing, read only `populated`. Without this every cgroup event read failed and stopped units were never noticed as empty. | `src/core/cgroup.c` |
| Freeze/thaw | cgroup v2 freezer (5.2) | **revert** of `3c86a935ad`: `cg_freezer_supported()` restored; freeze is `-EOPNOTSUPP` / no-op when `cgroup.freeze` is absent. | `src/basic/cgroup-util.[ch]`, `src/core/cgroup.c`, `src/core/unit.c` |
| Credentials (`LoadCredential=`, `SetCredential=`, `ImportCredential=` — used by many stock units) | new mount API `fsopen`/`fsmount`/`move_mount` (5.2) | **new**: if the credentials fs cannot be created because the API is missing, use the existing "plain directory" fallback (previously only used on EPERM). | `src/core/exec-credential.c` |
@@ -64,7 +65,10 @@ because upstream code was refactored since the fallback was removed.
EINVAL workaround (`00c4851af0`), expect warnings when used.
- **udev net_id** for IP-over-InfiniBand (`dev_port`, 4.20): `d3d375a686` not reverted; multi-port
IPoIB devices may get colliding names.
- `test-event`'s `test_pidfd` inherently requires pidfds and fails without them.
- `test-event`'s `test_pidfd` inherently requires pidfds and fails without them; it and
`test_child_pidfd_wnowait` also call `waitid(P_PIDFD)` directly and fail without it.
- logind sessions require pidfds (see `pam_systemd` row); on kernels without `pidfd_open()` no
login session can be created.
- Anything else newer than 5.10 listed in `README` as "recommended baseline" is expected to fall
back gracefully upstream, but that has not been verified on a real old kernel.
@@ -77,4 +81,8 @@ because upstream code was refactored since the fallback was removed.
(with the built generators) under an `LD_PRELOAD` shim that simulates an old kernel: strips
`STATX_MNT_ID*`/`STATX_ATTR_MOUNT_ROOT` from `statx()` results and makes `pidfd_open`,
`pidfd_send_signal`, `pidfd_spawn`, `close_range` and the new mount API return ENOSYS.
- **Not** booted on a real 4.19 kernel yet.
- `test-pidref`, `test-process-util`, `test-event` (except `test_pidfd`, `test_child_pidfd_wnowait`)
under an `LD_PRELOAD` shim that only makes `waitid(P_PIDFD)` fail with EINVAL (Android 4.19
vendor kernel case: pidfds stay enabled, waits use the `P_PID` fallback).
- Booted on an Android 4.19 vendor kernel (Xiaomi vince): reaches `graphical.target`; login
sessions not verified yet.
+1 -1
View File
@@ -522,7 +522,7 @@ int pidref_wait_for_terminate_full(PidRef *pidref, usec_t timeout, siginfo_t *re
siginfo_t si = {};
if (pidref->fd >= 0)
r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, WEXITED));
r = RET_NERRNO(pidfd_waitid(pidref->fd, pidref->pid, &si, WEXITED));
else
r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, WEXITED));
if (r >= 0) {
+3
View File
@@ -11,6 +11,9 @@ int pidfd_open_shim(pid_t pid, unsigned flags);
int pidfd_open_checked(pid_t pid, unsigned flags);
#define pidfd_open pidfd_open_checked
/* ANCIENT-MOD: waitid(P_PIDFD) (kernel v5.4) with waitid(P_PID) fallback. */
int pidfd_waitid(int fd, pid_t pid, siginfo_t *info, int options);
/* Defined since glibc-2.36.
* Supported since kernel v5.1 (3eb39f47934f9d5a3027fe00d906a45fe3a15fad). */
int pidfd_send_signal_shim(int fd, int sig, siginfo_t *info, unsigned flags);
+25 -3
View File
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <poll.h>
#include <sys/pidfd.h>
#include <sys/wait.h>
@@ -10,7 +11,8 @@ DEFINE_SYSCALL_SHIM(pidfd_open, int,
unsigned, flags)
/* ANCIENT-MOD: vendor kernels < 5.4 (e.g. Android 4.19) may backport pidfd_open() without
* waitid(P_PIDFD); report pidfds as unsupported there so the plain-PID fallbacks are used. */
* waitid(P_PIDFD). Pidfds are reported usable only if they can be signalled and polled (both needed
* by logind and sd-event); waits go through pidfd_waitid(), which falls back to waitid(P_PID). */
int pidfd_open_checked(pid_t pid, unsigned flags) {
static int usable = -1;
@@ -22,8 +24,9 @@ int pidfd_open_checked(pid_t pid, unsigned flags) {
return fd;
}
siginfo_t si = {};
usable = waitid(P_PIDFD, fd, &si, WEXITED|WNOHANG) >= 0 || errno != EINVAL;
/* Without pidfd poll support the default poll mask reports a live process as readable */
struct pollfd pfd = { .fd = fd, .events = POLLIN };
usable = pidfd_send_signal_shim(fd, 0, NULL, 0) >= 0 && poll(&pfd, 1, 0) == 0;
close(fd);
}
@@ -35,6 +38,25 @@ int pidfd_open_checked(pid_t pid, unsigned flags) {
return pidfd_open_shim(pid, flags);
}
int pidfd_waitid(int fd, pid_t pid, siginfo_t *info, int options) {
static int supported = -1;
if (supported < 0) {
int self = pidfd_open_shim(getpid(), 0);
if (self < 0)
return -1;
siginfo_t si = {};
supported = waitid(P_PIDFD, self, &si, WEXITED|WNOHANG) >= 0 || errno != EINVAL;
close(self);
}
if (supported)
return waitid(P_PIDFD, fd, info, options);
return waitid(P_PID, pid, info, options);
}
DEFINE_SYSCALL_SHIM(pidfd_send_signal, int,
int, fd,
int, sig,
+2 -2
View File
@@ -61,7 +61,7 @@ static int child_waitid(sd_event_source *s, siginfo_t *si, int options) {
assert(s);
if (s->child.pidfd >= 0)
return waitid(P_PIDFD, s->child.pidfd, si, options);
return pidfd_waitid(s->child.pidfd, s->child.pid, si, options);
return waitid(P_PID, s->child.pid, si, options);
}
@@ -3867,7 +3867,7 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
* be set to pending, and we would have returned early above. */
zero(s->child.siginfo);
if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0)
if (pidfd_waitid(s->child.pidfd, s->child.pid, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0)
return -errno;
if (s->child.siginfo.si_pid == 0)