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  * Same as msgctl09 but gets the actual msgmni value under procfs.
25  */
26 
27 #define _XOPEN_SOURCE 500
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/ipc.h>
31 #include <sys/msg.h>
32 #include <sys/wait.h>
33 #include <signal.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include "test.h"
40 #include "ipcmsg.h"
41 #include "../lib/libmsgctl.h"
42 
43 char *TCID = "msgctl11";
44 int TST_TOTAL = 1;
45 
46 #define MAXNREPS	1000
47 #ifndef CONFIG_COLDFIRE
48 #define MAXNPROCS	 1000000	/* This value is set to an arbitrary high limit. */
49 #else
50 #define MAXNPROCS	 100000	/* Coldfire can't deal with 1000000 */
51 #endif
52 #define MAXNKIDS	10
53 #define DEFNKIDS	2
54 
55 static int maxnkids = MAXNKIDS;	/* Used if pid_max is exceeded */
56 static key_t keyarray[MAXNPROCS];
57 static int pidarray[MAXNPROCS];
58 static int rkidarray[MAXNKIDS];
59 static int wkidarray[MAXNKIDS];
60 static int tid;
61 static int nprocs, nreps, nkids, MSGMNI;
62 static int maxnprocs;
63 static int procstat;
64 
65 void setup(void);
66 void cleanup(void);
67 
68 static void term(int);
69 static int dotest(key_t, int);
70 static void dotest_iteration(int off);
71 static void cleanup_msgqueue(int i, int tid);
72 
73 #ifdef UCLINUX
74 static char *argv0;
75 static key_t key_uclinux;
76 static int i_uclinux;
77 static int pid_uclinux;
78 static int child_process_uclinux;
79 static int rkid_uclinux;
80 
81 static void do_child_1_uclinux(void);
82 static void do_child_2_uclinux(void);
83 static void do_child_3_uclinux(void);
84 #endif
85 
main(int argc,char ** argv)86 int main(int argc, char **argv)
87 {
88 	int i, j, ok;
89 
90 #ifdef UCLINUX
91 
92 	argv0 = argv[0];
93 
94 	tst_parse_opts(argc, argv, NULL, NULL);
95 
96 	maybe_run_child(&do_child_1_uclinux, "ndd", 1, &key_uclinux,
97 			&i_uclinux);
98 	maybe_run_child(&do_child_2_uclinux, "nddd", 2, &key_uclinux,
99 			&pid_uclinux, &child_process_uclinux);
100 	maybe_run_child(&do_child_3_uclinux, "nddd", 3, &key_uclinux,
101 			&rkid_uclinux, &child_process_uclinux);
102 #endif
103 
104 	setup();
105 
106 	if (argc == 1) {
107 		/* Set default parameters */
108 		nreps = MAXNREPS;
109 		nkids = DEFNKIDS;
110 	} else if (argc == 4) {
111 		if (atoi(argv[1]) > MAXNREPS) {
112 			tst_resm(TINFO,
113 				 "Requested number of iterations too large, setting to Max. of %d",
114 				 MAXNREPS);
115 			nreps = MAXNREPS;
116 		} else {
117 			nreps = atoi(argv[1]);
118 		}
119 		if (atoi(argv[2]) > maxnprocs) {
120 			tst_resm(TINFO,
121 				 "Requested number of processes too large, setting to Max. of %d",
122 				 maxnprocs);
123 		} else {
124 			maxnprocs = atoi(argv[2]);
125 		}
126 		if (atoi(argv[3]) > maxnkids) {
127 			tst_resm(TINFO,
128 				 "Requested number of read/write pairs too large; setting to Max. of %d",
129 				 maxnkids);
130 			nkids = maxnkids;
131 		} else {
132 			nkids = atoi(argv[3]);
133 		}
134 	} else {
135 		tst_brkm(TCONF, cleanup,
136 			 " Usage: %s [ number of iterations  number of processes number of read/write pairs ]",
137 			 argv[0]);
138 	}
139 
140 	procstat = 0;
141 	srand48((unsigned)getpid() + (unsigned)(getppid() << 16));
142 	tid = -1;
143 
144 	/* Setup signal handling routine */
145 	if (sigset(SIGTERM, term) == SIG_ERR)
146 		tst_brkm(TFAIL, cleanup, "Sigset SIGTERM failed");
147 
148 	/* Set up array of unique keys for use in allocating message
149 	 * queues
150 	 */
151 	for (i = 0; i < MSGMNI; i++) {
152 		ok = 1;
153 		do {
154 			/* Get random key */
155 			keyarray[i] = (key_t) lrand48();
156 			/* Make sure key is unique and not private */
157 			if (keyarray[i] == IPC_PRIVATE) {
158 				ok = 0;
159 				continue;
160 			}
161 			for (j = 0; j < i; j++) {
162 				if (keyarray[j] == keyarray[i]) {
163 					ok = 0;
164 					break;
165 				}
166 				ok = 1;
167 			}
168 		} while (ok == 0);
169 	}
170 	/* Fork a number of processes, each of which will
171 	 * create a message queue with several (nkids) reader/writer
172 	 * pairs which will read and write a number (iterations)
173 	 * of random length messages with specific values (keys).
174 	 *
175 	 * We do not fork more than maxnprocs at a time and
176 	 * we fork until all the message queues get used.
177 	 */
178 
179 	if (MSGMNI <= maxnprocs) {
180 		nprocs = MSGMNI;
181 		dotest_iteration(0);
182 	} else {
183 		for (i = 0; i < (MSGMNI / maxnprocs); i++) {
184 			nprocs = maxnprocs;
185 			dotest_iteration(i*(MSGMNI / maxnprocs));
186 		}
187 
188 		nprocs = MSGMNI % maxnprocs;
189 		dotest_iteration(i*(MSGMNI / maxnprocs));
190 	}
191 
192 	tst_resm(TPASS, "msgctl11 ran successfully!");
193 
194 	cleanup();
195 	tst_exit();
196 }
197 
dotest_iteration(int off)198 static void dotest_iteration(int off)
199 {
200 	key_t key;
201 	int i, count, status;
202 	pid_t pid;
203 
204 	memset(pidarray, 0, sizeof(pidarray));
205 
206 	for (i = 0; i < nprocs; i++) {
207 		key = keyarray[off + i];
208 
209 		if ((pid = FORK_OR_VFORK()) < 0)
210 			tst_brkm(TFAIL, cleanup,
211 				 "Fork failed (may be OK if under stress)");
212 
213 		/* Child does this */
214 		if (pid == 0) {
215 #ifdef UCLINUX
216 			if (self_exec(argv0, "ndd", 1, key, i) < 0) {
217 				printf("\tself_exec failed\n");
218 				exit(FAIL);
219 			}
220 #else
221 			procstat = 1;
222 			exit(dotest(key, i));
223 #endif
224 		}
225 		pidarray[i] = pid;
226 	}
227 
228 	count = 0;
229 	while (1) {
230 		if ((wait(&status)) > 0) {
231 			if (status >> 8 != PASS)
232 				tst_brkm(TFAIL, cleanup,
233 					"Child exit status = %d", status >> 8);
234 			count++;
235 		} else {
236 			if (errno != EINTR) {
237 				break;
238 			}
239 #ifdef DEBUG
240 			tst_resm(TINFO, "Signal detected during wait");
241 #endif
242 		}
243 	}
244 	/* Make sure proper number of children exited */
245 	if (count != nprocs)
246 		tst_brkm(TFAIL, cleanup,
247 			 "Wrong number of children exited, Saw %d, Expected %d",
248 			 count, nprocs);
249 }
250 
251 #ifdef UCLINUX
do_child_1_uclinux(void)252 static void do_child_1_uclinux(void)
253 {
254 	procstat = 1;
255 	exit(dotest(key_uclinux, i_uclinux));
256 }
257 
do_child_2_uclinux(void)258 static void do_child_2_uclinux(void)
259 {
260 	procstat = 2;
261 	exit(doreader(key_uclinux, tid, pid_uclinux,
262 			child_process_uclinux, nreps));
263 }
264 
do_child_3_uclinux(void)265 static void do_child_3_uclinux(void)
266 {
267 	procstat = 2;
268 	exit(dowriter(key_uclinux, tid, rkid_uclinux,
269 			child_process_uclinux, nreps));
270 }
271 #endif
272 
cleanup_msgqueue(int i,int tid)273 static void cleanup_msgqueue(int i, int tid)
274 {
275 	/*
276 	 * Decrease the value of i by 1 because it
277 	 * is getting incremented even if the fork
278 	 * is failing.
279 	 */
280 
281 	i--;
282 	/*
283 	 * Kill all children & free message queue.
284 	 */
285 	for (; i >= 0; i--) {
286 		(void)kill(rkidarray[i], SIGKILL);
287 		(void)kill(wkidarray[i], SIGKILL);
288 	}
289 
290 	if (msgctl(tid, IPC_RMID, 0) < 0) {
291 		printf("Msgctl error in cleanup_msgqueue %d\n", errno);
292 		exit(FAIL);
293 	}
294 }
295 
dotest(key_t key,int child_process)296 static int dotest(key_t key, int child_process)
297 {
298 	int id, pid;
299 	int i, count, status, exit_status;
300 
301 	sighold(SIGTERM);
302 	if ((id = msgget(key, IPC_CREAT | S_IRUSR | S_IWUSR)) < 0) {
303 		printf("msgget() error in child %d: %s\n",
304 			child_process, strerror(errno));
305 		return FAIL;
306 	}
307 	tid = id;
308 	sigrelse(SIGTERM);
309 
310 	exit_status = PASS;
311 
312 	for (i = 0; i < nkids; i++) {
313 		if ((pid = FORK_OR_VFORK()) < 0) {
314 			printf("Fork failure in the first child of child group %d\n",
315 				child_process);
316 			cleanup_msgqueue(i, tid);
317 			return FAIL;
318 		}
319 		/* First child does this */
320 		if (pid == 0) {
321 #ifdef UCLINUX
322 			if (self_exec(argv0, "nddd", 2, key, getpid(),
323 				      child_process) < 0) {
324 				printf("self_exec failed\n");
325 				cleanup_msgqueue(i, tid);
326 				return FAIL;
327 			}
328 #else
329 			procstat = 2;
330 			exit(doreader(key, tid, getpid(),
331 					child_process, nreps));
332 #endif
333 		}
334 		rkidarray[i] = pid;
335 		if ((pid = FORK_OR_VFORK()) < 0) {
336 			printf("Fork failure in the second child of child group %d\n",
337 				child_process);
338 			/*
339 			 * Kill the reader child process
340 			 */
341 			(void)kill(rkidarray[i], SIGKILL);
342 
343 			cleanup_msgqueue(i, tid);
344 			return FAIL;
345 		}
346 		/* Second child does this */
347 		if (pid == 0) {
348 #ifdef UCLINUX
349 			if (self_exec(argv0, "nddd", 3, key, rkidarray[i],
350 				      child_process) < 0) {
351 				printf("\tFork failure in the first child of child group %d\n",
352 					child_process);
353 				/*
354 				 * Kill the reader child process
355 				 */
356 				(void)kill(rkidarray[i], SIGKILL);
357 
358 				cleanup_msgqueue(i, tid);
359 				return FAIL;
360 			}
361 #else
362 			procstat = 2;
363 			exit(dowriter(key, tid, rkidarray[i],
364 					child_process, nreps));
365 #endif
366 		}
367 		wkidarray[i] = pid;
368 	}
369 	/* Parent does this */
370 	count = 0;
371 	while (1) {
372 		if ((wait(&status)) > 0) {
373 			if (status >> 8 != PASS) {
374 				printf("Child exit status = %d from child group %d\n",
375 					status >> 8, child_process);
376 				for (i = 0; i < nkids; i++) {
377 					kill(rkidarray[i], SIGTERM);
378 					kill(wkidarray[i], SIGTERM);
379 				}
380 				if (msgctl(tid, IPC_RMID, 0) < 0) {
381 					printf("msgctl() error: %s\n",
382 						strerror(errno));
383 				}
384 				return FAIL;
385 			}
386 			count++;
387 		} else {
388 			if (errno != EINTR) {
389 				break;
390 			}
391 		}
392 	}
393 	/* Make sure proper number of children exited */
394 	if (count != (nkids * 2)) {
395 		printf("Wrong number of children exited in child group %d, saw %d, expected %d\n",
396 			child_process, count, (nkids * 2));
397 		if (msgctl(tid, IPC_RMID, 0) < 0) {
398 			printf("msgctl() error: %s\n", strerror(errno));
399 		}
400 		return FAIL;
401 	}
402 	if (msgctl(id, IPC_RMID, 0) < 0) {
403 		printf("msgctl() failure in child group %d: %s\n",
404 			child_process, strerror(errno));
405 		return FAIL;
406 	}
407 	return exit_status;
408 }
409 
410 /* ARGSUSED */
term(int sig LTP_ATTRIBUTE_UNUSED)411 static void term(int sig LTP_ATTRIBUTE_UNUSED)
412 {
413 	int i;
414 
415 	if (procstat == 0) {
416 #ifdef DEBUG
417 		tst_resm(TINFO, "SIGTERM signal received, test killing kids");
418 #endif
419 		for (i = 0; i < nprocs; i++) {
420 			if (pidarray[i] > 0) {
421 				if (kill(pidarray[i], SIGTERM) < 0) {
422 					tst_resm(TBROK,
423 						 "Kill failed to kill child %d",
424 						 i);
425 					exit(FAIL);
426 				}
427 			}
428 		}
429 		return;
430 	}
431 
432 	if (procstat == 2) {
433 		exit(PASS);
434 	}
435 
436 	if (tid == -1) {
437 		exit(FAIL);
438 	}
439 	for (i = 0; i < nkids; i++) {
440 		if (rkidarray[i] > 0)
441 			kill(rkidarray[i], SIGTERM);
442 		if (wkidarray[i] > 0)
443 			kill(wkidarray[i], SIGTERM);
444 	}
445 }
446 
setup(void)447 void setup(void)
448 {
449 	int nr_msgqs, free_pids;
450 
451 	tst_tmpdir();
452 	/* You will want to enable some signal handling so you can capture
453 	 * unexpected signals like SIGSEGV.
454 	 */
455 	tst_sig(FORK, DEF_HANDLER, cleanup);
456 
457 	/* One cavet that hasn't been fixed yet.  TEST_PAUSE contains the code to
458 	 * fork the test with the -c option.  You want to make sure you do this
459 	 * before you create your temporary directory.
460 	 */
461 	TEST_PAUSE;
462 
463 	nr_msgqs = get_max_msgqueues();
464 	if (nr_msgqs < 0)
465 		tst_brkm(TBROK, cleanup, "get_max_msgqueues() failed");
466 
467 	MSGMNI = nr_msgqs - get_used_msgqueues();
468 	if (MSGMNI <= 0)
469 		tst_brkm(TBROK, cleanup,
470 			 "Max number of message queues already used, cannot create more.");
471 
472 	tst_resm(TINFO, "Found %d available message queues", MSGMNI);
473 
474 	free_pids = tst_get_free_pids(cleanup);
475 	if (free_pids < 0) {
476 		tst_brkm(TBROK, cleanup, "Can't obtain free_pid count");
477 	} else if (!free_pids) {
478 		tst_brkm(TBROK, cleanup, "No free pids");
479 	}
480 
481 	/* We don't use more than a half of available pids.
482 	 * For each child we fork up to 2*maxnkids grandchildren. */
483 	maxnprocs = (free_pids / 2) / (1 + 2 * maxnkids);
484 
485 	if (!maxnprocs)
486 		tst_brkm(TBROK, cleanup, "Not enough free pids");
487 
488 	tst_resm(TINFO, "Using upto %d pids", free_pids / 2);
489 }
490 
cleanup(void)491 void cleanup(void)
492 {
493 	int status;
494 
495 	/*
496 	 * Remove the message queue from the system
497 	 */
498 #ifdef DEBUG
499 	tst_resm(TINFO, "Removing the message queue");
500 #endif
501 	(void)msgctl(tid, IPC_RMID, NULL);
502 	if ((status = msgctl(tid, IPC_STAT, NULL)) != -1) {
503 		(void)msgctl(tid, IPC_RMID, NULL);
504 		tst_resm(TFAIL, "msgctl(tid, IPC_RMID) failed");
505 
506 	}
507 
508 	tst_rmdir();
509 }
510