| /* |
| * Copyright (c) International Business Machines Corp., 2007 |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * This program is distributed in the hope that it will be useful |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See |
| * the GNU General Public License for more details. |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| *************************************************************************** |
| * |
| * Assertion: |
| * a) Create a container. |
| * b) Create many levels of child containers inside this container. |
| * c) Now do kill -9 init , outside of the container. |
| * d) This should kill all the child containers. |
| * (containers created at the level below) |
| * |
| * Description: |
| * 1. Parent process clone a process with flag CLONE_NEWPID |
| * 2. The container will recursively loop and creates 4 more containers. |
| * 3. All the container init's goes into sleep(), waiting to be terminated. |
| * 4. The parent process will kill child[3] by passing SIGKILL |
| * 5. Now parent process, verifies the child containers 4 & 5 are destroyed. |
| * 6. If they are killed then |
| * Test passed |
| * else Test failed. |
| * |
| * Test Name: pidns05 |
| * |
| * History: |
| * |
| * FLAG DATE NAME DESCRIPTION |
| * 31/10/08 Veerendra C <vechandr@in.ibm.com> Verifies killing of NestedCont's |
| * |
| *******************************************************************************/ |
| #define _GNU_SOURCE 1 |
| #include <sys/wait.h> |
| #include <assert.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <errno.h> |
| #include "test.h" |
| #include <libclone.h> |
| #include "pidns_helper.h" |
| |
| #define INIT_PID 1 |
| #define CINIT_PID 1 |
| #define PARENT_PID 0 |
| #define MAX_DEPTH 5 |
| |
| char *TCID = "pidns05"; |
| int TST_TOTAL = 1; |
| int fd[2]; |
| |
| void cleanup(void) |
| { |
| } |
| |
| int max_pid(void) |
| { |
| FILE *fp; |
| int ret; |
| |
| fp = fopen("/proc/sys/kernel/pid_max", "r"); |
| if (fp != NULL) { |
| fscanf(fp, "%d", &ret); |
| fclose(fp); |
| } else { |
| tst_resm(TBROK, "Cannot open /proc/sys/kernel/pid_max"); |
| ret = -1; |
| } |
| return ret; |
| } |
| |
| /* find_cinit_pids() iteratively finds the pid's having same PGID as its parent. |
| * Input parameter - Accepts pointer to pid_t : To copy the pid's matching. |
| * Returns - the number of pids matched. |
| */ |
| int find_cinit_pids(pid_t * pids) |
| { |
| int next = 0, pid_max, i; |
| pid_t parentpid, pgid, pgid2; |
| |
| pid_max = max_pid(); |
| parentpid = getpid(); |
| pgid = getpgid(parentpid); |
| |
| /* The loop breaks, when the loop counter reaches the parentpid value */ |
| for (i = parentpid + 1; i != parentpid; i++) { |
| if (i > pid_max) |
| i = 2; |
| |
| pgid2 = getpgid(i); |
| if (pgid2 == pgid) { |
| pids[next] = i; |
| next++; |
| } |
| } |
| return next; |
| } |
| |
| /* |
| * create_nested_container() Recursively create MAX_DEPTH nested containers |
| */ |
| int create_nested_container(void *vtest) |
| { |
| int exit_val; |
| int ret, count, *level; |
| pid_t cpid, ppid; |
| cpid = getpid(); |
| ppid = getppid(); |
| char mesg[] = "Nested Containers are created"; |
| |
| level = (int *)vtest; |
| count = *level; |
| |
| /* Child process closes up read side of pipe */ |
| close(fd[0]); |
| |
| /* Comparing the values to make sure pidns is created correctly */ |
| if (cpid != CINIT_PID || ppid != PARENT_PID) { |
| printf("Got unexpected cpid and/or ppid (cpid=%d ppid=%d)\n", |
| cpid, ppid); |
| exit_val = 1; |
| } |
| if (count > 1) { |
| count--; |
| ret = do_clone_unshare_test(T_CLONE, CLONE_NEWPID, |
| create_nested_container, |
| (void *)&count); |
| if (ret == -1) { |
| printf("clone failed; errno = %d : %s\n", |
| ret, strerror(ret)); |
| exit_val = 1; |
| } else |
| exit_val = 0; |
| } else { |
| /* Sending mesg, 'Nested containers created' through the pipe */ |
| write(fd[1], mesg, (strlen(mesg) + 1)); |
| exit_val = 0; |
| } |
| |
| close(fd[1]); |
| pause(); |
| |
| return exit_val; |
| } |
| |
| void kill_nested_containers() |
| { |
| int orig_count, new_count, status = 0, i; |
| pid_t pids[MAX_DEPTH]; |
| pid_t pids_new[MAX_DEPTH]; |
| |
| orig_count = find_cinit_pids(pids); |
| kill(pids[MAX_DEPTH - 3], SIGKILL); |
| sleep(1); |
| |
| /* After killing child container, getting the New PID list */ |
| new_count = find_cinit_pids(pids_new); |
| |
| /* Verifying that the child containers were destroyed when parent is killed */ |
| if (orig_count - 2 != new_count) |
| status = -1; |
| |
| for (i = 0; i < new_count; i++) { |
| if (pids[i] != pids_new[i]) |
| status = -1; |
| } |
| |
| if (status == 0) |
| tst_resm(TPASS, "The number of containers killed are %d", |
| orig_count - new_count); |
| else |
| tst_resm(TFAIL, "Failed to kill the sub-containers of " |
| "the container %d", pids[MAX_DEPTH - 3]); |
| |
| /* Loops through the containers created to exit from sleep() */ |
| for (i = 0; i < MAX_DEPTH; i++) { |
| kill(pids[i], SIGKILL); |
| waitpid(pids[i], &status, 0); |
| } |
| } |
| |
| static void setup(void) |
| { |
| tst_require_root(NULL); |
| check_newpid(); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int ret, nbytes, status; |
| char readbuffer[80]; |
| pid_t pid, pgid; |
| int count = MAX_DEPTH; |
| |
| setup(); |
| |
| /* |
| * XXX (garrcoop): why in the hell is this fork-wait written this way? |
| * This doesn't add up with the pattern used for the rest of the tests, |
| * so I'm pretty damn sure this test is written incorrectly. |
| */ |
| pid = fork(); |
| if (pid == -1) { |
| tst_brkm(TBROK | TERRNO, NULL, "fork failed"); |
| } else if (pid != 0) { |
| /* |
| * NOTE: use waitpid so that we know we're waiting for the |
| * _top-level_ child instead of a spawned subcontainer. |
| * |
| * XXX (garrcoop): Might want to mask SIGCHLD in the top-level |
| * child too, or not *shrugs*. |
| */ |
| if (waitpid(pid, &status, 0) == -1) { |
| perror("wait failed"); |
| } |
| if (WIFEXITED(status)) |
| exit(WEXITSTATUS(status)); |
| else |
| exit(status); |
| } |
| |
| /* To make all the containers share the same PGID as its parent */ |
| setpgid(0, 0); |
| |
| pid = getpid(); |
| pgid = getpgid(pid); |
| ret = pipe(fd); |
| if (ret == -1) |
| tst_brkm(TBROK | TERRNO, cleanup, "pipe failed"); |
| |
| TEST(do_clone_unshare_test(T_CLONE, CLONE_NEWPID, |
| create_nested_container, (void *)&count)); |
| if (TEST_RETURN == -1) { |
| tst_brkm(TFAIL | TTERRNO, cleanup, "clone failed"); |
| } |
| |
| close(fd[1]); |
| /* Waiting for the MAX_DEPTH number of containers to be created */ |
| nbytes = read(fd[0], readbuffer, sizeof(readbuffer)); |
| close(fd[0]); |
| if (nbytes > 0) |
| tst_resm(TINFO, " %d %s", MAX_DEPTH, readbuffer); |
| else |
| tst_brkm(TFAIL, cleanup, "unable to create %d containers", |
| MAX_DEPTH); |
| |
| /* Kill the container created */ |
| kill_nested_containers(); |
| cleanup(); |
| |
| tst_exit(); |
| } |