revert changes to apply with system where kernel is 4.19
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# 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` |
|
||||
| `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` |
|
||||
| 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.
|
||||
- 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.
|
||||
- **Not** booted on a real 4.19 kernel yet.
|
||||
@@ -206,6 +206,23 @@ bool cg_kill_supported(void) {
|
||||
return (supported = false);
|
||||
}
|
||||
|
||||
/* ANCIENT-MOD: cgroup v2 freezer is available since kernel 5.2 */
|
||||
bool cg_freezer_supported(void) {
|
||||
static thread_local int supported = -1;
|
||||
|
||||
if (supported >= 0)
|
||||
return supported;
|
||||
|
||||
if (cg_is_available() <= 0)
|
||||
return (supported = false);
|
||||
|
||||
if (access("/sys/fs/cgroup/init.scope/cgroup.freeze", F_OK) >= 0)
|
||||
return (supported = true);
|
||||
if (errno != ENOENT)
|
||||
log_debug_errno(errno, "Failed to check whether cgroup freezer is available, assuming not: %m");
|
||||
return (supported = false);
|
||||
}
|
||||
|
||||
int cg_enumerate_subgroups(const char *path, DIR **ret) {
|
||||
_cleanup_free_ char *fs = NULL;
|
||||
DIR *d;
|
||||
|
||||
@@ -235,6 +235,7 @@ int cg_mask_from_string(const char *s, CGroupMask *ret);
|
||||
int cg_mask_to_string(CGroupMask mask, char **ret);
|
||||
|
||||
bool cg_kill_supported(void);
|
||||
bool cg_freezer_supported(void);
|
||||
|
||||
DECLARE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
|
||||
|
||||
|
||||
+80
-16
@@ -260,9 +260,10 @@ int close_all_fds_frugal(const int except[], size_t n_except) {
|
||||
|
||||
assert(except || n_except == 0);
|
||||
|
||||
/* This is the inner fallback core of close_all_fds(). This never calls malloc() or so and hence is
|
||||
* safe to be called in signal handler context. Most users should call close_all_fds(), but when we
|
||||
* assume we are called from signal handler context, then use this simpler call instead. */
|
||||
/* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so
|
||||
* and hence is safe to be called in signal handler context. Most users should call close_all_fds(),
|
||||
* but when we assume we are called from signal handler context, then use this simpler call
|
||||
* instead. */
|
||||
|
||||
max_fd = get_max_fd();
|
||||
if (max_fd < 0)
|
||||
@@ -288,6 +289,44 @@ int close_all_fds_frugal(const int except[], size_t n_except) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int close_all_fds_by_proc(const int except[], size_t n_except) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
int r = 0;
|
||||
|
||||
d = opendir("/proc/self/fd");
|
||||
if (!d)
|
||||
return close_all_fds_frugal(except, n_except); /* ultimate fallback if /proc/ is not available */
|
||||
|
||||
FOREACH_DIRENT(de, d, return -errno) {
|
||||
int fd = -EBADF, q;
|
||||
|
||||
if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
|
||||
continue;
|
||||
|
||||
fd = parse_fd(de->d_name);
|
||||
if (fd < 0)
|
||||
/* Let's better ignore this, just in case */
|
||||
continue;
|
||||
|
||||
if (fd < 3)
|
||||
continue;
|
||||
|
||||
if (fd == dirfd(d))
|
||||
continue;
|
||||
|
||||
if (fd_in_set(fd, except, n_except))
|
||||
continue;
|
||||
|
||||
q = close_nointr(fd);
|
||||
if (q != -EBADF) /* Valgrind has its own FD and doesn't want to have it closed */
|
||||
RET_GATHER(r, q);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool have_close_range = true; /* Assume we live in the future */
|
||||
|
||||
static int close_all_fds_special_case(const int except[], size_t n_except) {
|
||||
assert(n_except == 0 || except);
|
||||
|
||||
@@ -295,6 +334,9 @@ static int close_all_fds_special_case(const int except[], size_t n_except) {
|
||||
* nicely, since we won't need sorting for them. Returns > 0 if the special casing worked, 0
|
||||
* otherwise. */
|
||||
|
||||
if (!have_close_range)
|
||||
return 0;
|
||||
|
||||
if (n_except == 1 && except[0] < 0) /* Minor optimization: if we only got one fd, and it's invalid,
|
||||
* we got none */
|
||||
n_except = 0;
|
||||
@@ -303,22 +345,31 @@ static int close_all_fds_special_case(const int except[], size_t n_except) {
|
||||
|
||||
case 0:
|
||||
/* Close everything. Yay! */
|
||||
if (close_range(3, INT_MAX, 0) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
if (close_range(3, INT_MAX, 0) >= 0)
|
||||
return 1;
|
||||
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) {
|
||||
have_close_range = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -errno;
|
||||
|
||||
case 1:
|
||||
/* Close all but exactly one, then we don't need no sorting. This is a pretty common
|
||||
* case, hence let's handle it specially. */
|
||||
|
||||
if (except[0] > 3 && close_range(3, except[0] - 1, 0) < 0)
|
||||
return -errno;
|
||||
if ((except[0] <= 3 || close_range(3, except[0]-1, 0) >= 0) &&
|
||||
(except[0] >= INT_MAX || close_range(MAX(3, except[0]+1), -1, 0) >= 0))
|
||||
return 1;
|
||||
|
||||
if (except[0] < INT_MAX && close_range(MAX(3, except[0] + 1), -1, 0) < 0)
|
||||
return -errno;
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) {
|
||||
have_close_range = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return -errno;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
@@ -350,6 +401,9 @@ int close_all_fds(const int except[], size_t n_except) {
|
||||
if (r > 0) /* special case worked! */
|
||||
return 0;
|
||||
|
||||
if (!have_close_range)
|
||||
return close_all_fds_by_proc(except, n_except);
|
||||
|
||||
_cleanup_free_ int *sorted_malloc = NULL;
|
||||
size_t n_sorted;
|
||||
int *sorted;
|
||||
@@ -369,7 +423,7 @@ int close_all_fds(const int except[], size_t n_except) {
|
||||
sorted = newa(int, n_sorted);
|
||||
|
||||
if (!sorted) /* Fallback on OOM. */
|
||||
return close_all_fds_frugal(except, n_except);
|
||||
return close_all_fds_by_proc(except, n_except);
|
||||
|
||||
memcpy(sorted, except, n_except * sizeof(int));
|
||||
|
||||
@@ -391,8 +445,13 @@ int close_all_fds(const int except[], size_t n_except) {
|
||||
continue;
|
||||
|
||||
/* Close everything between the start and end fds (both of which shall stay open) */
|
||||
if (close_range(start + 1, end - 1, 0) < 0)
|
||||
return -errno;
|
||||
if (close_range(start + 1, end - 1, 0) < 0) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||
return -errno;
|
||||
|
||||
have_close_range = false;
|
||||
return close_all_fds_by_proc(except, n_except);
|
||||
}
|
||||
}
|
||||
|
||||
/* The loop succeeded. Let's now close everything beyond the end */
|
||||
@@ -400,8 +459,13 @@ int close_all_fds(const int except[], size_t n_except) {
|
||||
if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */
|
||||
return 0;
|
||||
|
||||
if (close_range(sorted[n_sorted-1] + 1, INT_MAX, 0) < 0)
|
||||
return -errno;
|
||||
if (close_range(sorted[n_sorted-1] + 1, INT_MAX, 0) < 0) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||
return -errno;
|
||||
|
||||
have_close_range = false;
|
||||
return close_all_fds_by_proc(except, n_except);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -127,6 +127,7 @@ int get_max_fd(void);
|
||||
|
||||
int close_all_fds(const int except[], size_t n_except);
|
||||
int close_all_fds_without_malloc(const int except[], size_t n_except);
|
||||
int close_all_fds_by_proc(const int except[], size_t n_except);
|
||||
int close_all_fds_frugal(const int except[], size_t n_except);
|
||||
|
||||
int pack_fds(int fds[], size_t n);
|
||||
|
||||
+28
-2
@@ -322,7 +322,20 @@ int futimens_opath(int fd, const struct timespec ts[2]) {
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
return RET_NERRNO(utimensat(fd, "", ts, AT_EMPTY_PATH));
|
||||
if (utimensat(fd, "", ts, AT_EMPTY_PATH) >= 0)
|
||||
return 0;
|
||||
if (errno != EINVAL)
|
||||
return -errno;
|
||||
|
||||
/* ANCIENT-MOD: AT_EMPTY_PATH is supported since kernel 5.8, fall back to /proc/self/fd/ */
|
||||
if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, /* flags= */ 0) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
return proc_fd_enoent_errno();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stat_warn_permissions(const char *path, const struct stat *st) {
|
||||
@@ -371,7 +384,20 @@ int access_fd(int fd, int mode) {
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
return RET_NERRNO(faccessat(fd, "", mode, AT_EMPTY_PATH));
|
||||
if (faccessat(fd, "", mode, AT_EMPTY_PATH) >= 0)
|
||||
return 0;
|
||||
if (errno != EINVAL)
|
||||
return -errno;
|
||||
|
||||
/* ANCIENT-MOD: AT_EMPTY_PATH is supported since kernel 5.8, fall back to /proc/self/fd/ */
|
||||
if (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
return proc_fd_enoent_errno();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int touch_fd(int fd, usec_t stamp) {
|
||||
|
||||
@@ -681,6 +681,8 @@ int mount_option_supported(const char *fstype, const char *key, const char *valu
|
||||
if (fd < 0) {
|
||||
if (errno == ENODEV)
|
||||
return log_debug_errno(errno, "Kernel does not support file system type '%s'.", fstype);
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
return -EAGAIN; /* ANCIENT-MOD: new mount API not available (kernel < 5.2) → don't know */
|
||||
|
||||
return log_debug_errno(errno, "Failed to open superblock context for '%s': %m", fstype);
|
||||
}
|
||||
|
||||
+10
-2
@@ -28,8 +28,11 @@ int pidfd_check_pidfs(int pid_fd) {
|
||||
_cleanup_close_ int our_fd = -EBADF;
|
||||
if (pid_fd < 0) {
|
||||
our_fd = pidfd_open(getpid_cached(), /* flags= */ 0);
|
||||
if (our_fd < 0)
|
||||
if (our_fd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno)) /* ANCIENT-MOD: kernel < 5.3 */
|
||||
return (have_pidfs = false);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
pid_fd = our_fd;
|
||||
}
|
||||
@@ -102,6 +105,8 @@ int pidfd_info_mask_is_supported(uint64_t mask) {
|
||||
|
||||
if (cached == 0) {
|
||||
_cleanup_close_ int pidfd = r = RET_NERRNO(pidfd_open(getpid_cached(), /* flags= */ 0));
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) /* ANCIENT-MOD: kernel < 5.3 */
|
||||
return (cached = -EOPNOTSUPP);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -334,8 +339,11 @@ int pidfd_get_inode_id_self_cached(uint64_t *ret) {
|
||||
return initialized;
|
||||
|
||||
_cleanup_close_ int fd = pidfd_open(getpid_cached(), 0);
|
||||
if (fd < 0)
|
||||
if (fd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno)) /* ANCIENT-MOD: kernel < 5.3 */
|
||||
return (initialized = -EOPNOTSUPP);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
r = pidfd_get_inode_id(fd, &cached);
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
|
||||
+27
-8
@@ -106,9 +106,10 @@ int pidref_set_pid_full(PidRef *pidref, pid_t pid, unsigned flags) {
|
||||
|
||||
fd = pidfd_open(pid, flags);
|
||||
if (fd < 0) {
|
||||
/* Graceful fallback in case the kernel is out of fds.
|
||||
/* Graceful fallback in case the kernel is out of fds or lacks pidfd_open() (ANCIENT-MOD: < 5.3).
|
||||
* The PIDFD_THREAD flag is supported since kernel v6.9 (64bef697d33b75fc06c5789b3f8108680271529f). */
|
||||
if (!(ERRNO_IS_RESOURCE(errno) || (errno == EINVAL && FLAGS_SET(flags, PIDFD_THREAD))))
|
||||
if (!(ERRNO_IS_RESOURCE(errno) || ERRNO_IS_NOT_SUPPORTED(errno) ||
|
||||
(errno == EINVAL && FLAGS_SET(flags, PIDFD_THREAD))))
|
||||
return log_debug_errno(errno, "Failed to open pidfd for pid " PID_FMT ": %m", pid);
|
||||
|
||||
fd = -EBADF;
|
||||
@@ -468,16 +469,34 @@ int pidref_wait_for_terminate_full(PidRef *pidref, usec_t timeout, siginfo_t *re
|
||||
if (pidref->pid == 1 || pidref_is_self(pidref))
|
||||
return -ECHILD;
|
||||
|
||||
if (pidref->fd < 0 && (timeout != USEC_INFINITY || fiber_ops_is_set()))
|
||||
return -ENOMEDIUM;
|
||||
|
||||
usec_t ts = timeout == USEC_INFINITY ? USEC_INFINITY : usec_add(now(CLOCK_MONOTONIC), timeout);
|
||||
|
||||
/* ANCIENT-MOD: no pidfd (kernel < 5.3), poll with waitid(WNOHANG) to honour the timeout */
|
||||
if (pidref->fd < 0 && ts != USEC_INFINITY)
|
||||
for (;;) {
|
||||
siginfo_t si = {};
|
||||
|
||||
r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, WEXITED|WNOHANG));
|
||||
if (r == -EINTR)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (si.si_pid != 0) {
|
||||
if (ret_si)
|
||||
*ret_si = si;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (now(CLOCK_MONOTONIC) >= ts)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
(void) usleep_safe(10 * USEC_PER_MSEC);
|
||||
}
|
||||
|
||||
/* Poll the pidfd before waitid() if either there's a finite timeout (so we can honor it) or
|
||||
* we're on a fiber (so fd_wait_for_event() can suspend us instead of blocking the event loop
|
||||
* inside waitid()). Otherwise let waitid() block directly. The precondition above guarantees
|
||||
* pidref->fd >= 0 in both cases. */
|
||||
bool poll_first = ts != USEC_INFINITY || fiber_ops_is_set();
|
||||
* inside waitid()). Otherwise let waitid() block directly. */
|
||||
bool poll_first = pidref->fd >= 0 && (ts != USEC_INFINITY || fiber_ops_is_set());
|
||||
|
||||
for (;;) {
|
||||
if (poll_first) {
|
||||
|
||||
+13
-1
@@ -1419,13 +1419,25 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
|
||||
}
|
||||
|
||||
int socket_bind_to_ifindex(int fd, int ifindex) {
|
||||
char ifname[IF_NAMESIZE];
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
if (ifindex <= 0)
|
||||
/* Drop binding */
|
||||
return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0));
|
||||
|
||||
return setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
|
||||
r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
|
||||
if (r != -ENOPROTOOPT)
|
||||
return r;
|
||||
|
||||
/* ANCIENT-MOD: fall back to SO_BINDTODEVICE on kernels < 5.1 which didn't have SO_BINDTOIFINDEX */
|
||||
r = format_ifname(ifindex, ifname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return socket_bind_to_ifname(fd, ifname);
|
||||
}
|
||||
|
||||
int socket_autobind(int fd, char **ret_name) {
|
||||
|
||||
@@ -11,14 +11,17 @@
|
||||
#include "dirent-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "filesystems.h"
|
||||
#include "fs-util.h"
|
||||
#include "hash-funcs.h"
|
||||
#include "log.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "siphash24.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
@@ -413,6 +416,102 @@ static const char* statx_attribute_to_name(uint64_t attr);
|
||||
DEFINE_STATX_BITS_TO_STRING(statx_mask, unsigned, statx_mask_one_to_name, "0x%x");
|
||||
DEFINE_STATX_BITS_TO_STRING(statx_attributes, uint64_t, statx_attribute_to_name, "0x%" PRIx64);
|
||||
|
||||
/* ANCIENT-MOD: emulation of STATX_MNT_ID and STATX_ATTR_MOUNT_ROOT for kernels < 5.8 */
|
||||
static int fd_fdinfo_mnt_id(int fd, uint64_t *ret) {
|
||||
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
xsprintf(path, "/proc/self/fdinfo/%i", fd);
|
||||
|
||||
r = get_proc_field(path, "mnt_id", &p);
|
||||
if (r == -ENOENT)
|
||||
return proc_fd_enoent_errno();
|
||||
if (r == -ENODATA)
|
||||
return -EUNATCH;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return safe_atou64(p, ret);
|
||||
}
|
||||
|
||||
static int fd_open_parent(int fd, const struct statx *sx) {
|
||||
_cleanup_free_ char *p = NULL, *d = NULL;
|
||||
int r;
|
||||
|
||||
if (FLAGS_SET(sx->stx_mask, STATX_TYPE) ? S_ISDIR(sx->stx_mode) : fd_verify_directory(fd) >= 0)
|
||||
return RET_NERRNO(openat(fd, "..", O_PATH|O_DIRECTORY|O_CLOEXEC));
|
||||
|
||||
r = fd_get_path(fd, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = path_extract_directory(p, &d);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return RET_NERRNO(open(d, O_PATH|O_DIRECTORY|O_CLOEXEC));
|
||||
}
|
||||
|
||||
static int xstatx_emulate(
|
||||
int fd,
|
||||
const char *path,
|
||||
int statx_flags,
|
||||
bool want_mnt_id,
|
||||
bool want_mount_root,
|
||||
struct statx *sx) {
|
||||
|
||||
_cleanup_close_ int target_fd = -EBADF, parent_fd = -EBADF;
|
||||
uint64_t mnt_id, parent_mnt_id;
|
||||
int r;
|
||||
|
||||
assert(sx);
|
||||
|
||||
if (isempty(path) && fd >= 0)
|
||||
target_fd = RET_NERRNO(fcntl(fd, F_DUPFD_CLOEXEC, 3));
|
||||
else
|
||||
target_fd = RET_NERRNO(openat(fd, isempty(path) ? "." : path,
|
||||
O_PATH|O_CLOEXEC|(FLAGS_SET(statx_flags, AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0)));
|
||||
if (target_fd < 0)
|
||||
return target_fd;
|
||||
|
||||
r = fd_fdinfo_mnt_id(target_fd, &mnt_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (want_mnt_id) {
|
||||
sx->stx_mnt_id = mnt_id;
|
||||
sx->stx_mask |= STATX_MNT_ID;
|
||||
}
|
||||
|
||||
if (!want_mount_root)
|
||||
return 0;
|
||||
|
||||
bool is_root = false;
|
||||
parent_fd = fd_open_parent(target_fd, sx);
|
||||
if (parent_fd < 0)
|
||||
log_debug_errno(parent_fd, "Failed to open parent to determine mount root, assuming not a mount root: %m");
|
||||
else {
|
||||
struct stat a, b;
|
||||
|
||||
r = fd_fdinfo_mnt_id(parent_fd, &parent_mnt_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (mnt_id != parent_mnt_id)
|
||||
is_root = true;
|
||||
else if (fstat(target_fd, &a) >= 0 && fstat(parent_fd, &b) >= 0)
|
||||
is_root = stat_inode_same(&a, &b);
|
||||
}
|
||||
|
||||
SET_FLAG(sx->stx_attributes, STATX_ATTR_MOUNT_ROOT, is_root);
|
||||
sx->stx_attributes_mask |= STATX_ATTR_MOUNT_ROOT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xstatx_full(int fd,
|
||||
const char *path,
|
||||
int statx_flags,
|
||||
@@ -457,6 +556,18 @@ int xstatx_full(int fd,
|
||||
&sx) < 0)
|
||||
return negative_errno();
|
||||
|
||||
bool want_mnt_id =
|
||||
(FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) && !(sx.stx_mask & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE))) ||
|
||||
(FLAGS_SET(request_mask, STATX_MNT_ID) && !FLAGS_SET(sx.stx_mask, STATX_MNT_ID));
|
||||
bool want_mount_root =
|
||||
FLAGS_SET(mandatory_attributes, STATX_ATTR_MOUNT_ROOT) &&
|
||||
!FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT);
|
||||
if (want_mnt_id || want_mount_root) {
|
||||
r = xstatx_emulate(fd, path, statx_flags, want_mnt_id, want_mount_root, &sx);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to emulate statx() mount fields, ignoring: %m");
|
||||
}
|
||||
|
||||
if (FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) &&
|
||||
!(sx.stx_mask & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE)))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EUNATCH), "statx() did not return either STATX_MNT_ID or STATX_MNT_ID_UNIQUE.");
|
||||
|
||||
+41
-4
@@ -937,12 +937,39 @@ static void cgroup_delegate_xattr_apply(Unit *u) {
|
||||
}
|
||||
|
||||
static void cgroup_survive_xattr_apply(Unit *u) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (u->survive_final_kill_signal)
|
||||
unit_set_xattr_graceful(u, "user.survive_final_kill_signal", "1", 1);
|
||||
else
|
||||
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
|
||||
if (!crt)
|
||||
return;
|
||||
|
||||
if (u->survive_final_kill_signal) {
|
||||
r = cg_set_xattr(
|
||||
crt->cgroup_path,
|
||||
"user.survive_final_kill_signal",
|
||||
"1",
|
||||
1,
|
||||
/* flags= */ 0);
|
||||
/* user xattr support was added in kernel v5.7 */
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
r = cg_set_xattr(
|
||||
crt->cgroup_path,
|
||||
"trusted.survive_final_kill_signal",
|
||||
"1",
|
||||
1,
|
||||
/* flags= */ 0);
|
||||
if (r < 0)
|
||||
log_unit_debug_errno(u,
|
||||
r,
|
||||
"Failed to set 'survive_final_kill_signal' xattr on control "
|
||||
"group %s, ignoring: %m",
|
||||
empty_to_root(crt->cgroup_path));
|
||||
} else {
|
||||
unit_remove_xattr_graceful(u, "user.survive_final_kill_signal");
|
||||
unit_remove_xattr_graceful(u, "trusted.survive_final_kill_signal");
|
||||
}
|
||||
}
|
||||
|
||||
static void cgroup_xattr_apply(Unit *u) {
|
||||
@@ -3189,6 +3216,13 @@ static int unit_check_cgroup_events(Unit *u) {
|
||||
"cgroup.events",
|
||||
STRV_MAKE("populated", "frozen"),
|
||||
values);
|
||||
if (r == -ENXIO)
|
||||
/* ANCIENT-MOD: the "frozen" key exists only since kernel 5.2 (cgroup v2 freezer) */
|
||||
r = cg_get_keyed_attribute(
|
||||
crt->cgroup_path,
|
||||
"cgroup.events",
|
||||
STRV_MAKE("populated"),
|
||||
values);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -3203,7 +3237,7 @@ static int unit_check_cgroup_events(Unit *u) {
|
||||
/* Disregard freezer state changes due to operations not initiated by us.
|
||||
* See: https://github.com/systemd/systemd/pull/13512/files#r416469963 and
|
||||
* https://github.com/systemd/systemd/pull/13512#issuecomment-573007207 */
|
||||
if (IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_FREEZING_BY_PARENT, FREEZER_THAWING))
|
||||
if (values[1] && IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_FREEZING_BY_PARENT, FREEZER_THAWING))
|
||||
unit_freezer_complete(u, streq(values[1], "0") ? FREEZER_RUNNING : FREEZER_FROZEN);
|
||||
|
||||
free_many_charp(values, ELEMENTSOF(values));
|
||||
@@ -4122,6 +4156,9 @@ int unit_cgroup_freezer_action(Unit *u, FreezerAction action) {
|
||||
assert(action >= 0);
|
||||
assert(action < _FREEZER_ACTION_MAX);
|
||||
|
||||
if (!cg_freezer_supported())
|
||||
return 0;
|
||||
|
||||
unit_next_freezer_state(u, action, &next, &objective);
|
||||
|
||||
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
|
||||
|
||||
@@ -1052,8 +1052,9 @@ static int setup_credentials_internal(
|
||||
}
|
||||
|
||||
mfd = fsmount_credentials_fs(&fs_fd);
|
||||
if (ERRNO_IS_NEG_PRIVILEGE(mfd) && !dir_mounted) {
|
||||
log_debug_errno(mfd, "Lacking privilege to mount credentials fs, falling back to plain directory.");
|
||||
if ((ERRNO_IS_NEG_PRIVILEGE(mfd) || ERRNO_IS_NEG_NOT_SUPPORTED(mfd)) && !dir_mounted) {
|
||||
/* ANCIENT-MOD: also fall back if the new mount API is unavailable (kernel < 5.2) */
|
||||
log_debug_errno(mfd, "Lacking privilege or kernel support to mount credentials fs, falling back to plain directory.");
|
||||
return setup_credentials_plain_dir(context, cred_dir);
|
||||
}
|
||||
if (mfd < 0)
|
||||
|
||||
+28
-4
@@ -7,6 +7,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
@@ -1562,6 +1563,25 @@ static int mount_private_cgroup2fs(const MountEntry *m, const NamespaceParameter
|
||||
return mount_private_apivfs("cgroup2", mount_entry_path(m), "/sys/fs/cgroup", /* opts= */ NULL, p->runtime_scope);
|
||||
}
|
||||
|
||||
/* ANCIENT-MOD: before kernel 5.8 procfs options are per pid namespace (shared with the host /proc) and
|
||||
* textual hidepid= / subset= do not exist, hence ignore ProtectProc=/ProcSubset= there. */
|
||||
static bool procfs_per_instance_options_supported(void) {
|
||||
static int cached = -1;
|
||||
struct utsname uts;
|
||||
|
||||
if (cached >= 0)
|
||||
return cached;
|
||||
|
||||
if (uname(&uts) < 0)
|
||||
return (cached = true);
|
||||
|
||||
cached = strverscmp_improved(uts.release, "5.8") >= 0;
|
||||
if (!cached)
|
||||
log_debug("Kernel %s does not support per-instance procfs options, ignoring ProtectProc=/ProcSubset=.", uts.release);
|
||||
|
||||
return cached;
|
||||
}
|
||||
|
||||
static int mount_procfs(const MountEntry *m, const NamespaceParameters *p) {
|
||||
_cleanup_free_ char *opts = NULL;
|
||||
int r;
|
||||
@@ -1569,8 +1589,9 @@ static int mount_procfs(const MountEntry *m, const NamespaceParameters *p) {
|
||||
assert(m);
|
||||
assert(p);
|
||||
|
||||
if (p->protect_proc != PROTECT_PROC_DEFAULT ||
|
||||
p->proc_subset != PROC_SUBSET_ALL) {
|
||||
if ((p->protect_proc != PROTECT_PROC_DEFAULT ||
|
||||
p->proc_subset != PROC_SUBSET_ALL) &&
|
||||
procfs_per_instance_options_supported()) {
|
||||
|
||||
opts = strjoin("hidepid=",
|
||||
p->protect_proc == PROTECT_PROC_DEFAULT ? "off" : protect_proc_to_string(p->protect_proc));
|
||||
@@ -2663,7 +2684,9 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
|
||||
* as well now. */
|
||||
.propagation = MS_SLAVE,
|
||||
});
|
||||
if (_root_mount_fd < 0 && !ERRNO_IS_NEG_PRIVILEGE(_root_mount_fd) && _root_mount_fd != -EINVAL)
|
||||
/* ANCIENT-MOD: also tolerate missing open_tree() (kernel < 5.2) */
|
||||
if (_root_mount_fd < 0 && !ERRNO_IS_NEG_PRIVILEGE(_root_mount_fd) &&
|
||||
!ERRNO_IS_NEG_NOT_SUPPORTED(_root_mount_fd) && _root_mount_fd != -EINVAL)
|
||||
return log_debug_errno(_root_mount_fd, "Failed to clone specified directory: %m");
|
||||
|
||||
root_mount_fd = _root_mount_fd;
|
||||
@@ -2866,7 +2889,8 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
|
||||
/* In case /proc is successfully mounted with pid tree subset only (ProcSubset=pid), the protective
|
||||
mounts to non-pid /proc paths would fail. But the pid only option may have failed gracefully, so
|
||||
let's try the mounts but it's not fatal if they don't succeed. */
|
||||
bool ignore_protect_proc = p->ignore_protect_paths || p->proc_subset == PROC_SUBSET_PID;
|
||||
bool ignore_protect_proc = p->ignore_protect_paths ||
|
||||
(p->proc_subset == PROC_SUBSET_PID && procfs_per_instance_options_supported());
|
||||
if (p->protect_kernel_tunables) {
|
||||
r = append_static_mounts(&ml,
|
||||
protect_kernel_tunables_proc_table,
|
||||
|
||||
+1
-1
@@ -6654,7 +6654,7 @@ int unit_freezer_action(Unit *u, FreezerAction action) {
|
||||
assert(u);
|
||||
assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
|
||||
|
||||
if (!unit_can_freeze(u))
|
||||
if (!cg_freezer_supported() || !unit_can_freeze(u))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (u->job)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <linux/magic.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <threads.h>
|
||||
@@ -51,9 +52,20 @@ static bool EVENT_SOURCE_WATCH_PIDFD(const sd_event_source *s) {
|
||||
/* Returns true if this is a PID event source and can be implemented by watching EPOLLIN */
|
||||
return s &&
|
||||
s->type == SOURCE_CHILD &&
|
||||
s->child.pidfd >= 0 &&
|
||||
(s->child.options & ~WNOWAIT) == WEXITED;
|
||||
}
|
||||
|
||||
/* ANCIENT-MOD: child sources without pidfd (kernel < 5.3) fall back to waitid(P_PID) */
|
||||
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 waitid(P_PID, s->child.pid, si, options);
|
||||
}
|
||||
|
||||
static bool event_source_is_online(sd_event_source *s) {
|
||||
assert(s);
|
||||
return s->enabled != SD_EVENT_OFF && !s->ratelimited;
|
||||
@@ -1099,10 +1111,12 @@ static sd_event_source* source_free(sd_event_source *s) {
|
||||
|
||||
if (s->child.process_owned) {
|
||||
assert(s->child.pid > 0);
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
if (!s->child.exited) {
|
||||
r = RET_NERRNO(pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0));
|
||||
if (s->child.pidfd >= 0)
|
||||
r = RET_NERRNO(pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0));
|
||||
else
|
||||
r = RET_NERRNO(kill(s->child.pid, SIGKILL));
|
||||
if (r < 0 && r != -ESRCH)
|
||||
log_debug_errno(r, "Failed to kill process " PID_FMT ", ignoring: %m",
|
||||
s->child.pid);
|
||||
@@ -1112,7 +1126,7 @@ static sd_event_source* source_free(sd_event_source *s) {
|
||||
siginfo_t si = {};
|
||||
|
||||
/* Reap the child if we can */
|
||||
(void) waitid(P_PIDFD, s->child.pidfd, &si, WEXITED);
|
||||
(void) child_waitid(s, &si, WEXITED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1649,10 +1663,22 @@ _public_ int sd_event_add_child(
|
||||
* PID, and make regular waitid() handling race-free. */
|
||||
|
||||
s->child.pidfd = pidfd_open(pid, 0);
|
||||
if (s->child.pidfd < 0)
|
||||
return -errno;
|
||||
if (s->child.pidfd < 0) {
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||
return -errno;
|
||||
|
||||
s->child.pidfd_owned = true; /* If we allocate the pidfd we own it by default */
|
||||
/* Without pidfd we depend on SIGCHLD via signalfd, which only works if it is blocked */
|
||||
r = signal_is_blocked(SIGCHLD);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
log_debug("pidfd_open() unavailable, blocking SIGCHLD to watch child processes.");
|
||||
r = sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
} else
|
||||
s->child.pidfd_owned = true; /* If we allocate the pidfd we own it by default */
|
||||
|
||||
s->wakeup = WAKEUP_EVENT_SOURCE;
|
||||
s->child.options = options;
|
||||
@@ -3257,7 +3283,6 @@ _public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, cons
|
||||
assert_return(s->type == SOURCE_CHILD, -EDOM);
|
||||
assert_return(!event_origin_changed(s->event), -ECHILD);
|
||||
assert_return(SIGNAL_VALID(sig), -EINVAL);
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
/* If we already have seen indication the process exited refuse sending a signal early. */
|
||||
if (s->child.exited)
|
||||
@@ -3269,14 +3294,20 @@ _public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, cons
|
||||
if (si)
|
||||
copy = *si;
|
||||
|
||||
return RET_NERRNO(pidfd_send_signal(s->child.pidfd, sig, si ? © : NULL, flags));
|
||||
if (s->child.pidfd >= 0)
|
||||
return RET_NERRNO(pidfd_send_signal(s->child.pidfd, sig, si ? © : NULL, flags));
|
||||
|
||||
if (flags != 0)
|
||||
return -EOPNOTSUPP;
|
||||
if (si)
|
||||
return RET_NERRNO(syscall(SYS_rt_sigqueueinfo, s->child.pid, sig, ©));
|
||||
return RET_NERRNO(kill(s->child.pid, sig));
|
||||
}
|
||||
|
||||
_public_ int sd_event_source_get_child_pidfd_own(sd_event_source *s) {
|
||||
assert_return(s, -EINVAL);
|
||||
assert_return(s->type == SOURCE_CHILD, -EDOM);
|
||||
assert_return(!event_origin_changed(s->event), -ECHILD);
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
return s->child.pidfd_owned;
|
||||
}
|
||||
@@ -3285,7 +3316,9 @@ _public_ int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own) {
|
||||
assert_return(s, -EINVAL);
|
||||
assert_return(s->type == SOURCE_CHILD, -EDOM);
|
||||
assert_return(!event_origin_changed(s->event), -ECHILD);
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
if (s->child.pidfd < 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
s->child.pidfd_owned = own;
|
||||
return 0;
|
||||
@@ -3757,7 +3790,6 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
|
||||
|
||||
HASHMAP_FOREACH(s, e->child_sources) {
|
||||
assert(s->type == SOURCE_CHILD);
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
if (s->priority > threshold)
|
||||
continue;
|
||||
@@ -3777,7 +3809,7 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
|
||||
continue;
|
||||
|
||||
zero(s->child.siginfo);
|
||||
if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo,
|
||||
if (child_waitid(s, &s->child.siginfo,
|
||||
WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | (s->child.options & ~WNOWAIT)) < 0)
|
||||
return negative_errno();
|
||||
|
||||
@@ -3791,7 +3823,7 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
|
||||
* from the queue, since there's no benefit in leaving it queued. */
|
||||
|
||||
assert(s->child.options & (WSTOPPED|WCONTINUED));
|
||||
(void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
|
||||
(void) child_waitid(s, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
|
||||
}
|
||||
|
||||
r = source_set_pending(s, true);
|
||||
@@ -4274,7 +4306,7 @@ static int source_dispatch(sd_event_source *s) {
|
||||
|
||||
/* Now, reap the PID for good (unless WNOWAIT was specified by the caller). */
|
||||
if (zombie) {
|
||||
(void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|WEXITED|(s->child.options & WNOWAIT));
|
||||
(void) child_waitid(s, &s->child.siginfo, WNOHANG|WEXITED|(s->child.options & WNOWAIT));
|
||||
if (!FLAGS_SET(s->child.options, WNOWAIT))
|
||||
s->child.waited = true;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,8 @@ int rtnl_resolve_ifname_full(
|
||||
r = sd_netlink_call(*rtnl, message, 0, &reply);
|
||||
if (r >= 0)
|
||||
return parse_newlink_message(reply, ret_name, ret_altnames);
|
||||
if (r != -ENODEV)
|
||||
/* The kernels older than 76c9ac0ee878f6693d398d3a95ccaf85e1f597a6 (v5.5) return -EINVAL. */
|
||||
if (!IN_SET(r, -ENODEV, -EINVAL))
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@@ -858,7 +858,7 @@ static int create_session_message(
|
||||
|
||||
if (!avoid_pidfd) {
|
||||
pidfd = pidfd_open(getpid_cached(), 0);
|
||||
if (pidfd < 0)
|
||||
if (pidfd < 0 && !ERRNO_IS_NOT_SUPPORTED(errno)) /* ANCIENT-MOD: kernel < 5.3 */
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
||||
@@ -2448,6 +2448,38 @@ static int link_update_driver(Link *link, sd_netlink_message *message) {
|
||||
return 1; /* needs reconfigure */
|
||||
}
|
||||
|
||||
static int link_update_permanent_hardware_address_from_ethtool(Link *link, sd_netlink_message *message) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
assert(message);
|
||||
|
||||
if (link->ethtool_permanent_hw_addr_read)
|
||||
return 0;
|
||||
|
||||
/* When udevd is running, read the permanent hardware address after the interface is
|
||||
* initialized by udevd. Otherwise, ethtool may not work correctly. See issue #22538.
|
||||
* When udevd is not running, read the value when the interface is detected. */
|
||||
if (udev_available() && !link->dev)
|
||||
return 0;
|
||||
|
||||
/* If the interface does not have a hardware address, then it will not have a permanent address either. */
|
||||
r = netlink_message_read_hw_addr(message, IFLA_ADDRESS, NULL);
|
||||
if (r == -ENODATA)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return log_link_debug_errno(link, r, "Failed to read IFLA_ADDRESS attribute: %m");
|
||||
|
||||
link->ethtool_permanent_hw_addr_read = true;
|
||||
|
||||
r = ethtool_get_permanent_hw_addr(&link->manager->ethtool_fd, link->ifname, &link->permanent_hw_addr);
|
||||
if (r < 0)
|
||||
log_link_debug_errno(link, r, "Permanent hardware address not found, continuing without: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int link_update_permanent_hardware_address(Link *link, sd_netlink_message *message) {
|
||||
int r;
|
||||
|
||||
@@ -2459,10 +2491,15 @@ static int link_update_permanent_hardware_address(Link *link, sd_netlink_message
|
||||
return 0;
|
||||
|
||||
r = netlink_message_read_hw_addr(message, IFLA_PERM_ADDRESS, &link->permanent_hw_addr);
|
||||
if (r == -ENODATA)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return log_link_debug_errno(link, r, "Failed to read IFLA_PERM_ADDRESS attribute: %m");
|
||||
if (r < 0) {
|
||||
if (r != -ENODATA)
|
||||
return log_link_debug_errno(link, r, "Failed to read IFLA_PERM_ADDRESS attribute: %m");
|
||||
|
||||
/* Fallback to ethtool for kernels older than v5.6 (f74877a5457d34d604dba6dbbb13c4c05bac8b93). */
|
||||
r = link_update_permanent_hardware_address_from_ethtool(link, message);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (link->permanent_hw_addr.length > 0)
|
||||
log_link_debug(link, "Saved permanent hardware address: %s", HW_ADDR_TO_STR(&link->permanent_hw_addr));
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include "networkd-wwan-bus.h"
|
||||
#include "ordered-set.h"
|
||||
#include "qdisc.h"
|
||||
#include "selinux-util.h"
|
||||
#include "set.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
@@ -1149,8 +1150,12 @@ int manager_enumerate(Manager *m) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not enumerate neighbors: %m");
|
||||
|
||||
/* ANCIENT-MOD: NextHop support is added in kernel v5.3 (65ee00a9409f751188a8cdc0988167858eb4a536),
|
||||
* and older kernels return -EOPNOTSUPP, or -EINVAL if SELinux is enabled. */
|
||||
r = manager_enumerate_nexthop(m);
|
||||
if (r < 0)
|
||||
if (r == -EOPNOTSUPP || (r == -EINVAL && mac_selinux_enforcing()))
|
||||
log_debug_errno(r, "Could not enumerate nexthops, ignoring: %m");
|
||||
else if (r < 0)
|
||||
return log_error_errno(r, "Could not enumerate nexthops: %m");
|
||||
|
||||
r = manager_enumerate_routes(m);
|
||||
|
||||
@@ -1066,6 +1066,7 @@ static int fw_nftables_add_local_dnat_internal(
|
||||
|
||||
sd_netlink_message *messages[3] = {};
|
||||
CLEANUP_ELEMENTS(messages, sd_netlink_message_unref_array_clear);
|
||||
static bool ipv6_supported = true;
|
||||
uint32_t data[5], key[2], dlen;
|
||||
size_t msgcnt = 0;
|
||||
int r;
|
||||
@@ -1074,6 +1075,9 @@ static int fw_nftables_add_local_dnat_internal(
|
||||
assert(add || !previous_remote);
|
||||
assert(IN_SET(af, AF_INET, AF_INET6));
|
||||
|
||||
if (!ipv6_supported && af == AF_INET6)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!IN_SET(protocol, IPPROTO_TCP, IPPROTO_UDP))
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
@@ -1127,6 +1131,14 @@ static int fw_nftables_add_local_dnat_internal(
|
||||
|
||||
assert(msgcnt < ELEMENTSOF(messages));
|
||||
r = sd_nfnl_call_batch(nfnl, messages, msgcnt, NFNL_DEFAULT_TIMEOUT_USECS);
|
||||
if (r == -EOVERFLOW && af == AF_INET6) {
|
||||
/* ANCIENT-MOD: The current implementation of DNAT in systemd requires kernel's
|
||||
* fdb9c405e35bdc6e305b9b4e20ebc141ed14fc81 (v5.8), and the older kernel returns
|
||||
* -EOVERFLOW. Let's treat the error as -EOPNOTSUPP. */
|
||||
log_debug_errno(r, "The current implementation of IPv6 DNAT in systemd requires kernel 5.8 or newer, ignoring: %m");
|
||||
ipv6_supported = false;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ static bool is_in_survivor_cgroup(const PidRef *pid) {
|
||||
}
|
||||
|
||||
r = cg_get_xattr_bool(cgroup_path, "user.survive_final_kill_signal");
|
||||
/* user xattr support was added to kernel v5.7, try with the trusted namespace as a fallback */
|
||||
if (ERRNO_IS_NEG_XATTR_ABSENT(r))
|
||||
r = cg_get_xattr_bool(cgroup_path, "trusted.survive_final_kill_signal");
|
||||
if (r < 0 && !ERRNO_IS_NEG_XATTR_ABSENT(r))
|
||||
log_debug_errno(r,
|
||||
"Failed to get survive_final_kill_signal xattr of %s, ignoring: %m",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/loop.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/ioctl.h>
|
||||
@@ -200,6 +201,21 @@ static int loop_configure_fallback(int fd, const struct loop_config *c) {
|
||||
random_u64_range(UINT64_C(240) * USEC_PER_MSEC * n_attempts/64));
|
||||
}
|
||||
|
||||
/* Work around a kernel bug, where changing offset/size of the loopback device doesn't correctly
|
||||
* invalidate the buffer cache. For details see:
|
||||
*
|
||||
* https://android.googlesource.com/platform/system/apex/+/bef74542fbbb4cd629793f4efee8e0053b360570
|
||||
*
|
||||
* This was fixed in kernel 5.0, see:
|
||||
*
|
||||
* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5db470e229e22b7eda6e23b5566e532c96fb5bc3
|
||||
*
|
||||
* We'll run the work-around here in the legacy LOOP_SET_STATUS64 codepath. In the LOOP_CONFIGURE
|
||||
* codepath above it should not be necessary. */
|
||||
if (c->info.lo_offset != 0 || c->info.lo_sizelimit != 0)
|
||||
if (ioctl(fd, BLKFLSBUF, 0) < 0)
|
||||
log_debug_errno(errno, "Failed to issue BLKFLSBUF ioctl, ignoring: %m");
|
||||
|
||||
/* If a block size is requested then try to configure it. If that doesn't work, ignore errors, but
|
||||
* afterwards, let's validate what is in effect, and if it doesn't match what we want, fail */
|
||||
if (c->block_size != 0) {
|
||||
|
||||
+22
-13
@@ -43,26 +43,35 @@ typedef struct MountPoint {
|
||||
} MountPoint;
|
||||
|
||||
static int cgroupfs_mount_options(int priority, const char *type, char **ret) {
|
||||
_cleanup_free_ char *opts = NULL;
|
||||
int r;
|
||||
|
||||
assert(type);
|
||||
assert(streq(type, "cgroup2"));
|
||||
assert(ret);
|
||||
|
||||
/* memory_hugetlb_accounting mount option is since kernel v6.7 (8cba9576df601c384abd334a503c3f6e1e29eefb). */
|
||||
/* ANCIENT-MOD: memory_recursiveprot is probed (kernel >= 5.7) instead of being always passed.
|
||||
* memory_hugetlb_accounting mount option is since kernel v6.7 (8cba9576df601c384abd334a503c3f6e1e29eefb). */
|
||||
|
||||
r = mount_option_supported("cgroup2", "memory_hugetlb_accounting", /* value= */ NULL);
|
||||
if (r <= 0) {
|
||||
if (r < 0)
|
||||
log_full_errno(priority, r, "Failed to determine whether cgroupfs supports 'memory_hugetlb_accounting' mount option, assuming not: %m");
|
||||
else
|
||||
log_debug("'memory_hugetlb_accounting' not supported by cgroupfs, not using mount option.");
|
||||
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
FOREACH_STRING(o, "memory_recursiveprot", "memory_hugetlb_accounting") {
|
||||
r = mount_option_supported("cgroup2", o, /* value= */ NULL);
|
||||
if (r == -EAGAIN)
|
||||
/* No fsopen(), i.e. kernel < 5.2, hence none of these options exist either */
|
||||
log_debug("Cannot probe cgroupfs mount option '%s', kernel too old, not using it.", o);
|
||||
else if (r < 0 && streq(o, "memory_recursiveprot")) {
|
||||
log_full_errno(priority, r, "Failed to determine whether cgroupfs supports '%s' mount option, assuming it does: %m", o);
|
||||
if (!strextend_with_separator(&opts, ",", o))
|
||||
return log_oom_full(priority);
|
||||
} else if (r < 0)
|
||||
log_full_errno(priority, r, "Failed to determine whether cgroupfs supports '%s' mount option, assuming not: %m", o);
|
||||
else if (r == 0)
|
||||
log_debug("'%s' not supported by cgroupfs, not using mount option.", o);
|
||||
else if (!strextend_with_separator(&opts, ",", o))
|
||||
return log_oom_full(priority);
|
||||
}
|
||||
|
||||
return strdup_to(ret, "memory_hugetlb_accounting");
|
||||
*ret = TAKE_PTR(opts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mount_cgroupfs(const char *path) {
|
||||
@@ -82,7 +91,7 @@ int mount_cgroupfs(const char *path) {
|
||||
return r;
|
||||
|
||||
/* These options shall be kept in sync with those in mount_table below. */
|
||||
if (!strprepend_with_separator(&opts, ",", "nsdelegate,memory_recursiveprot"))
|
||||
if (!strprepend_with_separator(&opts, ",", "nsdelegate"))
|
||||
return log_oom();
|
||||
|
||||
return mount_nofollow_verbose(LOG_ERR, "cgroup2", path, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, opts);
|
||||
@@ -200,7 +209,7 @@ static const MountPoint mount_table[] = {
|
||||
.what = "cgroup2",
|
||||
.where = "/sys/fs/cgroup",
|
||||
.type = "cgroup2",
|
||||
.options = "nsdelegate,memory_recursiveprot",
|
||||
.options = "nsdelegate",
|
||||
.options_fn = cgroupfs_mount_options,
|
||||
.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV,
|
||||
.mode = MNT_FATAL|MNT_IN_CONTAINER|MNT_CHECK_WRITABLE,
|
||||
|
||||
@@ -343,13 +343,19 @@ TEST(close_all_fds) {
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
r = ASSERT_OK(pidref_safe_fork("(caf-nomalloc)", flags, NULL));
|
||||
ASSERT_OK(r = pidref_safe_fork("(caf-nomalloc)", flags, NULL));
|
||||
if (r == 0) {
|
||||
test_close_all_fds_inner(close_all_fds_without_malloc);
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
r = ASSERT_OK(pidref_safe_fork("(caf-frugal)", flags, NULL));
|
||||
ASSERT_OK(r = pidref_safe_fork("(caf-proc)", flags, NULL));
|
||||
if (r == 0) {
|
||||
test_close_all_fds_inner(close_all_fds_by_proc);
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
ASSERT_OK(r = pidref_safe_fork("(caf-frugal)", flags, NULL));
|
||||
if (r == 0) {
|
||||
test_close_all_fds_inner(close_all_fds_frugal);
|
||||
_exit(EXIT_SUCCESS);
|
||||
|
||||
Reference in New Issue
Block a user