222 lines · 5.2 KB · C
RawDownload
/* 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 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);	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);	ASSERT_NE(pid, 0);	EXPECT_EQ(close(pidfd), 0);	pidfd = -1; 	pid = sys_clone3(&args, sizeof(args));	ASSERT_GE(pid, 0); 	if (pid == 0)		exit(EXIT_SUCCESS); 	pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED);	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, sizeof(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), 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), 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), 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), 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);	ASSERT_LT(ret, 0);	ASSERT_EQ(errno, ECHILD);	EXPECT_EQ(close(pidfd), 0); 	pid = sys_clone3(&args, sizeof(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);	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);	ASSERT_EQ(ret, 0); 	ASSERT_EQ(fcntl(pidfd, F_SETFL, (flags & ~O_NONBLOCK)), 0); 	ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED), 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), 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