pidfd_wait.c (5431B)
1/* SPDX-License-Identifier: GPL-2.0 */ 2 3#define _GNU_SOURCE 4#include <errno.h> 5#include <linux/sched.h> 6#include <linux/types.h> 7#include <signal.h> 8#include <stdint.h> 9#include <stdio.h> 10#include <stdlib.h> 11#include <sched.h> 12#include <string.h> 13#include <sys/resource.h> 14#include <sys/time.h> 15#include <sys/types.h> 16#include <sys/wait.h> 17#include <unistd.h> 18 19#include "pidfd.h" 20#include "../kselftest_harness.h" 21 22#define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr))) 23 24/* Attempt to de-conflict with the selftests tree. */ 25#ifndef SKIP 26#define SKIP(s, ...) XFAIL(s, ##__VA_ARGS__) 27#endif 28 29static pid_t sys_clone3(struct clone_args *args) 30{ 31 return syscall(__NR_clone3, args, sizeof(struct clone_args)); 32} 33 34static int sys_waitid(int which, pid_t pid, siginfo_t *info, int options, 35 struct rusage *ru) 36{ 37 return syscall(__NR_waitid, which, pid, info, options, ru); 38} 39 40TEST(wait_simple) 41{ 42 int pidfd = -1; 43 pid_t parent_tid = -1; 44 struct clone_args args = { 45 .parent_tid = ptr_to_u64(&parent_tid), 46 .pidfd = ptr_to_u64(&pidfd), 47 .flags = CLONE_PIDFD | CLONE_PARENT_SETTID, 48 .exit_signal = SIGCHLD, 49 }; 50 pid_t pid; 51 siginfo_t info = { 52 .si_signo = 0, 53 }; 54 55 pidfd = open("/proc/self", O_DIRECTORY | O_RDONLY | O_CLOEXEC); 56 ASSERT_GE(pidfd, 0); 57 58 pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL); 59 ASSERT_NE(pid, 0); 60 EXPECT_EQ(close(pidfd), 0); 61 pidfd = -1; 62 63 pidfd = open("/dev/null", O_RDONLY | O_CLOEXEC); 64 ASSERT_GE(pidfd, 0); 65 66 pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL); 67 ASSERT_NE(pid, 0); 68 EXPECT_EQ(close(pidfd), 0); 69 pidfd = -1; 70 71 pid = sys_clone3(&args); 72 ASSERT_GE(pid, 0); 73 74 if (pid == 0) 75 exit(EXIT_SUCCESS); 76 77 pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL); 78 ASSERT_GE(pid, 0); 79 ASSERT_EQ(WIFEXITED(info.si_status), true); 80 ASSERT_EQ(WEXITSTATUS(info.si_status), 0); 81 EXPECT_EQ(close(pidfd), 0); 82 83 ASSERT_EQ(info.si_signo, SIGCHLD); 84 ASSERT_EQ(info.si_code, CLD_EXITED); 85 ASSERT_EQ(info.si_pid, parent_tid); 86} 87 88TEST(wait_states) 89{ 90 int pidfd = -1; 91 pid_t parent_tid = -1; 92 struct clone_args args = { 93 .parent_tid = ptr_to_u64(&parent_tid), 94 .pidfd = ptr_to_u64(&pidfd), 95 .flags = CLONE_PIDFD | CLONE_PARENT_SETTID, 96 .exit_signal = SIGCHLD, 97 }; 98 pid_t pid; 99 siginfo_t info = { 100 .si_signo = 0, 101 }; 102 103 pid = sys_clone3(&args); 104 ASSERT_GE(pid, 0); 105 106 if (pid == 0) { 107 kill(getpid(), SIGSTOP); 108 kill(getpid(), SIGSTOP); 109 exit(EXIT_SUCCESS); 110 } 111 112 ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL), 0); 113 ASSERT_EQ(info.si_signo, SIGCHLD); 114 ASSERT_EQ(info.si_code, CLD_STOPPED); 115 ASSERT_EQ(info.si_pid, parent_tid); 116 117 ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0), 0); 118 119 ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WCONTINUED, NULL), 0); 120 ASSERT_EQ(info.si_signo, SIGCHLD); 121 ASSERT_EQ(info.si_code, CLD_CONTINUED); 122 ASSERT_EQ(info.si_pid, parent_tid); 123 124 ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WUNTRACED, NULL), 0); 125 ASSERT_EQ(info.si_signo, SIGCHLD); 126 ASSERT_EQ(info.si_code, CLD_STOPPED); 127 ASSERT_EQ(info.si_pid, parent_tid); 128 129 ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0), 0); 130 131 ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL), 0); 132 ASSERT_EQ(info.si_signo, SIGCHLD); 133 ASSERT_EQ(info.si_code, CLD_KILLED); 134 ASSERT_EQ(info.si_pid, parent_tid); 135 136 EXPECT_EQ(close(pidfd), 0); 137} 138 139TEST(wait_nonblock) 140{ 141 int pidfd, status = 0; 142 unsigned int flags = 0; 143 pid_t parent_tid = -1; 144 struct clone_args args = { 145 .parent_tid = ptr_to_u64(&parent_tid), 146 .flags = CLONE_PARENT_SETTID, 147 .exit_signal = SIGCHLD, 148 }; 149 int ret; 150 pid_t pid; 151 siginfo_t info = { 152 .si_signo = 0, 153 }; 154 155 /* 156 * Callers need to see ECHILD with non-blocking pidfds when no child 157 * processes exists. 158 */ 159 pidfd = sys_pidfd_open(getpid(), PIDFD_NONBLOCK); 160 EXPECT_GE(pidfd, 0) { 161 /* pidfd_open() doesn't support PIDFD_NONBLOCK. */ 162 ASSERT_EQ(errno, EINVAL); 163 SKIP(return, "Skipping PIDFD_NONBLOCK test"); 164 } 165 166 ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL); 167 ASSERT_LT(ret, 0); 168 ASSERT_EQ(errno, ECHILD); 169 EXPECT_EQ(close(pidfd), 0); 170 171 pid = sys_clone3(&args); 172 ASSERT_GE(pid, 0); 173 174 if (pid == 0) { 175 kill(getpid(), SIGSTOP); 176 exit(EXIT_SUCCESS); 177 } 178 179 pidfd = sys_pidfd_open(pid, PIDFD_NONBLOCK); 180 EXPECT_GE(pidfd, 0) { 181 /* pidfd_open() doesn't support PIDFD_NONBLOCK. */ 182 ASSERT_EQ(errno, EINVAL); 183 SKIP(return, "Skipping PIDFD_NONBLOCK test"); 184 } 185 186 flags = fcntl(pidfd, F_GETFL, 0); 187 ASSERT_GT(flags, 0); 188 ASSERT_GT((flags & O_NONBLOCK), 0); 189 190 /* 191 * Callers need to see EAGAIN/EWOULDBLOCK with non-blocking pidfd when 192 * child processes exist but none have exited. 193 */ 194 ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL); 195 ASSERT_LT(ret, 0); 196 ASSERT_EQ(errno, EAGAIN); 197 198 /* 199 * Callers need to continue seeing 0 with non-blocking pidfd and 200 * WNOHANG raised explicitly when child processes exist but none have 201 * exited. 202 */ 203 ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED | WNOHANG, NULL); 204 ASSERT_EQ(ret, 0); 205 206 ASSERT_EQ(fcntl(pidfd, F_SETFL, (flags & ~O_NONBLOCK)), 0); 207 208 ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL), 0); 209 ASSERT_EQ(info.si_signo, SIGCHLD); 210 ASSERT_EQ(info.si_code, CLD_STOPPED); 211 ASSERT_EQ(info.si_pid, parent_tid); 212 213 ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0), 0); 214 215 ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL), 0); 216 ASSERT_EQ(info.si_signo, SIGCHLD); 217 ASSERT_EQ(info.si_code, CLD_EXITED); 218 ASSERT_EQ(info.si_pid, parent_tid); 219 220 EXPECT_EQ(close(pidfd), 0); 221} 222 223TEST_HARNESS_MAIN