2019-07-28 00:22:30 +02:00
/* 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"
2020-09-02 12:21:29 +02:00
# include "../kselftest_harness.h"
2019-07-28 00:22:30 +02:00
# define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr)))
2020-09-02 12:21:30 +02:00
/* Attempt to de-conflict with the selftests tree. */
# ifndef SKIP
# define SKIP(s, ...) XFAIL(s, ##__VA_ARGS__)
# endif
2019-07-28 00:22:30 +02:00
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 ) ;
}
2020-09-02 12:21:29 +02:00
TEST ( wait_simple )
2019-07-28 00:22:30 +02:00
{
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 ) ;
2020-09-02 12:21:29 +02:00
ASSERT_GE ( pidfd , 0 ) ;
2019-07-28 00:22:30 +02:00
pid = sys_waitid ( P_PIDFD , pidfd , & info , WEXITED , NULL ) ;
2020-09-02 12:21:29 +02:00
ASSERT_NE ( pid , 0 ) ;
EXPECT_EQ ( close ( pidfd ) , 0 ) ;
2019-07-28 00:22:30 +02:00
pidfd = - 1 ;
pidfd = open ( " /dev/null " , O_RDONLY | O_CLOEXEC ) ;
2020-09-02 12:21:29 +02:00
ASSERT_GE ( pidfd , 0 ) ;
2019-07-28 00:22:30 +02:00
pid = sys_waitid ( P_PIDFD , pidfd , & info , WEXITED , NULL ) ;
2020-09-02 12:21:29 +02:00
ASSERT_NE ( pid , 0 ) ;
EXPECT_EQ ( close ( pidfd ) , 0 ) ;
2019-07-28 00:22:30 +02:00
pidfd = - 1 ;
pid = sys_clone3 ( & args ) ;
2020-09-02 12:21:30 +02:00
ASSERT_GE ( pid , 0 ) ;
2019-07-28 00:22:30 +02:00
if ( pid = = 0 )
exit ( EXIT_SUCCESS ) ;
pid = sys_waitid ( P_PIDFD , pidfd , & info , WEXITED , NULL ) ;
2020-09-02 12:21:29 +02:00
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 ) ;
2019-07-28 00:22:30 +02:00
}
2020-09-02 12:21:29 +02:00
TEST ( wait_states )
2019-07-28 00:22:30 +02:00
{
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 ) ;
2020-09-02 12:21:29 +02:00
ASSERT_GE ( pid , 0 ) ;
2019-07-28 00:22:30 +02:00
if ( pid = = 0 ) {
kill ( getpid ( ) , SIGSTOP ) ;
kill ( getpid ( ) , SIGSTOP ) ;
exit ( EXIT_SUCCESS ) ;
}
2020-09-02 12:21:29 +02:00
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 ) ;
2019-07-28 00:22:30 +02:00
2020-09-02 12:21:29 +02:00
ASSERT_EQ ( sys_pidfd_send_signal ( pidfd , SIGCONT , NULL , 0 ) , 0 ) ;
2019-07-28 00:22:30 +02:00
2020-09-02 12:21:29 +02:00
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 ) ;
2019-07-28 00:22:30 +02:00
2020-09-02 12:21:29 +02:00
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 ) ;
2019-07-28 00:22:30 +02:00
2020-09-02 12:21:29 +02:00
ASSERT_EQ ( sys_pidfd_send_signal ( pidfd , SIGKILL , NULL , 0 ) , 0 ) ;
2019-07-28 00:22:30 +02:00
2020-09-02 12:21:29 +02:00
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 ) ;
2019-07-28 00:22:30 +02:00
2020-09-02 12:21:29 +02:00
EXPECT_EQ ( close ( pidfd ) , 0 ) ;
2019-07-28 00:22:30 +02:00
}
2020-09-02 12:21:30 +02:00
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 ) ;
2020-10-09 11:52:22 +02:00
ASSERT_EQ ( fcntl ( pidfd , F_SETFL , ( flags & ~ O_NONBLOCK ) ) , 0 ) ;
2020-09-02 12:21:30 +02:00
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 ) ;
}
2020-09-02 12:21:29 +02:00
TEST_HARNESS_MAIN