123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- /* SPDX-License-Identifier: GPL-2.0 */
- #define _GNU_SOURCE
- #include <errno.h>
- #include <linux/sched.h>
- #include <linux/types.h>
- #include <signal.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <sched.h>
- #include <string.h>
- #include <sys/resource.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <unistd.h>
- #include "pidfd.h"
- #include "../kselftest_harness.h"
- #define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr)))
- /* Attempt to de-conflict with the selftests tree. */
- #ifndef SKIP
- #define SKIP(s, ...) XFAIL(s, ##__VA_ARGS__)
- #endif
- static pid_t sys_clone3(struct clone_args *args)
- {
- return syscall(__NR_clone3, args, sizeof(struct clone_args));
- }
- static int sys_waitid(int which, pid_t pid, siginfo_t *info, int options,
- struct rusage *ru)
- {
- return syscall(__NR_waitid, which, pid, info, options, ru);
- }
- TEST(wait_simple)
- {
- int pidfd = -1;
- pid_t parent_tid = -1;
- struct clone_args args = {
- .parent_tid = ptr_to_u64(&parent_tid),
- .pidfd = ptr_to_u64(&pidfd),
- .flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
- .exit_signal = SIGCHLD,
- };
- pid_t pid;
- siginfo_t info = {
- .si_signo = 0,
- };
- pidfd = open("/proc/self", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
- ASSERT_GE(pidfd, 0);
- pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
- ASSERT_NE(pid, 0);
- EXPECT_EQ(close(pidfd), 0);
- pidfd = -1;
- pidfd = open("/dev/null", O_RDONLY | O_CLOEXEC);
- ASSERT_GE(pidfd, 0);
- pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
- ASSERT_NE(pid, 0);
- EXPECT_EQ(close(pidfd), 0);
- pidfd = -1;
- pid = sys_clone3(&args);
- ASSERT_GE(pid, 0);
- if (pid == 0)
- exit(EXIT_SUCCESS);
- pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
- ASSERT_GE(pid, 0);
- ASSERT_EQ(WIFEXITED(info.si_status), true);
- ASSERT_EQ(WEXITSTATUS(info.si_status), 0);
- EXPECT_EQ(close(pidfd), 0);
- ASSERT_EQ(info.si_signo, SIGCHLD);
- ASSERT_EQ(info.si_code, CLD_EXITED);
- ASSERT_EQ(info.si_pid, parent_tid);
- }
- TEST(wait_states)
- {
- int pidfd = -1;
- pid_t parent_tid = -1;
- struct clone_args args = {
- .parent_tid = ptr_to_u64(&parent_tid),
- .pidfd = ptr_to_u64(&pidfd),
- .flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
- .exit_signal = SIGCHLD,
- };
- int pfd[2];
- pid_t pid;
- siginfo_t info = {
- .si_signo = 0,
- };
- ASSERT_EQ(pipe(pfd), 0);
- pid = sys_clone3(&args);
- ASSERT_GE(pid, 0);
- if (pid == 0) {
- char buf[2];
- close(pfd[1]);
- kill(getpid(), SIGSTOP);
- ASSERT_EQ(read(pfd[0], buf, 1), 1);
- close(pfd[0]);
- kill(getpid(), SIGSTOP);
- exit(EXIT_SUCCESS);
- }
- close(pfd[0]);
- ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL), 0);
- ASSERT_EQ(info.si_signo, SIGCHLD);
- ASSERT_EQ(info.si_code, CLD_STOPPED);
- ASSERT_EQ(info.si_pid, parent_tid);
- ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0), 0);
- ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WCONTINUED, NULL), 0);
- ASSERT_EQ(write(pfd[1], "C", 1), 1);
- close(pfd[1]);
- ASSERT_EQ(info.si_signo, SIGCHLD);
- ASSERT_EQ(info.si_code, CLD_CONTINUED);
- ASSERT_EQ(info.si_pid, parent_tid);
- ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WUNTRACED, NULL), 0);
- ASSERT_EQ(info.si_signo, SIGCHLD);
- ASSERT_EQ(info.si_code, CLD_STOPPED);
- ASSERT_EQ(info.si_pid, parent_tid);
- ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0), 0);
- ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL), 0);
- ASSERT_EQ(info.si_signo, SIGCHLD);
- ASSERT_EQ(info.si_code, CLD_KILLED);
- ASSERT_EQ(info.si_pid, parent_tid);
- EXPECT_EQ(close(pidfd), 0);
- }
- TEST(wait_nonblock)
- {
- int pidfd;
- unsigned int flags = 0;
- pid_t parent_tid = -1;
- struct clone_args args = {
- .parent_tid = ptr_to_u64(&parent_tid),
- .flags = CLONE_PARENT_SETTID,
- .exit_signal = SIGCHLD,
- };
- int ret;
- pid_t pid;
- siginfo_t info = {
- .si_signo = 0,
- };
- /*
- * Callers need to see ECHILD with non-blocking pidfds when no child
- * processes exists.
- */
- pidfd = sys_pidfd_open(getpid(), PIDFD_NONBLOCK);
- EXPECT_GE(pidfd, 0) {
- /* pidfd_open() doesn't support PIDFD_NONBLOCK. */
- ASSERT_EQ(errno, EINVAL);
- SKIP(return, "Skipping PIDFD_NONBLOCK test");
- }
- ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
- ASSERT_LT(ret, 0);
- ASSERT_EQ(errno, ECHILD);
- EXPECT_EQ(close(pidfd), 0);
- pid = sys_clone3(&args);
- ASSERT_GE(pid, 0);
- if (pid == 0) {
- kill(getpid(), SIGSTOP);
- exit(EXIT_SUCCESS);
- }
- pidfd = sys_pidfd_open(pid, PIDFD_NONBLOCK);
- EXPECT_GE(pidfd, 0) {
- /* pidfd_open() doesn't support PIDFD_NONBLOCK. */
- ASSERT_EQ(errno, EINVAL);
- SKIP(return, "Skipping PIDFD_NONBLOCK test");
- }
- flags = fcntl(pidfd, F_GETFL, 0);
- ASSERT_GT(flags, 0);
- ASSERT_GT((flags & O_NONBLOCK), 0);
- /*
- * Callers need to see EAGAIN/EWOULDBLOCK with non-blocking pidfd when
- * child processes exist but none have exited.
- */
- ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
- ASSERT_LT(ret, 0);
- ASSERT_EQ(errno, EAGAIN);
- /*
- * Callers need to continue seeing 0 with non-blocking pidfd and
- * WNOHANG raised explicitly when child processes exist but none have
- * exited.
- */
- ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED | WNOHANG, NULL);
- ASSERT_EQ(ret, 0);
- ASSERT_EQ(fcntl(pidfd, F_SETFL, (flags & ~O_NONBLOCK)), 0);
- ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL), 0);
- ASSERT_EQ(info.si_signo, SIGCHLD);
- ASSERT_EQ(info.si_code, CLD_STOPPED);
- ASSERT_EQ(info.si_pid, parent_tid);
- ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0), 0);
- ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL), 0);
- ASSERT_EQ(info.si_signo, SIGCHLD);
- ASSERT_EQ(info.si_code, CLD_EXITED);
- ASSERT_EQ(info.si_pid, parent_tid);
- EXPECT_EQ(close(pidfd), 0);
- }
- TEST_HARNESS_MAIN
|