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* 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 "pidns_helper.h"
50#include "test.h"
51
52char *TCID = "pidns20";
53int TST_TOTAL = 1;
54
55int parent_cinit[2];
56int cinit_parent[2];
57int broken = 1;			/* broken should be 0 when test completes properly */
58
59#define CHILD_PID       1
60#define PARENT_PID      0
61
62/*
63 * child_signal_handler() - to handle SIGUSR1
64 */
65static void child_signal_handler(int sig, siginfo_t * si, void *unused)
66{
67	if (si->si_signo != SIGUSR1)
68		tst_resm(TBROK, "cinit: recieved %s unexpectedly!",
69			 strsignal(si->si_signo));
70	else
71		tst_resm(TPASS, "cinit: user function is called as expected");
72
73	/* Disable broken flag */
74	broken = 0;
75}
76
77/*
78 * child_fn() - Inside container
79 */
80int child_fn(void *arg)
81{
82	pid_t pid, ppid;
83	sigset_t newset;
84	struct sigaction sa;
85	char buf[5];
86
87	/* Setup pipe read and write ends */
88	pid = getpid();
89	ppid = getppid();
90
91	if (pid != CHILD_PID || ppid != PARENT_PID) {
92		printf("cinit: pidns was not created properly\n");
93		exit(1);
94	}
95
96	/* Setup pipes to communicate with parent */
97	close(cinit_parent[0]);
98	close(parent_cinit[1]);
99
100	/* Block SIGUSR1 signal */
101	sigemptyset(&newset);
102	sigaddset(&newset, SIGUSR1);
103	if (sigprocmask(SIG_BLOCK, &newset, 0) == -1) {
104		perror("cinit: sigprocmask() failed");
105		exit(1);
106	}
107	tst_resm(TINFO, "cinit: blocked SIGUSR1");
108
109	/* Let parent to queue SIGUSR1 in pending */
110	if (write(cinit_parent[1], "c:go", 5) != 5) {
111		perror("cinit: pipe is broken to write");
112		exit(1);
113	}
114
115	/* Check if parent has queued up SIGUSR1 */
116	read(parent_cinit[0], buf, 5);
117	if (strcmp(buf, "p:go") != 0) {
118		printf("cinit: parent did not respond!\n");
119		exit(1);
120	}
121
122	/* Now redefine handler for SIGUSR1 */
123	sa.sa_flags = SA_SIGINFO;
124	sigfillset(&sa.sa_mask);
125	sa.sa_sigaction = child_signal_handler;
126	if (sigaction(SIGUSR1, &sa, NULL) == -1) {
127		perror("cinit: sigaction failed");
128		exit(1);
129	}
130
131	/* Unblock traffic on SIGUSR1 queue */
132	tst_resm(TINFO, "cinit: unblocking SIGUSR1");
133	sigprocmask(SIG_UNBLOCK, &newset, 0);
134
135	/* Check if new handler is called */
136	if (broken == 1) {
137		printf("cinit: broken flag didn't change\n");
138		exit(1);
139	}
140
141	/* Cleanup and exit */
142	close(cinit_parent[1]);
143	close(parent_cinit[0]);
144	exit(0);
145}
146
147static void setup(void)
148{
149	tst_require_root();
150	check_newpid();
151}
152
153int main(int argc, char *argv[])
154{
155	int status;
156	char buf[5];
157	pid_t cpid;
158
159	setup();
160
161	/* Create pipes for intercommunication */
162	if (pipe(parent_cinit) == -1 || pipe(cinit_parent) == -1) {
163		tst_brkm(TBROK | TERRNO, NULL, "pipe failed");
164	}
165
166	cpid = ltp_clone_quick(CLONE_NEWPID | SIGCHLD, child_fn, NULL);
167	if (cpid == -1) {
168		tst_brkm(TBROK | TERRNO, NULL, "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, NULL, "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, NULL, "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, NULL, "write failed");
189	}
190
191	/* collect exit status of child */
192	if (wait(&status) == -1) {
193		tst_brkm(TBROK | TERRNO, NULL, "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",
203				 WTERMSIG(status));
204	}
205
206	/* Cleanup and exit */
207	close(parent_cinit[1]);
208	close(cinit_parent[0]);
209	tst_exit();
210}
211