| /* 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.h" | |
| #define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr))) | |
| 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); | |
| } | |
| static int test_pidfd_wait_simple(void) | |
| { | |
| const char *test_name = "pidfd wait simple"; | |
| int pidfd = -1, status = 0; | |
| 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 ret; | |
| pid_t pid; | |
| siginfo_t info = { | |
| .si_signo = 0, | |
| }; | |
| pidfd = open("/proc/self", O_DIRECTORY | O_RDONLY | O_CLOEXEC); | |
| if (pidfd < 0) | |
| ksft_exit_fail_msg("%s test: failed to open /proc/self %s\n", | |
| test_name, strerror(errno)); | |
| pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL); | |
| if (pid == 0) | |
| ksft_exit_fail_msg( | |
| "%s test: succeeded to wait on invalid pidfd %s\n", | |
| test_name, strerror(errno)); | |
| close(pidfd); | |
| pidfd = -1; | |
| pidfd = open("/dev/null", O_RDONLY | O_CLOEXEC); | |
| if (pidfd == 0) | |
| ksft_exit_fail_msg("%s test: failed to open /dev/null %s\n", | |
| test_name, strerror(errno)); | |
| pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL); | |
| if (pid == 0) | |
| ksft_exit_fail_msg( | |
| "%s test: succeeded to wait on invalid pidfd %s\n", | |
| test_name, strerror(errno)); | |
| close(pidfd); | |
| pidfd = -1; | |
| pid = sys_clone3(&args); | |
| if (pid < 0) | |
| ksft_exit_fail_msg("%s test: failed to create new process %s\n", | |
| test_name, strerror(errno)); | |
| if (pid == 0) | |
| exit(EXIT_SUCCESS); | |
| pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL); | |
| if (pid < 0) | |
| ksft_exit_fail_msg( | |
| "%s test: failed to wait on process with pid %d and pidfd %d: %s\n", | |
| test_name, parent_tid, pidfd, strerror(errno)); | |
| if (!WIFEXITED(info.si_status) || WEXITSTATUS(info.si_status)) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected status received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, parent_tid, pidfd, strerror(errno)); | |
| close(pidfd); | |
| if (info.si_signo != SIGCHLD) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_signo, parent_tid, pidfd, | |
| strerror(errno)); | |
| if (info.si_code != CLD_EXITED) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_code, parent_tid, pidfd, | |
| strerror(errno)); | |
| if (info.si_pid != parent_tid) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_pid, parent_tid, pidfd, | |
| strerror(errno)); | |
| ksft_test_result_pass("%s test: Passed\n", test_name); | |
| return 0; | |
| } | |
| static int test_pidfd_wait_states(void) | |
| { | |
| const char *test_name = "pidfd wait states"; | |
| int pidfd = -1, status = 0; | |
| 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 ret; | |
| pid_t pid; | |
| siginfo_t info = { | |
| .si_signo = 0, | |
| }; | |
| pid = sys_clone3(&args); | |
| if (pid < 0) | |
| ksft_exit_fail_msg("%s test: failed to create new process %s\n", | |
| test_name, strerror(errno)); | |
| if (pid == 0) { | |
| kill(getpid(), SIGSTOP); | |
| kill(getpid(), SIGSTOP); | |
| exit(EXIT_SUCCESS); | |
| } | |
| ret = sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL); | |
| if (ret < 0) | |
| ksft_exit_fail_msg( | |
| "%s test: failed to wait on WSTOPPED process with pid %d and pidfd %d: %s\n", | |
| test_name, parent_tid, pidfd, strerror(errno)); | |
| if (info.si_signo != SIGCHLD) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_signo, parent_tid, pidfd, | |
| strerror(errno)); | |
| if (info.si_code != CLD_STOPPED) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_code, parent_tid, pidfd, | |
| strerror(errno)); | |
| if (info.si_pid != parent_tid) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_pid, parent_tid, pidfd, | |
| strerror(errno)); | |
| ret = sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0); | |
| if (ret < 0) | |
| ksft_exit_fail_msg( | |
| "%s test: failed to send signal to process with pid %d and pidfd %d: %s\n", | |
| test_name, parent_tid, pidfd, strerror(errno)); | |
| ret = sys_waitid(P_PIDFD, pidfd, &info, WCONTINUED, NULL); | |
| if (ret < 0) | |
| ksft_exit_fail_msg( | |
| "%s test: failed to wait WCONTINUED on process with pid %d and pidfd %d: %s\n", | |
| test_name, parent_tid, pidfd, strerror(errno)); | |
| if (info.si_signo != SIGCHLD) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_signo, parent_tid, pidfd, | |
| strerror(errno)); | |
| if (info.si_code != CLD_CONTINUED) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_code, parent_tid, pidfd, | |
| strerror(errno)); | |
| if (info.si_pid != parent_tid) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_pid, parent_tid, pidfd, | |
| strerror(errno)); | |
| ret = sys_waitid(P_PIDFD, pidfd, &info, WUNTRACED, NULL); | |
| if (ret < 0) | |
| ksft_exit_fail_msg( | |
| "%s test: failed to wait on WUNTRACED process with pid %d and pidfd %d: %s\n", | |
| test_name, parent_tid, pidfd, strerror(errno)); | |
| if (info.si_signo != SIGCHLD) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_signo, parent_tid, pidfd, | |
| strerror(errno)); | |
| if (info.si_code != CLD_STOPPED) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_code, parent_tid, pidfd, | |
| strerror(errno)); | |
| if (info.si_pid != parent_tid) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_pid, parent_tid, pidfd, | |
| strerror(errno)); | |
| ret = sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0); | |
| if (ret < 0) | |
| ksft_exit_fail_msg( | |
| "%s test: failed to send SIGKILL to process with pid %d and pidfd %d: %s\n", | |
| test_name, parent_tid, pidfd, strerror(errno)); | |
| ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL); | |
| if (ret < 0) | |
| ksft_exit_fail_msg( | |
| "%s test: failed to wait on WEXITED process with pid %d and pidfd %d: %s\n", | |
| test_name, parent_tid, pidfd, strerror(errno)); | |
| if (info.si_signo != SIGCHLD) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_signo, parent_tid, pidfd, | |
| strerror(errno)); | |
| if (info.si_code != CLD_KILLED) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_code, parent_tid, pidfd, | |
| strerror(errno)); | |
| if (info.si_pid != parent_tid) | |
| ksft_exit_fail_msg( | |
| "%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n", | |
| test_name, info.si_pid, parent_tid, pidfd, | |
| strerror(errno)); | |
| close(pidfd); | |
| ksft_test_result_pass("%s test: Passed\n", test_name); | |
| return 0; | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| ksft_print_header(); | |
| ksft_set_plan(2); | |
| test_pidfd_wait_simple(); | |
| test_pidfd_wait_states(); | |
| return ksft_exit_pass(); | |
| } |