From: Yu Watanabe Date: Wed, 5 Aug 2026 17:23:16 +0000 (+0900) Subject: pidfd-util: introduce pidfd_info_mask_is_supported() helper function X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7ae2bc919915e09e2d79febfbd68ae6265a22088;p=thirdparty%2Fsystemd.git pidfd-util: introduce pidfd_info_mask_is_supported() helper function Since kernel v6.19, ioctl(PIDFD_GET_INFO) can return the set of supported flags when PIDFD_INFO_SUPPORTED_MASK is specified. This allows checking whether, e.g., PIDFD_INFO_COREDUMP_SIGNAL is supported by the running kernel. --- diff --git a/src/basic/pidfd-util.c b/src/basic/pidfd-util.c index 545d3876137..f15235f5c22 100644 --- a/src/basic/pidfd-util.c +++ b/src/basic/pidfd-util.c @@ -92,6 +92,48 @@ int pidfd_get_info(int fd, struct pidfd_info *info) { return 0; } +int pidfd_info_mask_is_supported(uint64_t mask) { + static thread_local uint64_t cached_mask = 0; + static thread_local int cached = 0; /* 0 when not cached, 1 when cached, negative errno on failure. */ + int r; + + if (cached < 0) + return cached; + + if (cached == 0) { + _cleanup_close_ int pidfd = r = RET_NERRNO(pidfd_open(getpid_cached(), /* flags= */ 0)); + if (r < 0) + return r; + + struct pidfd_info info = { + .mask = PIDFD_INFO_SUPPORTED_MASK, /* Since dfd78546c95330db2252e0d7e937a15ab5eddb4e (v6.19). */ + }; + + r = pidfd_get_info(pidfd, &info); + if (r == -EOPNOTSUPP) + return (cached = -EOPNOTSUPP); + if (r < 0) + return r; + + if (FLAGS_SET(info.mask, PIDFD_INFO_SUPPORTED_MASK) && + FLAGS_SET(info.supported_mask, PIDFD_INFO_SUPPORTED_MASK)) + /* The kernel is v6.19 or newer. Let's use the provided supported_mask. */ + cached_mask = info.supported_mask; + else + /* The kernel is older than v6.19. These three flags have been supported since + * ioctl(PIDFD_GET_INFO) was introduced, so a successful pidfd_get_info() call + * guarantees they are supported. However, there is no reliable way to + * determine whether PIDFD_INFO_EXIT and PIDFD_INFO_COREDUMP are supported, as + * they predate PIDFD_INFO_SUPPORTED_MASK, so assume they are not. */ + cached_mask = PIDFD_INFO_PID | PIDFD_INFO_CREDS | PIDFD_INFO_CGROUPID; + + cached = true; + } + + /* Return true when all requested features are supported. */ + return FLAGS_SET(cached_mask, mask); +} + static int pidfd_get_pid_fdinfo(int fd, pid_t *ret) { char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)]; _cleanup_free_ char *p = NULL; diff --git a/src/basic/pidfd-util.h b/src/basic/pidfd-util.h index d6d3e461393..2503a10efe8 100644 --- a/src/basic/pidfd-util.h +++ b/src/basic/pidfd-util.h @@ -8,6 +8,7 @@ int pidfd_get_namespace(int fd, unsigned long ns_type_cmd); int pidfd_get_info(int fd, struct pidfd_info *info); +int pidfd_info_mask_is_supported(uint64_t mask); int pidfd_get_pid(int fd, pid_t *ret); int pidfd_verify_pid(int pidfd, pid_t pid); diff --git a/src/test/test-pidfd-util.c b/src/test/test-pidfd-util.c index 12e9b313348..e77e6b7cbde 100644 --- a/src/test/test-pidfd-util.c +++ b/src/test/test-pidfd-util.c @@ -21,4 +21,45 @@ TEST(pidfd_get_inode_id_self_cached) { } } +TEST(pidfd_info_mask_is_supported) { + int r; + + r = pidfd_info_mask_is_supported(PIDFD_INFO_PID | PIDFD_INFO_CREDS | PIDFD_INFO_CGROUPID); + if (r > 0) + log_info("PIDFD_INFO_PID, PIDFD_INFO_CREDS, and PIDFD_INFO_CGROUPID are supported."); + else { + ASSERT_ERROR(r, EOPNOTSUPP); + log_info("ioctl(PIDFD_GET_INFO) is not supported. Maybe the kernel is older than v6.13."); + } + + /* The flag PIDFD_INFO_EXIT itself is supported since v6.15, but we have no way to check if it is + * supported without PIDFD_INFO_SUPPORTED_MASK (since v6.19) (or, of course, we can check it by + * terminating a process...). */ + if (ASSERT_OK_OR(pidfd_info_mask_is_supported(PIDFD_INFO_EXIT), -EOPNOTSUPP) > 0) + log_info("PIDFD_INFO_EXIT is supported."); + else + log_info("PIDFD_INFO_EXIT is not supported. Maybe the kernel is older than v6.19."); + + /* Similar here. The flag PIDFD_INFO_COREDUMP itself is supported since v6.16. */ + if (ASSERT_OK_OR(pidfd_info_mask_is_supported(PIDFD_INFO_COREDUMP), -EOPNOTSUPP) > 0) + log_info("PIDFD_INFO_COREDUMP is supported."); + else + log_info("PIDFD_INFO_COREDUMP is not supported. Maybe the kernel is older than v6.19."); + + if (ASSERT_OK_OR(pidfd_info_mask_is_supported(PIDFD_INFO_SUPPORTED_MASK), -EOPNOTSUPP) > 0) + log_info("PIDFD_INFO_SUPPORTED_MASK is supported."); + else + log_info("PIDFD_INFO_SUPPORTED_MASK is not supported. Maybe the kernel is older than v6.19."); + + if (ASSERT_OK_OR(pidfd_info_mask_is_supported(PIDFD_INFO_COREDUMP_SIGNAL), -EOPNOTSUPP) > 0) + log_info("PIDFD_INFO_COREDUMP_SIGNAL is supported."); + else + log_info("PIDFD_INFO_COREDUMP_SIGNAL is not supported. Maybe the kernel is older than v6.19."); + + if (ASSERT_OK_OR(pidfd_info_mask_is_supported(PIDFD_INFO_COREDUMP_CODE), -EOPNOTSUPP) > 0) + log_info("PIDFD_INFO_COREDUMP_CODE is supported."); + else + log_info("PIDFD_INFO_COREDUMP_CODE is not supported. Maybe the kernel is older than v7.1."); +} + DEFINE_TEST_MAIN(LOG_DEBUG);