1/*
2 * Copyright (c) International Business Machines  Corp., 2002
3 *
4 * This program is free software;  you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY;  without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12 * the GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program;  if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * 06/30/2001   Port to Linux   nsharoff@us.ibm.com
19 * 11/11/2002   Port to LTP     dbarrera@us.ibm.com
20 */
21
22/*
23 * Get and manipulate a message queue.
24 */
25
26#define _XOPEN_SOURCE 500
27#include <sys/stat.h>
28#include <sys/types.h>
29#include <sys/ipc.h>
30#include <sys/msg.h>
31#include <sys/wait.h>
32#include <signal.h>
33#include <errno.h>
34#include <stdio.h>
35#include <string.h>
36#include <stdlib.h>
37#include <unistd.h>
38#include "test.h"
39#include "ipcmsg.h"
40#include "../lib/libmsgctl.h"
41
42char *TCID = "msgctl09";
43int TST_TOTAL = 1;
44
45#define MAXNREPS	1000
46#ifndef CONFIG_COLDFIRE
47#define MAXNPROCS	 1000000	/* This value is set to an arbitrary high limit. */
48#else
49#define MAXNPROCS	 100000	/* Coldfire can't deal with 1000000 */
50#endif
51#define MAXNKIDS	10
52
53static key_t keyarray[MAXNPROCS];
54static int pidarray[MAXNPROCS];
55static int rkidarray[MAXNKIDS];
56static int wkidarray[MAXNKIDS];
57static int tid;
58static int nprocs, nreps, nkids, MSGMNI;
59static int procstat;
60
61void setup(void);
62void cleanup(void);
63
64static void term(int);
65static int dotest(key_t, int);
66static void cleanup_msgqueue(int i, int tid);
67
68static char *opt_nprocs;
69static char *opt_nkids;
70static char *opt_nreps;
71
72static option_t options[] = {
73	{"n:", NULL, &opt_nprocs},
74	{"c:", NULL, &opt_nkids},
75	{"l:", NULL, &opt_nreps},
76	{NULL, NULL, NULL},
77};
78
79static void usage(void)
80{
81	printf("  -n      Number of processes\n");
82	printf("  -c      Number of read/write child pairs\n");
83	printf("  -l      Number of iterations\n");
84}
85
86int main(int argc, char **argv)
87{
88	int i, j, ok, pid;
89	int count, status;
90
91	tst_parse_opts(argc, argv, options, usage);
92
93	setup();
94
95	nreps = MAXNREPS;
96	nprocs = MSGMNI;
97	nkids = MAXNKIDS;
98
99	if (opt_nreps) {
100		nreps = atoi(opt_nreps);
101		if (nreps > MAXNREPS) {
102			tst_resm(TINFO,
103				 "Requested number of iterations too large, "
104				 "setting to Max. of %d", MAXNREPS);
105			nreps = MAXNREPS;
106		}
107	}
108
109	if (opt_nprocs) {
110		nprocs = atoi(opt_nprocs);
111		if (nprocs > MSGMNI) {
112			tst_resm(TINFO,
113				 "Requested number of processes too large, "
114				 "setting to Max. of %d", MSGMNI);
115			nprocs = MSGMNI;
116		}
117	}
118
119	if (opt_nkids) {
120		nkids = atoi(opt_nkids);
121		if (nkids > MAXNKIDS) {
122			tst_resm(TINFO,
123				 "Requested number of read/write pairs too "
124				 "large, setting to Max. of %d", MAXNKIDS);
125			nkids = MAXNKIDS;
126		}
127	}
128
129	procstat = 0;
130	srand48((unsigned)getpid() + (unsigned)(getppid() << 16));
131	tid = -1;
132
133	/* Setup signal handleing routine */
134	if (sigset(SIGTERM, term) == SIG_ERR) {
135		tst_brkm(TFAIL, NULL, "Sigset SIGTERM failed");
136	}
137	/* Set up array of unique keys for use in allocating message
138	 * queues
139	 */
140	for (i = 0; i < nprocs; i++) {
141		ok = 1;
142		do {
143			/* Get random key */
144			keyarray[i] = (key_t) lrand48();
145			/* Make sure key is unique and not private */
146			if (keyarray[i] == IPC_PRIVATE) {
147				ok = 0;
148				continue;
149			}
150			for (j = 0; j < i; j++) {
151				if (keyarray[j] == keyarray[i]) {
152					ok = 0;
153					break;
154				}
155				ok = 1;
156			}
157		} while (ok == 0);
158	}
159	/* Fork a number of processes (nprocs), each of which will
160	 * create a message queue with several (nkids) reader/writer
161	 * pairs which will read and write a number (iterations)
162	 * of random length messages with specific values (keys).
163	 */
164
165	for (i = 0; i < nprocs; i++) {
166		fflush(stdout);
167		if ((pid = FORK_OR_VFORK()) < 0) {
168			tst_brkm(TFAIL,
169				 NULL,
170				 "\tFork failed (may be OK if under stress)");
171		}
172		/* Child does this */
173		if (pid == 0) {
174			procstat = 1;
175			exit(dotest(keyarray[i], i));
176		}
177		pidarray[i] = pid;
178	}
179
180	count = 0;
181	while (1) {
182		if ((wait(&status)) > 0) {
183			if (status >> 8 != PASS) {
184				tst_brkm(TFAIL, NULL,
185					 "Child exit status = %d",
186					 status >> 8);
187			}
188			count++;
189		} else {
190			if (errno != EINTR) {
191				break;
192			}
193#ifdef DEBUG
194			tst_resm(TINFO, "Signal detected during wait");
195#endif
196		}
197	}
198	/* Make sure proper number of children exited */
199	if (count != nprocs) {
200		tst_brkm(TFAIL,
201			 NULL,
202			 "Wrong number of children exited, Saw %d, Expected %d",
203			 count, nprocs);
204	}
205
206	tst_resm(TPASS, "msgctl09 ran successfully!");
207
208	cleanup();
209	tst_exit();
210}
211
212static void cleanup_msgqueue(int i, int tid)
213{
214	/*
215	 * Decrease the value of i by 1 because it
216	 * is getting incremented even if the fork
217	 * is failing.
218	 */
219
220	i--;
221	/*
222	 * Kill all children & free message queue.
223	 */
224	for (; i >= 0; i--) {
225		(void)kill(rkidarray[i], SIGKILL);
226		(void)kill(wkidarray[i], SIGKILL);
227	}
228
229	if (msgctl(tid, IPC_RMID, 0) < 0) {
230		tst_brkm(TFAIL | TERRNO, NULL, "Msgctl error in cleanup");
231	}
232}
233
234static int dotest(key_t key, int child_process)
235{
236	int id, pid;
237	int i, count, status, exit_status;
238
239	sighold(SIGTERM);
240	if ((id = msgget(key, IPC_CREAT | S_IRUSR | S_IWUSR)) < 0) {
241		printf("msgget() error in child %d: %s\n",
242			child_process, strerror(errno));
243		return FAIL;
244	}
245	tid = id;
246	sigrelse(SIGTERM);
247
248	exit_status = PASS;
249
250	for (i = 0; i < nkids; i++) {
251		fflush(stdout);
252		if ((pid = FORK_OR_VFORK()) < 0) {
253			printf("Fork failure in the first child of child group %d\n",
254				child_process);
255			cleanup_msgqueue(i, tid);
256			return FAIL;
257		}
258		/* First child does this */
259		if (pid == 0) {
260			procstat = 2;
261			exit(doreader(key, tid, getpid(),
262					child_process, nreps));
263		}
264		rkidarray[i] = pid;
265		fflush(stdout);
266		if ((pid = FORK_OR_VFORK()) < 0) {
267			printf("Fork failure in the second child of child group %d\n",
268				child_process);
269			/*
270			 * Kill the reader child process
271			 */
272			(void)kill(rkidarray[i], SIGKILL);
273
274			cleanup_msgqueue(i, tid);
275			return FAIL;
276		}
277		/* Second child does this */
278		if (pid == 0) {
279			procstat = 2;
280			exit(dowriter(key, tid, rkidarray[i],
281					child_process, nreps));
282		}
283		wkidarray[i] = pid;
284	}
285	/* Parent does this */
286	count = 0;
287	while (1) {
288		if ((wait(&status)) > 0) {
289			if (status >> 8 != PASS) {
290				printf("Child exit status = %d from child group %d\n",
291					status >> 8, child_process);
292				for (i = 0; i < nkids; i++) {
293					kill(rkidarray[i], SIGTERM);
294					kill(wkidarray[i], SIGTERM);
295				}
296				if (msgctl(tid, IPC_RMID, 0) < 0) {
297					printf("msgctl() error: %s\n",
298						strerror(errno));
299				}
300				return FAIL;
301			}
302			count++;
303		} else {
304			if (errno != EINTR) {
305				break;
306			}
307		}
308	}
309	/* Make sure proper number of children exited */
310	if (count != (nkids * 2)) {
311		printf("Wrong number of children exited in child group %d, saw %d, expected %d\n",
312			child_process, count, (nkids * 2));
313		if (msgctl(tid, IPC_RMID, 0) < 0) {
314			printf("msgctl() error: %s\n", strerror(errno));
315		}
316		return FAIL;
317	}
318	if (msgctl(id, IPC_RMID, 0) < 0) {
319		printf("msgctl() failure in child group %d: %s\n",
320			child_process, strerror(errno));
321		return FAIL;
322	}
323	return exit_status;
324}
325
326static void term(int sig LTP_ATTRIBUTE_UNUSED)
327{
328	int i;
329
330	if (procstat == 0) {
331#ifdef DEBUG
332		tst_resm(TINFO, "SIGTERM signal received, test killing kids");
333#endif
334		for (i = 0; i < nprocs; i++) {
335			if (pidarray[i] > 0) {
336				if (kill(pidarray[i], SIGTERM) < 0) {
337					printf("Kill failed to kill child %d",
338						i);
339					exit(FAIL);
340				}
341			}
342		}
343		return;
344	}
345
346	if (procstat == 2) {
347		fflush(stdout);
348		exit(PASS);
349	}
350
351	if (tid == -1) {
352		exit(FAIL);
353	}
354	for (i = 0; i < nkids; i++) {
355		if (rkidarray[i] > 0)
356			kill(rkidarray[i], SIGTERM);
357		if (wkidarray[i] > 0)
358			kill(wkidarray[i], SIGTERM);
359	}
360}
361
362void setup(void)
363{
364	int nr_msgqs;
365
366	tst_tmpdir();
367
368	tst_sig(FORK, DEF_HANDLER, cleanup);
369
370	TEST_PAUSE;
371
372	nr_msgqs = get_max_msgqueues();
373	if (nr_msgqs < 0)
374		cleanup();
375
376	nr_msgqs -= get_used_msgqueues();
377	if (nr_msgqs <= 0) {
378		tst_resm(TBROK,
379			 "Max number of message queues already used, cannot create more.");
380		cleanup();
381	}
382
383	/*
384	 * Since msgmni scales to the memory size, it may reach huge values
385	 * that are not necessary for this test.
386	 * That's why we define NR_MSGQUEUES as a high boundary for it.
387	 */
388	MSGMNI = min(nr_msgqs, NR_MSGQUEUES);
389}
390
391void cleanup(void)
392{
393	int status;
394
395#ifdef DEBUG
396	tst_resm(TINFO, "Removing the message queue");
397#endif
398	fflush(stdout);
399	(void)msgctl(tid, IPC_RMID, NULL);
400	if ((status = msgctl(tid, IPC_STAT, NULL)) != -1) {
401		(void)msgctl(tid, IPC_RMID, NULL);
402		tst_resm(TFAIL, "msgctl(tid, IPC_RMID) failed");
403
404	}
405
406	fflush(stdout);
407	tst_rmdir();
408}
409