| // 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, |
| }; |