mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-04 16:25:34 +00:00

Verify that the PIDFD_NONBLOCK flag works with pidfd_open() and that waitid() with a non-blocking pidfd returns EAGAIN: TAP version 13 1..3 # Starting 3 tests from 1 test cases. # RUN global.wait_simple ... # OK global.wait_simple ok 1 global.wait_simple # RUN global.wait_states ... # OK global.wait_states ok 2 global.wait_states # RUN global.wait_nonblock ... # OK global.wait_nonblock ok 3 global.wait_nonblock # PASSED: 3 / 3 tests passed. # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com> Reviewed-by: Josh Triplett <josh@joshtriplett.org> Cc: Shuah Khan <shuah@kernel.org> Cc: linux-kselftest@vger.kernel.org Link: https://lore.kernel.org/r/20200902102130.147672-5-christian.brauner@ubuntu.com
225 lines
5.3 KiB
C
225 lines
5.3 KiB
C
/* 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, 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);
|
|
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, 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);
|
|
ASSERT_GE(pid, 0);
|
|
|
|
if (pid == 0) {
|
|
kill(getpid(), SIGSTOP);
|
|
kill(getpid(), SIGSTOP);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
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(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, status = 0;
|
|
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(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(fcntl(pidfd, F_SETFL, (flags & ~O_NONBLOCK)), 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
|