Files
systemd/ANCIENT-MOD.md
T
SymbX 9e79e97458
Make a Github release / release (push) Skipped
workaround for waitpid
2026-10-02 00:12:54 +03:00

10 KiB
Raw Blame History

ANCIENT-MOD: running on old kernels (target: 4.19)

Upstream requires kernel ≥ 5.10 (see README, "REQUIREMENTS") and dropped the fallback code for older kernels. This branch restores or adds fallbacks so the system can at least boot on 4.19. Code touched by this effort is marked with ANCIENT-MOD comments where practical.

Legend: revert = reverse-applied an upstream commit (possibly adjusted), new = new code because upstream code was refactored since the fallback was removed.

Boot-critical

Area Kernel feature Change Where
Mount point checks (is_mount_point_at(), path_is_mount_point()), used by early mount_setup() for /proc, /sys, /dev with MNT_FATAL STATX_ATTR_MOUNT_ROOT (5.8) new: xstatx_full() emulates the attribute when it is required but missing: compares the mount id (from /proc/self/fdinfo) of the inode with its parent's (.. for directories, parent path for others); same inode as parent ⇒ root ⇒ mount point. Replaces upstream fallback removed in a98a6eb95c. src/basic/stat-util.c (xstatx_emulate())
chase() (used everywhere), path_get_mnt_id_at(), fds_inode_and_mount_same(), XSTATX_MNT_ID_BEST STATX_MNT_ID (5.8/5.10) new: same emulation fills stx_mnt_id from /proc/self/fdinfo/<fd> mnt_id: (available since 3.15). Replaces removals in 7ecfa87c56, e5a3127339. src/basic/stat-util.c
cgroup2 mount memory_recursiveprot (5.7) revert (adjusted) of 80c5b63db5: option is probed again instead of always passed; if it cannot be probed because fsopen() is missing (< 5.2) it is not used. src/shared/mount-setup.c
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). 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
ProtectProc= / ProcSubset= (used by logind, networkd, timesyncd, hostnamed, …) per-instance procfs, textual hidepid=, subset= (5.8) new: on kernels < 5.8 the options are ignored (they would either fail with EINVAL or, worse, change the host's shared /proc superblock). /proc/sys & friends are then still protected by the normal ProtectKernel* mounts. Upstream removed the old fallback in 0cba5bdcf1. src/core/namespace.c
RootDirectory= open_tree() (5.2) new: tolerate ENOSYS when cloning the root directory mount and use the classic bind mount path. src/core/namespace.c
futimens_opath(), access_fd() AT_EMPTY_PATH for utimensat()/faccessat() (5.8) revert of 52bf232e82 (manually): fall back to /proc/self/fd/<fd>. src/basic/fs-util.c

Networking

Area Kernel feature Change Where
socket_bind_to_ifindex() (DHCP client, resolved, networkd, …) SO_BINDTOIFINDEX (5.1) revert of ddbbc98bd5 (manually): fall back to SO_BINDTODEVICE. src/basic/socket-util.c
rtnl_resolve_ifname_full() (alternative names) IFLA_ALT_IFNAME lookup (5.5) revert of 42b09813f4: treat EINVAL like ENODEV. src/libsystemd/sd-netlink/netlink-util.c
networkd startup enumeration nexthop (5.3) revert of 034b69d8b8 (manually): ignore -EOPNOTSUPP from nexthop enumeration, otherwise networkd refuses to start. src/network/networkd-manager.c
networkd permanent MAC address IFLA_PERM_ADDRESS (5.6) revert of 04f05d9bd7: fall back to ethtool. src/network/networkd-link.c
IPv6 DNAT (nspawn port forwarding / firewall-util) nft concatenation fix (5.8) revert of 58fb46ecd5 (manually): -EOVERFLOW ⇒ -EOPNOTSUPP. src/shared/firewall-util.c

Other

Area Kernel feature Change Where
SurviveFinalKillSignal= user.* xattrs on cgroupfs (5.7) revert of 8292b859f3: fall back to trusted.survive_final_kill_signal. src/core/cgroup.c, src/shared/killall.c
Loop devices via LOOP_SET_STATUS64 fallback buffer cache invalidation bug (fixed in 5.0) revert of 5e3887e6c1: issue BLKFLSBUF when offset/size is set. src/shared/loop-util.c

Known remaining gaps (not addressed)

  • cgroup v1 is gone upstream: the kernel must boot with a cgroup v2 hierarchy. 4.19's cgroup v2 lacks the cpuset (5.0) and freezer (5.2) controllers, cgroup.kill (5.14, already graceful).
  • PSI (4.20): systemd-oomd does not work; memory-pressure event sources are already optional.
  • BPF: features needing BPF links / BPF LSM / bounded loops (5.3–5.7) — RestrictFileSystems=, RestrictNetworkInterfaces=, SocketBind*=, BindNetworkInterface=, … — report "not supported" (upstream probing, ef12eadfc8).
  • New mount API without fallback (5.2/5.12): PrivateBPF=, ExtensionImages=/ExtensionDirectories= (overlay via fsopen), MountImages=, runtime systemctl bind/mount-image, idmapped mounts, mountfsd, nsresourced, sysext/confext, homed (also needs fscrypt v2 / 5.4), coredump's mount of the crashed process' root.
  • MS_NOSYMFOLLOW (5.10) is passed as a legacy mount(2) flag; older kernels silently ignore unknown flags, so the mount just lacks nosymfollow.
  • Credentials use a plain directory instead of a dedicated (noswap) tmpfs, i.e. less isolation.
  • Per-device IO weight (IODeviceWeight=, BFQ per-device weight, 5.4): upstream dropped the 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; 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.

Testing done

  • Full native test suite (meson test -C build) on a 7.x kernel.
  • Selected tests (test-stat-util, test-mountpoint-util, test-chase, test-fs-util, test-fd-util, test-pidref, test-process-util, test-socket-util, test-mount-util, test-udev-spawn, test-async, test-event except test_pidfd) and systemd --test --system (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.
  • 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.