blob: 33d5a7e594d0c939fb20855620bb7818230d5737 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2018 FUJITSU LIMITED. All rights reserved.
* Author: Xiao Yang <yangx.jy@cn.fujitsu.com>
*
* Test PR_SET_CHILD_SUBREAPER and PR_GET_CHILD_SUBREAPER of prctl(2).
* 1) If PR_SET_CHILD_SUBREAPER marks a process as a child subreaper, it
* fulfills the role of init(1) for its descendant orphaned process.
* The PPID of its orphaned process will be reparented to the subreaper
* process, and the subreaper process can receive a SIGCHLD signal and
* wait(2) on the orphaned process to discover corresponding termination
* status.
* 2) The setting of PR_SET_CHILD_SUBREAPER is not inherited by children
* created by fork(2).
* 3) PR_GET_CHILD_SUBREAPER can get the setting of PR_SET_CHILD_SUBREAPER.
*
* These flags was added by kernel commit ebec18a6d3aa:
* "prctl: add PR_{SET,GET}_CHILD_SUBREAPER to allow simple process supervision"
*/
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/prctl.h>
#include "tst_test.h"
#include "lapi/prctl.h"
static volatile int sigchild_recv;
static void check_get_subreaper(int exp_val)
{
int get_val;
TEST(prctl(PR_GET_CHILD_SUBREAPER, &get_val));
if (TST_RET == -1) {
tst_res(TFAIL | TTERRNO, "prctl(PR_GET_CHILD_SUBREAPER) failed");
return;
}
if (get_val == exp_val) {
tst_res(TPASS, "prctl(PR_GET_CHILD_SUBREAPER) got expected %d",
get_val);
} else {
tst_res(TFAIL, "prctl(PR_GET_CHILD_SUBREAPER) got %d, expected %d",
get_val, exp_val);
}
}
static void verify_prctl(void)
{
int status, ret;
pid_t pid;
pid_t ppid = getpid();
sigchild_recv = 0;
TEST(prctl(PR_SET_CHILD_SUBREAPER, 1));
if (TST_RET == -1) {
if (TST_ERR == EINVAL) {
tst_res(TCONF,
"prctl() doesn't support PR_SET_CHILD_SUBREAPER");
} else {
tst_res(TFAIL | TTERRNO,
"prctl(PR_SET_CHILD_SUBREAPER) failed");
}
return;
}
tst_res(TPASS, "prctl(PR_SET_CHILD_SUBREAPER) succeeded");
pid = SAFE_FORK();
if (!pid) {
pid_t cpid;
cpid = SAFE_FORK();
if (!cpid) {
TST_CHECKPOINT_WAIT(0);
if (getppid() != ppid) {
tst_res(TFAIL,
"PPID of orphaned process was not reparented");
exit(0);
}
tst_res(TPASS, "PPID of orphaned process was reparented");
exit(0);
}
check_get_subreaper(0);
exit(0);
}
SAFE_WAITPID(pid, NULL, 0);
TST_CHECKPOINT_WAKE(0);
ret = wait(&status);
if (ret > 0) {
tst_res(TPASS, "wait() got orphaned process, pid %d, status %d",
ret, status);
} else {
tst_res(TFAIL | TERRNO,
"wait() failed to get orphaned process");
}
if (sigchild_recv == 2)
tst_res(TPASS, "received SIGCHLD from orphaned process");
else
tst_res(TFAIL, "didn't receive SIGCHLD from orphaned process");
check_get_subreaper(1);
}
static void sighandler(int sig LTP_ATTRIBUTE_UNUSED)
{
sigchild_recv++;
}
static void setup(void)
{
SAFE_SIGNAL(SIGCHLD, sighandler);
}
static struct tst_test test = {
.setup = setup,
.forks_child = 1,
.needs_checkpoints = 1,
.test_all = verify_prctl,
};