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