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