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