pidns30.c revision 2c28215423293e443469a07ae7011135d058b671
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 "linux_syscall_numbers.h"
57#include "libclone.h"
58
59char *TCID = "pidns30";
60int TST_TOTAL = 1;
61
62char *mqname = "mq1";
63int result = TFAIL;
64
65int errno;
66int father_to_child[2];
67int child_to_father[2];
68
69#define CHILD_PID       1
70#define PARENT_PID      0
71
72#define MSG      "HOW ARE YOU"
73#define MSG_PRIO 1
74
75#define NO_STEP	-1
76#define F_STEP_0 0x00
77#define F_STEP_1 0x01
78#define F_STEP_2 0x02
79#define F_STEP_3 0x03
80#define C_STEP_0 0x10
81#define C_STEP_1 0x11
82#define C_STEP_2 0x12
83
84static void remove_pipe(int *fd)
85{
86	close(fd[0]);
87	close(fd[1]);
88}
89
90static void remove_mqueue(mqd_t mqd)
91{
92	mq_close(mqd);
93	syscall(__NR_mq_unlink, mqname);
94}
95
96/*
97 * steps F_STEP_XX : called from main
98 * steps C_STEP_XX : called from child_fn
99 */
100static void cleanup_resources(int step, mqd_t mqd)
101{
102	switch (step) {
103	case C_STEP_2:
104		close(child_to_father[1]);
105		close(father_to_child[0]);
106		mq_close(mqd);
107		break;
108
109	case C_STEP_1:
110		syscall(__NR_mq_notify, mqd, NULL);
111		/* fall through */
112	case C_STEP_0:
113		mq_close(mqd);
114		break;
115
116	case F_STEP_3:
117		remove_mqueue(mqd);
118		close(child_to_father[0]);
119		close(father_to_child[1]);
120		break;
121
122	case F_STEP_2:
123		remove_mqueue(mqd);
124		/* fall through */
125	case F_STEP_1:
126		remove_pipe(father_to_child);
127		/* fall through */
128	case F_STEP_0:
129		remove_pipe(child_to_father);
130		break;
131	default:
132		tst_resm(TWARN, "Unknown code - no resource removed.");
133		break;
134	}
135}
136
137/*
138 * cleanup_mqueue() - performs all ONE TIME cleanup for this test at
139 *             	      completion or premature exit.
140 */
141static void cleanup_mqueue(int result, int step, mqd_t mqd)
142{
143	if (step != NO_STEP)
144		cleanup_resources(step, mqd);
145
146	/* Clean the test testcase as LTP wants*/
147	TEST_CLEANUP;
148
149	tst_exit();
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 * child_fn() - Inside container
196 */
197int child_fn(void *arg)
198{
199	pid_t pid, ppid;
200	struct sigaction sa;
201	mqd_t mqd;
202	struct sigevent notif;
203	char buf[5];
204
205	/* Set process id and parent pid */
206	pid = getpid();
207	ppid = getppid();
208
209	if (pid != CHILD_PID || ppid != PARENT_PID) {
210		tst_resm(TBROK, "cinit: pidns is not created");
211		cleanup_mqueue(TBROK, NO_STEP, 0);
212	}
213
214	/* Close the appropriate end of each pipe */
215	close(child_to_father[0]);
216	close(father_to_child[1]);
217
218	mqd = syscall(__NR_mq_open, mqname, O_RDONLY);
219	if (mqd == (mqd_t)-1) {
220		tst_resm(TBROK, "cinit: mq_open() failed (%s)",
221			strerror(errno));
222		cleanup_mqueue(TBROK, NO_STEP, 0);
223	}
224	tst_resm(TINFO, "cinit: mq_open succeeded");
225
226	/* Register for notification on message arrival */
227	notif.sigev_notify = SIGEV_SIGNAL;
228	notif.sigev_signo = SIGUSR1;
229	notif.sigev_value.sival_int = mqd;
230	if (syscall(__NR_mq_notify, mqd, &notif) == (mqd_t)-1) {
231		tst_resm(TBROK, "cinit: mq_notify() failed (%s)",
232			strerror(errno));
233		cleanup_mqueue(TBROK, C_STEP_0, mqd);
234	}
235	tst_resm(TINFO, "cinit: successfully registered for notification");
236
237	/* Define handler for SIGUSR1 */
238	sa.sa_flags = SA_SIGINFO;
239	sigemptyset(&sa.sa_mask);
240	sa.sa_sigaction = child_signal_handler;
241	if (sigaction(SIGUSR1, &sa, NULL) == -1) {
242		tst_resm(TBROK, "cinit: sigaction() failed(%s)",
243			strerror(errno));
244		cleanup_mqueue(TBROK, C_STEP_1, mqd);
245	}
246	tst_resm(TINFO, "cinit: successfully registered handler for SIGUSR1");
247
248	/* Ask parent to send a message to the mqueue */
249	if (write(child_to_father[1], "c:ok", 5) != 5) {
250		tst_resm(TBROK, "cinit: pipe is broken (%s)", strerror(errno));
251		cleanup_mqueue(TBROK, C_STEP_1, mqd);
252	}
253
254	sleep(3);
255
256	/* Has parent sent a message? */
257	read(father_to_child[0], buf, 5);
258	if (strcmp(buf, "f:ok")) {
259		tst_resm(TBROK, "cinit: parent did not send the message!");
260		cleanup_mqueue(TBROK, C_STEP_1, mqd);
261	}
262	tst_resm(TINFO, "cinit: my father is done - cleaning");
263
264	/* Be silent on errors from now on */
265	cleanup_resources(C_STEP_2, mqd);
266
267	exit(0);
268}
269
270/***********************************************************************
271*   M A I N
272***********************************************************************/
273
274int main(int argc, char *argv[])
275{
276	int status;
277	char buf[5];
278	pid_t cpid;
279	mqd_t mqd;
280	mqd_t rc;
281
282	if (pipe(child_to_father) == -1) {
283		tst_resm(TBROK, "parent: pipe() failed. aborting!");
284		cleanup_mqueue(TBROK, NO_STEP, 0);
285	}
286
287	if (pipe(father_to_child) == -1) {
288		tst_resm(TBROK, "parent: pipe() failed. aborting!");
289		cleanup_mqueue(TBROK, F_STEP_0, 0);
290	}
291
292	syscall(__NR_mq_unlink, mqname);
293	mqd = syscall(__NR_mq_open, mqname, O_RDWR|O_CREAT|O_EXCL, 0777, NULL);
294	if (mqd == (mqd_t)-1) {
295		tst_resm(TBROK, "parent: mq_open() failed (%s)",
296			strerror(errno));
297		cleanup_mqueue(TBROK, F_STEP_1, 0);
298	}
299	tst_resm(TINFO, "parent: successfully created posix mqueue");
300
301	/* container creation on PID namespace */
302	cpid = ltp_clone_quick(CLONE_NEWPID|SIGCHLD, child_fn, NULL);
303	if (cpid < 0) {
304		tst_resm(TBROK, "parent: clone() failed(%s)", strerror(errno));
305		cleanup_mqueue(TBROK, F_STEP_2, mqd);
306	}
307
308	/* Close the appropriate end of each pipe */
309	close(child_to_father[1]);
310	close(father_to_child[0]);
311
312	/* Is container ready */
313	read(child_to_father[0], buf, 5);
314	if (strcmp(buf, "c:ok")) {
315		tst_resm(TBROK, "parent: container did not respond!");
316		cleanup_mqueue(TBROK, F_STEP_2, mqd);
317	}
318
319	rc = mq_send(mqd, MSG, strlen(MSG), MSG_PRIO);
320	if (rc == (mqd_t)-1) {
321		tst_resm(TBROK, "parent: mq_send() failed (%s)",
322			strerror(errno));
323		cleanup_mqueue(TBROK, F_STEP_2, mqd);
324	}
325	tst_resm(TINFO, "parent: mq_send() succeeded");
326
327	/* Tell the child the message has been sent */
328	if (write(father_to_child[1], "f:ok", 5) != 5) {
329		tst_resm(TBROK, "father: pipe is broken(%s)", strerror(errno));
330		cleanup_mqueue(TBROK, F_STEP_2, mqd);
331	}
332
333	/* Wait for child to finish */
334	if (wait(&status) == -1) {
335		tst_resm(TBROK, "parent: wait() failed(%s)", strerror(errno));
336		cleanup_mqueue(TBROK, F_STEP_2, mqd);
337	}
338
339	cleanup_mqueue(result, F_STEP_3, mqd);
340
341	tst_exit();
342}