Home | History | Annotate | Download | only in pidns
      1 /*
      2 * Copyright (c) International Business Machines Corp., 2007
      3 * This program is free software; you can redistribute it and/or modify
      4 * it under the terms of the GNU General Public License as published by
      5 * the Free Software Foundation; either version 2 of the License, or
      6 * (at your option) any later version.
      7 * This program is distributed in the hope that it will be useful
      8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
     10 * the GNU General Public License for more details.
     11 * You should have received a copy of the GNU General Public License
     12 * along with this program; if not, write to the Free Software
     13 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     14 *
     15 ***************************************************************************
     16 *
     17 * Assertion:
     18 *   a) Create a  container.
     19 *   b) Create many levels of child containers inside this container.
     20 *   c) Now do kill -9 init , outside of the container.
     21 *   d) This should kill all the child containers.
     22 *      (containers created at the level below)
     23 *
     24 * Description:
     25 * 1. Parent process clone a process with flag CLONE_NEWPID
     26 * 2. The container will recursively loop and creates 4 more containers.
     27 * 3. All the container init's  goes into sleep(), waiting to be terminated.
     28 * 4. The parent process will kill child[3] by passing SIGKILL
     29 * 5. Now parent process, verifies the child containers 4 & 5 are destroyed.
     30 * 6. If they are killed then
     31 *	Test passed
     32 *  else Test failed.
     33 *
     34 * Test Name: pidns05
     35 *
     36 * History:
     37 *
     38 * FLAG DATE		NAME				DESCRIPTION
     39 * 31/10/08  Veerendra C <vechandr (at) in.ibm.com>	Verifies killing of NestedCont's
     40 *
     41 *******************************************************************************/
     42 #define _GNU_SOURCE 1
     43 #include <sys/wait.h>
     44 #include <assert.h>
     45 #include <stdio.h>
     46 #include <stdlib.h>
     47 #include <unistd.h>
     48 #include <string.h>
     49 #include <errno.h>
     50 #include "pidns_helper.h"
     51 #include "test.h"
     52 
     53 #define INIT_PID	1
     54 #define CINIT_PID	1
     55 #define PARENT_PID	0
     56 #define MAX_DEPTH	5
     57 
     58 char *TCID = "pidns05";
     59 int TST_TOTAL = 1;
     60 int fd[2];
     61 
     62 int max_pid(void)
     63 {
     64 	FILE *fp;
     65 	int ret;
     66 
     67 	fp = fopen("/proc/sys/kernel/pid_max", "r");
     68 	if (fp != NULL) {
     69 		fscanf(fp, "%d", &ret);
     70 		fclose(fp);
     71 	} else {
     72 		tst_resm(TBROK, "Cannot open /proc/sys/kernel/pid_max");
     73 		ret = -1;
     74 	}
     75 	return ret;
     76 }
     77 
     78 /* find_cinit_pids() iteratively finds the pid's having same PGID as its parent.
     79  * Input parameter - Accepts pointer to pid_t : To copy the pid's matching.
     80  * Returns - the number of pids matched.
     81 */
     82 int find_cinit_pids(pid_t * pids)
     83 {
     84 	int next = 0, pid_max, i;
     85 	pid_t parentpid, pgid, pgid2;
     86 
     87 	pid_max = max_pid();
     88 	parentpid = getpid();
     89 	pgid = getpgid(parentpid);
     90 
     91 	/* The loop breaks, when the loop counter reaches the parentpid value */
     92 	for (i = parentpid + 1; i != parentpid; i++) {
     93 		if (i > pid_max)
     94 			i = 2;
     95 
     96 		pgid2 = getpgid(i);
     97 		if (pgid2 == pgid) {
     98 			pids[next] = i;
     99 			next++;
    100 		}
    101 	}
    102 	return next;
    103 }
    104 
    105 /*
    106 * create_nested_container() Recursively create MAX_DEPTH nested containers
    107 */
    108 int create_nested_container(void *vtest)
    109 {
    110 	int exit_val;
    111 	int ret, count, *level;
    112 	pid_t cpid, ppid;
    113 	cpid = getpid();
    114 	ppid = getppid();
    115 	char mesg[] = "Nested Containers are created";
    116 
    117 	level = (int *)vtest;
    118 	count = *level;
    119 
    120 	/* Child process closes up read side of pipe */
    121 	close(fd[0]);
    122 
    123 	/* Comparing the values to make sure pidns is created correctly */
    124 	if (cpid != CINIT_PID || ppid != PARENT_PID) {
    125 		printf("Got unexpected cpid and/or ppid (cpid=%d ppid=%d)\n",
    126 		       cpid, ppid);
    127 		exit_val = 1;
    128 	}
    129 	if (count > 1) {
    130 		count--;
    131 		ret = do_clone_unshare_test(T_CLONE, CLONE_NEWPID,
    132 					    create_nested_container,
    133 					    (void *)&count);
    134 		if (ret == -1) {
    135 			printf("clone failed; errno = %d : %s\n",
    136 			       ret, strerror(ret));
    137 			exit_val = 1;
    138 		} else
    139 			exit_val = 0;
    140 	} else {
    141 		/* Sending mesg, 'Nested containers created' through the pipe */
    142 		write(fd[1], mesg, (strlen(mesg) + 1));
    143 		exit_val = 0;
    144 	}
    145 
    146 	close(fd[1]);
    147 	pause();
    148 
    149 	return exit_val;
    150 }
    151 
    152 void kill_nested_containers()
    153 {
    154 	int orig_count, new_count, status = 0, i;
    155 	pid_t pids[MAX_DEPTH];
    156 	pid_t pids_new[MAX_DEPTH];
    157 
    158 	orig_count = find_cinit_pids(pids);
    159 	kill(pids[MAX_DEPTH - 3], SIGKILL);
    160 	sleep(1);
    161 
    162 	/* After killing child container, getting the New PID list */
    163 	new_count = find_cinit_pids(pids_new);
    164 
    165 	/* Verifying that the child containers were destroyed when parent is killed */
    166 	if (orig_count - 2 != new_count)
    167 		status = -1;
    168 
    169 	for (i = 0; i < new_count; i++) {
    170 		if (pids[i] != pids_new[i])
    171 			status = -1;
    172 	}
    173 
    174 	if (status == 0)
    175 		tst_resm(TPASS, "The number of containers killed are %d",
    176 			 orig_count - new_count);
    177 	else
    178 		tst_resm(TFAIL, "Failed to kill the sub-containers of "
    179 			 "the container %d", pids[MAX_DEPTH - 3]);
    180 
    181 	/* Loops through the containers created to exit from sleep() */
    182 	for (i = 0; i < MAX_DEPTH; i++) {
    183 		kill(pids[i], SIGKILL);
    184 		waitpid(pids[i], &status, 0);
    185 	}
    186 }
    187 
    188 static void setup(void)
    189 {
    190 	tst_require_root();
    191 	check_newpid();
    192 }
    193 
    194 int main(int argc, char *argv[])
    195 {
    196 	int ret, nbytes, status;
    197 	char readbuffer[80];
    198 	pid_t pid, pgid;
    199 	int count = MAX_DEPTH;
    200 
    201 	setup();
    202 
    203 	/*
    204 	 * XXX (garrcoop): why in the hell is this fork-wait written this way?
    205 	 * This doesn't add up with the pattern used for the rest of the tests,
    206 	 * so I'm pretty damn sure this test is written incorrectly.
    207 	 */
    208 	pid = fork();
    209 	if (pid == -1) {
    210 		tst_brkm(TBROK | TERRNO, NULL, "fork failed");
    211 	} else if (pid != 0) {
    212 		/*
    213 		 * NOTE: use waitpid so that we know we're waiting for the
    214 		 * _top-level_ child instead of a spawned subcontainer.
    215 		 *
    216 		 * XXX (garrcoop): Might want to mask SIGCHLD in the top-level
    217 		 * child too, or not *shrugs*.
    218 		 */
    219 		if (waitpid(pid, &status, 0) == -1) {
    220 			perror("wait failed");
    221 		}
    222 		if (WIFEXITED(status))
    223 			exit(WEXITSTATUS(status));
    224 		else
    225 			exit(status);
    226 	}
    227 
    228 	/* To make all the containers share the same PGID as its parent */
    229 	setpgid(0, 0);
    230 
    231 	pid = getpid();
    232 	pgid = getpgid(pid);
    233 	ret = pipe(fd);
    234 	if (ret == -1)
    235 		tst_brkm(TBROK | TERRNO, NULL, "pipe failed");
    236 
    237 	TEST(do_clone_unshare_test(T_CLONE, CLONE_NEWPID,
    238 				   create_nested_container, (void *)&count));
    239 	if (TEST_RETURN == -1) {
    240 		tst_brkm(TFAIL | TTERRNO, NULL, "clone failed");
    241 	}
    242 
    243 	close(fd[1]);
    244 	/* Waiting for the MAX_DEPTH number of containers to be created */
    245 	nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
    246 	close(fd[0]);
    247 	if (nbytes > 0)
    248 		tst_resm(TINFO, " %d %s", MAX_DEPTH, readbuffer);
    249 	else
    250 		tst_brkm(TFAIL, NULL, "unable to create %d containers",
    251 			 MAX_DEPTH);
    252 
    253 	/* Kill the container created */
    254 	kill_nested_containers();
    255 
    256 	tst_exit();
    257 }
    258