• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "libmsgctl.h"
41 
42 char *TCID = "msgstress02";
43 int 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 
53 static key_t keyarray[MAXNPROCS];
54 static int pidarray[MAXNPROCS];
55 static int rkidarray[MAXNKIDS];
56 static int wkidarray[MAXNKIDS];
57 static int tid;
58 static int nprocs, nreps, nkids, MSGMNI;
59 static int procstat;
60 
61 void setup(void);
62 void cleanup(void);
63 
64 static void term(int);
65 static int dotest(key_t, int);
66 static void cleanup_msgqueue(int i, int tid);
67 
68 static char *opt_nprocs;
69 static char *opt_nkids;
70 static char *opt_nreps;
71 
72 static option_t options[] = {
73 	{"n:", NULL, &opt_nprocs},
74 	{"c:", NULL, &opt_nkids},
75 	{"l:", NULL, &opt_nreps},
76 	{NULL, NULL, NULL},
77 };
78 
usage(void)79 static 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 
main(int argc,char ** argv)86 int 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, "Test ran successfully!");
207 
208 	cleanup();
209 	tst_exit();
210 }
211 
cleanup_msgqueue(int i,int tid)212 static 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 
dotest(key_t key,int child_process)234 static 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 
term(int sig LTP_ATTRIBUTE_UNUSED)326 static 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 
setup(void)362 void 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 
cleanup(void)391 void 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