pidns20.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* File: pidns20.c
17* *
18* * Description:
19* *  The pidns20.c testcase verifies that signal handler of SIGUSR1 is called
20* *  (and cinit is NOT terminated) when:
21* *    - container-init blocks SIGUSR1,
22* *    - parent queues SIGUSR1 and
23* *    - a handler is specified for SIGUSR1 before it is unblocked.
24* *
25* * Test Assertion & Strategy:
26* *  Create a PID namespace container.
27* *  Block SIGUSR1 signal inside it.
28* *  Let parent to deliver SIGUSR1 signal to container.
29* *  Redefine SIGUSR1 handler of cinit to user function.
30* *  Unblock SIGUSR1 from blocked queue.
31* *  Check if user function is called.
32* *
33* * Usage: <for command-line>
34* *  pidns20
35* *
36* * History:
37* *  DATE      NAME                             DESCRIPTION
38* *  13/11/08  Gowrishankar M 			Creation of this test.
39* *            <gowrishankar.m@in.ibm.com>
40*
41******************************************************************************/
42#define _GNU_SOURCE 1
43#include <sys/wait.h>
44#include <sys/types.h>
45#include <signal.h>
46#include <stdlib.h>
47#include <unistd.h>
48#include <stdio.h>
49#include "usctest.h"
50#include "test.h"
51#include <libclone.h>
52
53char *TCID = "pidns20";
54int TST_TOTAL = 1;
55
56int errno;
57int parent_cinit[2];
58int cinit_parent[2];
59int broken = 1; /* broken should be 0 when test completes properly */
60
61#define CHILD_PID       1
62#define PARENT_PID      0
63
64void cleanup()
65{
66	/* Clean the test testcase as LTP wants*/
67	TEST_CLEANUP;
68}
69
70/*
71 * child_signal_handler() - to handle SIGUSR1
72 */
73static void child_signal_handler(int sig, siginfo_t *si, void *unused)
74{
75	if (si->si_signo != SIGUSR1)
76		tst_resm(TBROK, "cinit: recieved %s unexpectedly!",\
77				strsignal(si->si_signo));
78	else
79		tst_resm(TPASS, "cinit: user function is called as expected");
80
81	/* Disable broken flag */
82	broken = 0;
83}
84
85/*
86 * child_fn() - Inside container
87 */
88int child_fn(void *arg)
89{
90	pid_t pid, ppid;
91	sigset_t newset;
92	struct sigaction sa;
93	char buf[5];
94
95	/* Setup pipe read and write ends */
96	pid = getpid();
97	ppid = getppid();
98
99	if (pid != CHILD_PID || ppid != PARENT_PID) {
100		printf("cinit: pidns was not created properly\n");
101		exit(1);
102	}
103
104	/* Setup pipes to communicate with parent */
105	close(cinit_parent[0]);
106	close(parent_cinit[1]);
107
108	/* Block SIGUSR1 signal */
109	sigemptyset(&newset);
110	sigaddset(&newset, SIGUSR1);
111	if (sigprocmask(SIG_BLOCK, &newset, 0) == -1) {
112		perror("cinit: sigprocmask() failed");
113		exit(1);
114	}
115	tst_resm(TINFO, "cinit: blocked SIGUSR1");
116
117	/* Let parent to queue SIGUSR1 in pending */
118	if (write(cinit_parent[1], "c:go", 5) != 5) {
119		perror("cinit: pipe is broken to write");
120		exit(1);
121	}
122
123	/* Check if parent has queued up SIGUSR1 */
124	read(parent_cinit[0], buf, 5);
125	if (strcmp(buf, "p:go") != 0) {
126		printf("cinit: parent did not respond!\n");
127		exit(1);
128	}
129
130	/* Now redefine handler for SIGUSR1 */
131	sa.sa_flags = SA_SIGINFO;
132	sigfillset(&sa.sa_mask);
133	sa.sa_sigaction = child_signal_handler;
134	if (sigaction(SIGUSR1, &sa, NULL) == -1) {
135		perror("cinit: sigaction failed");
136		exit(1);
137	}
138
139	/* Unblock traffic on SIGUSR1 queue */
140	tst_resm(TINFO, "cinit: unblocking SIGUSR1");
141	sigprocmask(SIG_UNBLOCK, &newset, 0);
142
143	/* Check if new handler is called */
144	if (broken == 1) {
145		printf("cinit: broken flag didn't change\n");
146		exit(1);
147	}
148
149	/* Cleanup and exit */
150	close(cinit_parent[1]);
151	close(parent_cinit[0]);
152	exit(0);
153}
154
155int main(int argc, char *argv[])
156{
157	int status;
158	char buf[5];
159	pid_t cpid;
160
161	/* Create pipes for intercommunication */
162	if (pipe(parent_cinit) == -1 || pipe(cinit_parent) == -1) {
163		tst_brkm(TBROK|TERRNO, cleanup, "pipe failed");
164	}
165
166	cpid = ltp_clone_quick(CLONE_NEWPID|SIGCHLD, child_fn, NULL);
167	if (cpid == -1) {
168		tst_brkm(TBROK|TERRNO, cleanup, "clone failed");
169	}
170
171	/* Setup pipe read and write ends */
172	close(cinit_parent[1]);
173	close(parent_cinit[0]);
174
175	/* Is container ready */
176	read(cinit_parent[0], buf, 5);
177	if (strcmp(buf, "c:go") != 0) {
178		tst_brkm(TBROK, cleanup, "parent: container did not respond!");
179	}
180
181	/* Enqueue SIGUSR1 in pending signal queue of container */
182	if (kill(cpid, SIGUSR1) == -1) {
183		tst_brkm(TBROK|TERRNO, cleanup, "kill() failed");
184	}
185
186	tst_resm(TINFO, "parent: signalled SIGUSR1 to container");
187	if (write(parent_cinit[1], "p:go", 5) != 5) {
188		tst_brkm(TBROK|TERRNO, cleanup, "write failed");
189	}
190
191	/* collect exit status of child */
192	if (wait(&status) == -1) {
193		tst_brkm(TBROK|TERRNO, cleanup, "wait failed");
194	}
195
196	if (WIFSIGNALED(status)) {
197		if (WTERMSIG(status) == SIGUSR1)
198			tst_resm(TFAIL,
199			    "user function was not called inside cinit");
200		else
201			tst_resm(TBROK,
202			    "cinit was terminated by %d", WTERMSIG(status));
203	}
204
205	/* Cleanup and exit */
206	close(parent_cinit[1]);
207	close(cinit_parent[0]);
208	cleanup();
209	tst_exit();
210}