pidns30.c revision ce23f15806425582250d38d41a02b1a547a32d7b
1/*
2* Copyright (c) Bull S.A.S. 2008
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: pidns30.c
17*
18*   Description:
19*    This testcase checks if the si_pid is correctly set when a process
20*    that has registered for notification on a posix mqueue is in a
21*    descendant namespace wrt the process that sends a message to that posix
22*    mqueue.
23*
24*   Test Assertion & Strategy:
25*    Parent                                   Child
26*    --------------------------------------------------------------------------
27*    Create a POSIX mqueue.
28*    Create a PID namespace container.
29*                                             Open that mqueue for reading
30*                                             Register for notification when a
31*                                                message arrives in that mqueue
32*                                             Install a handler for SIGUSR1.
33*    Write something to the mqueue.
34*                                             Inside the handler, check that
35*                                                si_pid is set to 0
36*
37*   Usage: <for command-line>
38*    pidns30
39*
40*   History:
41*    DATE      NAME                             DESCRIPTION
42*    01/12/08  Nadia Derbey               Creation of this test.
43*              <Nadia.Derbey@bull.net>
44*
45******************************************************************************/
46#define _GNU_SOURCE 1
47#include <sys/wait.h>
48#include <sys/types.h>
49#include <signal.h>
50#include <stdlib.h>
51#include <unistd.h>
52#include <stdio.h>
53#include <mqueue.h>
54#include <usctest.h>
55#include <test.h>
56#include <libclone.h>
57
58char *TCID = "pidns30";
59int TST_TOTAL = 1;
60
61char *mqname = "/mq1";
62int result = TFAIL;
63
64int errno;
65int father_to_child[2];
66int child_to_father[2];
67
68#define CHILD_PID       1
69#define PARENT_PID      0
70
71#define MSG      "HOW ARE YOU"
72#define MSG_PRIO 1
73
74#define NO_STEP	-1
75#define F_STEP_0 0x00
76#define F_STEP_1 0x01
77#define F_STEP_2 0x02
78#define F_STEP_3 0x03
79#define C_STEP_0 0x10
80#define C_STEP_1 0x11
81#define C_STEP_2 0x12
82
83static void remove_pipe(int *fd)
84{
85	close(fd[0]);
86	close(fd[1]);
87}
88
89static void remove_mqueue(mqd_t mqd)
90{
91	mq_close(mqd);
92	mq_unlink(mqname);
93}
94
95/*
96 * steps F_STEP_XX : called from main
97 * steps C_STEP_XX : called from child_fn
98 */
99static void cleanup_resources(int step, mqd_t mqd)
100{
101	switch (step) {
102	case C_STEP_2:
103		close(child_to_father[1]);
104		close(father_to_child[0]);
105		mq_close(mqd);
106		break;
107
108	case C_STEP_1:
109		mq_notify(mqd, NULL);
110		/* fall through */
111	case C_STEP_0:
112		mq_close(mqd);
113		break;
114
115	case F_STEP_3:
116		remove_mqueue(mqd);
117		close(child_to_father[0]);
118		close(father_to_child[1]);
119		break;
120
121	case F_STEP_2:
122		remove_mqueue(mqd);
123		/* fall through */
124	case F_STEP_1:
125		remove_pipe(father_to_child);
126		/* fall through */
127	case F_STEP_0:
128		remove_pipe(child_to_father);
129		break;
130	default:
131		tst_resm(TWARN, "Unknown code - no resource removed.");
132		break;
133	}
134}
135
136/*
137 * cleanup() - performs all ONE TIME cleanup for this test at
138 *             completion or premature exit.
139 */
140static void cleanup(int result, int step, mqd_t mqd)
141{
142	if (step != NO_STEP)
143		cleanup_resources(step, mqd);
144
145	/* Clean the test testcase as LTP wants*/
146	TEST_CLEANUP;
147
148	/* exit with return code appropriate for results */
149	tst_exit(result);
150}
151
152/*
153 * child_signal_handler() - to handle SIGUSR1
154 */
155static void child_signal_handler(int sig, siginfo_t *si, void *unused)
156{
157	char buf[256];
158	mqd_t rc;
159	struct mq_attr attr;
160
161	if (si->si_signo != SIGUSR1) {
162		tst_resm(TBROK, "cinit: received %s unexpectedly",
163			strsignal(si->si_signo));
164		return;
165	}
166
167	if (si->si_code != SI_MESGQ) {
168		tst_resm(TBROK, "cinit: expected signal code SI_MESGQ - Got %d",
169			si->si_code);
170		return;
171	}
172
173	if (si->si_pid) {
174		tst_resm(TFAIL,
175			"cinit: expected signal originator PID = 0 - Got %d",
176			si->si_pid);
177		return;
178	}
179
180	tst_resm(TPASS, "cinit: signal originator PID = 0");
181	result = TPASS;
182
183	/*
184	 * Now read the message - Be silent on errors since this is not the
185	 * test purpose.
186	 */
187	rc = mq_getattr((mqd_t)si->si_int, &attr);
188	if (rc == (mqd_t)-1)
189		return;
190
191	mq_receive((mqd_t)si->si_int, buf, attr.mq_msgsize, NULL);
192}
193
194
195/*
196 * child_fn() - Inside container
197 */
198int child_fn(void *arg)
199{
200	pid_t pid, ppid;
201	struct sigaction sa;
202	mqd_t mqd;
203	struct sigevent notif;
204	char buf[5];
205
206	/* Set process id and parent pid */
207	pid = getpid();
208	ppid = getppid();
209
210	if (pid != CHILD_PID || ppid != PARENT_PID) {
211		tst_resm(TBROK, "cinit: pidns is not created");
212		cleanup(TBROK, NO_STEP, 0);
213	}
214
215	/* Close the appropriate end of each pipe */
216	close(child_to_father[0]);
217	close(father_to_child[1]);
218
219	mqd = mq_open(mqname, O_RDONLY);
220	if (mqd == (mqd_t)-1) {
221		tst_resm(TBROK, "cinit: mq_open() failed (%s)",
222			strerror(errno));
223		cleanup(TBROK, NO_STEP, 0);
224	}
225	tst_resm(TINFO, "cinit: mq_open succeeded");
226
227	/* Register for notification on message arrival */
228	notif.sigev_notify = SIGEV_SIGNAL;
229	notif.sigev_signo = SIGUSR1;
230	notif.sigev_value.sival_int = mqd;
231	if (mq_notify(mqd, &notif) == (mqd_t)-1) {
232		tst_resm(TBROK, "cinit: mq_notify() failed (%s)",
233			strerror(errno));
234		cleanup(TBROK, C_STEP_0, mqd);
235	}
236	tst_resm(TINFO, "cinit: successfully registered for notification");
237
238	/* Define handler for SIGUSR1 */
239	sa.sa_flags = SA_SIGINFO;
240	sigemptyset(&sa.sa_mask);
241	sa.sa_sigaction = child_signal_handler;
242	if (sigaction(SIGUSR1, &sa, NULL) == -1) {
243		tst_resm(TBROK, "cinit: sigaction() failed(%s)",
244			strerror(errno));
245		cleanup(TBROK, C_STEP_1, mqd);
246	}
247	tst_resm(TINFO, "cinit: successfully registered handler for SIGUSR1");
248
249	/* Ask parent to send a message to the mqueue */
250	if (write(child_to_father[1], "c:ok", 5) != 5) {
251		tst_resm(TBROK, "cinit: pipe is broken (%s)", strerror(errno));
252		cleanup(TBROK, C_STEP_1, mqd);
253	}
254
255	sleep(3);
256
257	/* Has parent sent a message? */
258	read(father_to_child[0], buf, 5);
259	if (strcmp(buf, "f:ok")) {
260		tst_resm(TBROK, "cinit: parent did not send the message!");
261		cleanup(TBROK, C_STEP_1, mqd);
262	}
263	tst_resm(TINFO, "cinit: my father is done - cleaning");
264
265	/* Be silent on errors from now on */
266	cleanup_resources(C_STEP_2, mqd);
267
268	exit(0);
269}
270
271/***********************************************************************
272*   M A I N
273***********************************************************************/
274
275int main(int argc, char *argv[])
276{
277	int status;
278	char buf[5];
279	pid_t cpid;
280	mqd_t mqd;
281	mqd_t rc;
282
283	if (pipe(child_to_father) == -1) {
284		tst_resm(TBROK, "parent: pipe() failed. aborting!");
285		cleanup(TBROK, NO_STEP, 0);
286	}
287
288	if (pipe(father_to_child) == -1) {
289		tst_resm(TBROK, "parent: pipe() failed. aborting!");
290		cleanup(TBROK, F_STEP_0, 0);
291	}
292
293	mq_unlink(mqname);
294	mqd = mq_open(mqname, O_RDWR|O_CREAT|O_EXCL, 0777, NULL);
295	if (mqd == (mqd_t)-1) {
296		tst_resm(TBROK, "parent: mq_open() failed (%s)",
297			strerror(errno));
298		cleanup(TBROK, F_STEP_1, 0);
299	}
300	tst_resm(TINFO, "parent: successfully created posix mqueue");
301
302	/* container creation on PID namespace */
303	cpid = do_clone(CLONE_NEWPID|SIGCHLD, child_fn, NULL);
304	if (cpid < 0) {
305		tst_resm(TBROK, "parent: clone() failed(%s)", strerror(errno));
306		cleanup(TBROK, F_STEP_2, mqd);
307	}
308
309	/* Close the appropriate end of each pipe */
310	close(child_to_father[1]);
311	close(father_to_child[0]);
312
313	/* Is container ready */
314	read(child_to_father[0], buf, 5);
315	if (strcmp(buf, "c:ok")) {
316		tst_resm(TBROK, "parent: container did not respond!");
317		cleanup(TBROK, F_STEP_2, mqd);
318	}
319
320	rc = mq_send(mqd, MSG, strlen(MSG), MSG_PRIO);
321	if (rc == (mqd_t)-1) {
322		tst_resm(TBROK, "parent: mq_send() failed (%s)",
323			strerror(errno));
324		cleanup(TBROK, F_STEP_2, mqd);
325	}
326	tst_resm(TINFO, "parent: mq_send() succeeded");
327
328	/* Tell the child the message has been sent */
329	if (write(father_to_child[1], "f:ok", 5) != 5) {
330		tst_resm(TBROK, "father: pipe is broken(%s)", strerror(errno));
331		cleanup(TBROK, F_STEP_2, mqd);
332	}
333
334	/* Wait for child to finish */
335	if (wait(&status) == -1) {
336		tst_resm(TBROK, "parent: wait() failed(%s)", strerror(errno));
337		cleanup(TBROK, F_STEP_2, mqd);
338	}
339
340	cleanup(result, F_STEP_3, mqd);
341
342	/* NOT REACHED */
343	return 0;
344}    /* End main */
345