// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2018 FUJITSU LIMITED. All rights reserved. * Author: Xiao Yang * * 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 #include #include #include #include #include #include #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, };