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@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 #include "safe_macros.h"
53
54 #define INIT_PID 1
55 #define CINIT_PID 1
56 #define PARENT_PID 0
57 #define MAX_DEPTH 5
58
59 char *TCID = "pidns05";
60 int TST_TOTAL = 1;
61 int fd[2];
62
max_pid(void)63 int max_pid(void)
64 {
65 FILE *fp;
66 int ret;
67
68 fp = fopen("/proc/sys/kernel/pid_max", "r");
69 if (fp != NULL) {
70 fscanf(fp, "%d", &ret);
71 fclose(fp);
72 } else {
73 tst_resm(TBROK, "Cannot open /proc/sys/kernel/pid_max");
74 ret = -1;
75 }
76 return ret;
77 }
78
79 /* find_cinit_pids() iteratively finds the pid's having same PGID as its parent.
80 * Input parameter - Accepts pointer to pid_t : To copy the pid's matching.
81 * Returns - the number of pids matched.
82 */
find_cinit_pids(pid_t * pids)83 int find_cinit_pids(pid_t * pids)
84 {
85 int next = 0, pid_max, i;
86 pid_t parentpid, pgid, pgid2;
87
88 pid_max = max_pid();
89 parentpid = getpid();
90 pgid = getpgid(parentpid);
91
92 /* The loop breaks, when the loop counter reaches the parentpid value */
93 for (i = parentpid + 1; i != parentpid; i++) {
94 if (i > pid_max)
95 i = 2;
96
97 pgid2 = getpgid(i);
98 if (pgid2 == pgid) {
99 pids[next] = i;
100 next++;
101 }
102 }
103 return next;
104 }
105
106 /*
107 * create_nested_container() Recursively create MAX_DEPTH nested containers
108 */
create_nested_container(void * vtest)109 int create_nested_container(void *vtest)
110 {
111 int exit_val;
112 int ret, count, *level;
113 pid_t cpid, ppid;
114 cpid = getpid();
115 ppid = getppid();
116 char mesg[] = "Nested Containers are created";
117
118 level = (int *)vtest;
119 count = *level;
120
121 /* Child process closes up read side of pipe */
122 close(fd[0]);
123
124 /* Comparing the values to make sure pidns is created correctly */
125 if (cpid != CINIT_PID || ppid != PARENT_PID) {
126 printf("Got unexpected cpid and/or ppid (cpid=%d ppid=%d)\n",
127 cpid, ppid);
128 exit_val = 1;
129 }
130 if (count > 1) {
131 count--;
132 ret = do_clone_unshare_test(T_CLONE, CLONE_NEWPID,
133 create_nested_container,
134 (void *)&count);
135 if (ret == -1) {
136 printf("clone failed; errno = %d : %s\n",
137 ret, strerror(ret));
138 exit_val = 1;
139 } else
140 exit_val = 0;
141 } else {
142 /* Sending mesg, 'Nested containers created' through the pipe */
143 write(fd[1], mesg, (strlen(mesg) + 1));
144 exit_val = 0;
145 }
146
147 close(fd[1]);
148 pause();
149
150 return exit_val;
151 }
152
kill_nested_containers()153 void kill_nested_containers()
154 {
155 int orig_count, new_count, status = 0, i;
156 pid_t pids[MAX_DEPTH];
157 pid_t pids_new[MAX_DEPTH];
158
159 orig_count = find_cinit_pids(pids);
160 kill(pids[MAX_DEPTH - 3], SIGKILL);
161 sleep(1);
162
163 /* After killing child container, getting the New PID list */
164 new_count = find_cinit_pids(pids_new);
165
166 /* Verifying that the child containers were destroyed when parent is killed */
167 if (orig_count - 2 != new_count)
168 status = -1;
169
170 for (i = 0; i < new_count; i++) {
171 if (pids[i] != pids_new[i])
172 status = -1;
173 }
174
175 if (status == 0)
176 tst_resm(TPASS, "The number of containers killed are %d",
177 orig_count - new_count);
178 else
179 tst_resm(TFAIL, "Failed to kill the sub-containers of "
180 "the container %d", pids[MAX_DEPTH - 3]);
181
182 /* Loops through the containers created to exit from sleep() */
183 for (i = 0; i < MAX_DEPTH; i++) {
184 kill(pids[i], SIGKILL);
185 waitpid(pids[i], &status, 0);
186 }
187 }
188
setup(void)189 static void setup(void)
190 {
191 tst_require_root();
192 check_newpid();
193 }
194
main(void)195 int main(void)
196 {
197 int ret, nbytes, status;
198 char readbuffer[80];
199 pid_t pid, pgid;
200 int count = MAX_DEPTH;
201
202 setup();
203
204 /*
205 * XXX (garrcoop): why in the hell is this fork-wait written this way?
206 * This doesn't add up with the pattern used for the rest of the tests,
207 * so I'm pretty damn sure this test is written incorrectly.
208 */
209 pid = fork();
210 if (pid == -1) {
211 tst_brkm(TBROK | TERRNO, NULL, "fork failed");
212 } else if (pid != 0) {
213 /*
214 * NOTE: use waitpid so that we know we're waiting for the
215 * _top-level_ child instead of a spawned subcontainer.
216 *
217 * XXX (garrcoop): Might want to mask SIGCHLD in the top-level
218 * child too, or not *shrugs*.
219 */
220 if (waitpid(pid, &status, 0) == -1) {
221 perror("wait failed");
222 }
223 if (WIFEXITED(status))
224 exit(WEXITSTATUS(status));
225 else
226 exit(status);
227 }
228
229 /* To make all the containers share the same PGID as its parent */
230 setpgid(0, 0);
231
232 pid = getpid();
233 pgid = getpgid(pid);
234 SAFE_PIPE(NULL, fd);
235
236 TEST(do_clone_unshare_test(T_CLONE, CLONE_NEWPID,
237 create_nested_container, (void *)&count));
238 if (TEST_RETURN == -1) {
239 tst_brkm(TFAIL | TTERRNO, NULL, "clone failed");
240 }
241
242 close(fd[1]);
243 /* Waiting for the MAX_DEPTH number of containers to be created */
244 nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
245 close(fd[0]);
246 if (nbytes > 0)
247 tst_resm(TINFO, " %d %s", MAX_DEPTH, readbuffer);
248 else
249 tst_brkm(TFAIL, NULL, "unable to create %d containers",
250 MAX_DEPTH);
251
252 /* Kill the container created */
253 kill_nested_containers();
254
255 tst_exit();
256 }
257