1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2001
4 *
5 *   This program is free software;  you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation; either version 2 of the License, or
8 *   (at your option) any later version.
9 *
10 *   This program is distributed in the hope that it will be useful,
11 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13 *   the GNU General Public License for more details.
14 *
15 *   You should have received a copy of the GNU General Public License
16 *   along with this program;  if not, write to the Free Software
17 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/*
21 * NAME
22 *	msgrcv05.c
23 *
24 * DESCRIPTION
25 *	msgrcv05 - test for EINTR error
26 *
27 * ALGORITHM
28 *	create a message queue with read/write permissions
29 *	loop if that option was specified
30 *	fork a child who attempts to read a non-existent message with msgrcv()
31 *	parent sends a SIGHUP to the child, then waits for the child to complete
32 *	check the errno value
33 *	  issue a PASS message if we get EINTR
34 *	otherwise, the tests fails
35 *	  issue a FAIL message
36 *	child exits, parent calls cleanup
37 *
38 * USAGE:  <for command-line>
39 *  msgrcv05 [-c n] [-e] [-i n] [-I x] [-P x] [-t]
40 *     where,  -c n : Run n copies concurrently.
41 *             -e   : Turn on errno logging.
42 *	       -i n : Execute test n times.
43 *	       -I x : Execute test for x seconds.
44 *	       -P x : Pause for x seconds between iterations.
45 *	       -t   : Turn on syscall timing.
46 *
47 * HISTORY
48 *	03/2001 - Written by Wayne Boyer
49 *      14/03/2008 Matthieu Fertré (Matthieu.Fertre@irisa.fr)
50 *      - Fix concurrency issue. Due to the use of usleep function to
51 *        synchronize processes, synchronization issues can occur on a loaded
52 *        system. Fix this by using pipes to synchronize processes.
53 *
54 * RESTRICTIONS
55 *	none
56 */
57
58#include "test.h"
59#include "safe_macros.h"
60
61#include "ipcmsg.h"
62
63#include <sys/types.h>
64#include <sys/wait.h>
65
66void do_child(void);
67void cleanup(void);
68void setup(void);
69#ifdef UCLINUX
70#define PIPE_NAME	"msgrcv05"
71void do_child_uclinux(void);
72#endif
73
74char *TCID = "msgrcv05";
75int TST_TOTAL = 1;
76
77int msg_q_1 = -1;		/* The message queue id created in setup */
78
79MSGBUF rcv_buf;
80pid_t c_pid;
81
82int main(int ac, char **av)
83{
84	int lc;
85
86	tst_parse_opts(ac, av, NULL, NULL);
87
88#ifdef UCLINUX
89	maybe_run_child(&do_child_uclinux, "d", &msg_q_1);
90#endif
91
92	setup();		/* global setup */
93
94	for (lc = 0; TEST_LOOPING(lc); lc++) {
95		tst_count = 0;
96
97		/*
98		 * fork a child that will attempt to read a non-existent
99		 * message from the queue
100		 */
101		if ((c_pid = FORK_OR_VFORK()) == -1)
102			tst_brkm(TBROK, cleanup, "could not fork");
103
104		if (c_pid == 0) {
105			/*
106			 * Attempt to read a message without IPC_NOWAIT.
107			 * With no message to read, the child sleeps.
108			 */
109
110#ifdef UCLINUX
111			if (self_exec(av[0], "d", msg_q_1) < 0)
112				tst_brkm(TBROK, cleanup, "could not self_exec");
113#else
114			do_child();
115#endif
116		} else {
117			TST_PROCESS_STATE_WAIT(cleanup, c_pid, 'S');
118
119			/* send a signal that must be caught to the child */
120			SAFE_KILL(cleanup, c_pid, SIGHUP);
121
122			waitpid(c_pid, NULL, 0);
123		}
124	}
125
126	cleanup();
127
128	tst_exit();
129}
130
131void do_child(void)
132{
133	TEST(msgrcv(msg_q_1, &rcv_buf, MSGSIZE, 1, 0));
134
135	if (TEST_RETURN != -1)
136		tst_brkm(TFAIL, NULL, "call succeeded unexpectedly");
137
138	switch (TEST_ERRNO) {
139	case EINTR:
140		tst_resm(TPASS, "got EINTR as expected");
141		break;
142	default:
143		tst_resm(TFAIL | TTERRNO,
144			 "call failed with an unexpected error");
145		break;
146	}
147
148	exit(0);
149}
150
151void sighandler(int sig)
152{
153	if (sig == SIGHUP)
154		return;
155	else
156		tst_brkm(TBROK, NULL, "unexpected signal %d received", sig);
157}
158
159#ifdef UCLINUX
160/*
161 * do_child_uclinux() - capture signals again, then run do_child()
162 */
163void do_child_uclinux(void)
164{
165	tst_sig(FORK, sighandler, cleanup);
166
167	do_child();
168}
169#endif
170
171/*
172 * setup() - performs all the ONE TIME setup for this test.
173 */
174void setup(void)
175{
176
177	tst_sig(FORK, sighandler, cleanup);
178
179	TEST_PAUSE;
180
181	/*
182	 * Create a temporary directory and cd into it.
183	 * This helps to ensure that a unique msgkey is created.
184	 * See ../lib/libipc.c for more information.
185	 */
186	tst_tmpdir();
187
188	msgkey = getipckey();
189
190	/* create a message queue with read/write permission */
191	if ((msg_q_1 = msgget(msgkey, IPC_CREAT | IPC_EXCL | MSG_RW)) == -1)
192		tst_brkm(TBROK, cleanup, "Can't create message queue");
193}
194
195/*
196 * cleanup() - performs all the ONE TIME cleanup for this test at completion
197 * 	       or premature exit.
198 */
199void cleanup(void)
200{
201	/* if it exists, remove the message queue that was created */
202	rm_queue(msg_q_1);
203
204	tst_rmdir();
205
206}
207