Imported Upstream version 20091031
[ltp-debian.git] / testcases / kernel / syscalls / ipc / msgctl / msgctl09.c
blobfef167390c8d4b81b97a805fd933d53252eae4b7
1 /*
3 * Copyright (c) International Business Machines Corp., 2002
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 /* 06/30/2001 Port to Linux nsharoff@us.ibm.com */
21 /* 11/11/2002 Port to LTP dbarrera@us.ibm.com */
24 * NAME
25 * msgctl09
27 * CALLS
28 * msgget(2) msgctl(2) msgop(2)
30 * ALGORITHM
31 * Get and manipulate a message queue.
33 * RESTRICTIONS
37 #define _XOPEN_SOURCE 500
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <sys/ipc.h>
41 #include <sys/msg.h>
42 #include <sys/wait.h>
43 #include <signal.h>
44 #include <errno.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include "test.h"
50 #include "usctest.h"
51 #include "ipcmsg.h"
53 #define MAXNREPS 1000
54 #ifndef CONFIG_COLDFIRE
55 #define MAXNPROCS 1000000 /* This value is set to an arbitrary high limit. */
56 #else
57 #define MAXNPROCS 100000 /* Coldfire can't deal with 1000000 */
58 #endif
59 #define MAXNKIDS 10
60 #define FAIL 1
61 #define PASS 0
63 int dotest(key_t, int);
64 int doreader(long, int, int);
65 int dowriter(long, int, int);
66 int fill_buffer(char *, char, int);
67 int verify(char *, char, int, int);
68 void setup();
69 void cleanup();
72 * These globals must be defined in the test.
73 * */
75 char *TCID = "msgctl09"; /* Test program identifier. */
76 int TST_TOTAL = 1; /* Total number of test cases. */
77 extern int Tst_count; /* Test Case counter for tst_* routines */
79 int exp_enos[] = { 0 }; /* List must end with 0 */
81 key_t keyarray[MAXNPROCS];
83 struct {
84 long type;
85 struct {
86 char len;
87 char pbytes[99];
88 } data;
89 } buffer;
91 int pidarray[MAXNPROCS];
92 int rkidarray[MAXNKIDS];
93 int wkidarray[MAXNKIDS];
94 int tid;
95 int nprocs, nreps, nkids, MSGMNI;
96 int procstat;
97 void term(int);
98 #ifdef UCLINUX
99 static char *argv0;
101 void do_child_1_uclinux();
102 static key_t key_uclinux;
103 static int i_uclinux;
105 void do_child_2_uclinux();
106 static int pid_uclinux;
107 static int child_process_uclinux;
109 void do_child_3_uclinux();
110 static int rkid_uclinux;
111 #endif
112 void cleanup_msgqueue(int i, int tid);
114 /*-----------------------------------------------------------------*/
115 int main(argc, argv)
116 int argc;
117 char *argv[];
119 register int i, j, ok, pid;
120 int count, status;
122 #ifdef UCLINUX
123 char *msg; /* message returned from parse_opts */
125 argv0 = argv[0];
127 /* parse standard options */
128 if ((msg =
129 parse_opts(argc, argv, (option_t *) NULL, NULL)) != (char *)NULL) {
130 tst_brkm(TBROK, cleanup, "OPTION PARSING ERROR - %s", msg);
133 maybe_run_child(&do_child_1_uclinux, "ndd", 1, &key_uclinux,
134 &i_uclinux);
135 maybe_run_child(&do_child_2_uclinux, "nddd", 2, &key_uclinux,
136 &pid_uclinux, &child_process_uclinux);
137 maybe_run_child(&do_child_3_uclinux, "nddd", 3, &key_uclinux,
138 &rkid_uclinux, &child_process_uclinux);
139 #endif
141 setup();
143 if (argc == 1) {
144 /* Set default parameters */
145 nreps = MAXNREPS;
146 nprocs = MSGMNI;
147 nkids = MAXNKIDS;
148 } else if (argc == 4) {
149 if (atoi(argv[1]) > MAXNREPS) {
150 tst_resm(TCONF,
151 "Requested number of iterations too large, setting to Max. of %d",
152 MAXNREPS);
153 nreps = MAXNREPS;
154 } else {
155 nreps = atoi(argv[1]);
157 if (atoi(argv[2]) > MSGMNI) {
158 tst_resm(TCONF,
159 "Requested number of processes too large, setting to Max. of %d",
160 MSGMNI);
161 nprocs = MSGMNI;
162 } else {
163 nprocs = atoi(argv[2]);
165 if (atoi(argv[3]) > MAXNKIDS) {
166 tst_resm(TCONF,
167 "Requested number of read/write pairs too large; setting to Max. of %d",
168 MAXNKIDS);
169 nkids = MAXNKIDS;
170 } else {
171 nkids = atoi(argv[3]);
173 } else {
174 tst_resm(TCONF,
175 " Usage: %s [ number of iterations number of processes number of read/write pairs ]",
176 argv[0]);
177 tst_exit();
180 procstat = 0;
181 srand48((unsigned)getpid() + (unsigned)(getppid() << 16));
182 tid = -1;
184 /* Setup signal handleing routine */
185 if (sigset(SIGTERM, term) == SIG_ERR) {
186 tst_resm(TFAIL, "Sigset SIGTERM failed");
187 tst_exit();
189 /* Set up array of unique keys for use in allocating message
190 * queues
192 for (i = 0; i < nprocs; i++) {
193 ok = 1;
194 do {
195 /* Get random key */
196 keyarray[i] = (key_t) lrand48();
197 /* Make sure key is unique and not private */
198 if (keyarray[i] == IPC_PRIVATE) {
199 ok = 0;
200 continue;
202 for (j = 0; j < i; j++) {
203 if (keyarray[j] == keyarray[i]) {
204 ok = 0;
205 break;
207 ok = 1;
209 } while (ok == 0);
211 /*-----------------------------------------------------------------*/
212 /* Fork a number of processes (nprocs), each of which will
213 * create a message queue with several (nkids) reader/writer
214 * pairs which will read and write a number (iterations)
215 * of random length messages with specific values (keys).
218 for (i = 0; i < nprocs; i++) {
219 fflush(stdout);
220 if ((pid = FORK_OR_VFORK()) < 0) {
221 tst_resm(TFAIL,
222 "\tFork failed (may be OK if under stress)");
223 tst_exit();
225 /* Child does this */
226 if (pid == 0) {
227 #ifdef UCLINUX
228 if (self_exec(argv[0], "ndd", 1, keyarray[i], i) < 0) {
229 tst_resm(TFAIL, "\tself_exec failed");
230 tst_exit();
232 #else
233 procstat = 1;
234 exit(dotest(keyarray[i], i));
235 #endif
237 pidarray[i] = pid;
240 count = 0;
241 while (1) {
242 if ((wait(&status)) > 0) {
243 if (status >> 8 != PASS) {
244 tst_resm(TFAIL, "Child exit status = %d",
245 status >> 8);
246 tst_exit();
248 count++;
249 } else {
250 if (errno != EINTR) {
251 break;
253 #ifdef DEBUG
254 tst_resm(TINFO, "Signal detected during wait");
255 #endif
258 /* Make sure proper number of children exited */
259 if (count != nprocs) {
260 tst_resm(TFAIL,
261 "Wrong number of children exited, Saw %d, Expected %d",
262 count, nprocs);
263 tst_exit();
266 tst_resm(TPASS, "msgctl09 ran successfully!");
268 cleanup();
270 return (0);
274 /*--------------------------------------------------------------------*/
276 #ifdef UCLINUX
277 void do_child_1_uclinux()
279 procstat = 1;
280 exit(dotest(key_uclinux, i_uclinux));
283 void do_child_2_uclinux()
285 procstat = 2;
286 exit(doreader(key_uclinux, pid_uclinux, child_process_uclinux));
289 void do_child_3_uclinux()
291 procstat = 2;
292 exit(dowriter(key_uclinux, rkid_uclinux, child_process_uclinux));
294 #endif
296 void cleanup_msgqueue(int i, int tid)
299 * Decrease the value of i by 1 because it
300 * is getting incremented even if the fork
301 * is failing.
304 i--;
306 * Kill all children & free message queue.
308 for (; i >= 0; i--) {
309 (void)kill(rkidarray[i], SIGKILL);
310 (void)kill(wkidarray[i], SIGKILL);
313 if (msgctl(tid, IPC_RMID, 0) < 0) {
314 tst_resm(TFAIL|TERRNO, "Msgctl error in cleanup");
315 tst_exit();
319 int dotest(key, child_process)
320 key_t key;
321 int child_process;
323 int id, pid;
324 int i, count, status, exit_status;
326 sighold(SIGTERM);
327 if ((id = msgget(key, IPC_CREAT | S_IRUSR | S_IWUSR)) < 0) {
328 tst_resm(TFAIL|TERRNO, "Msgget error in child %d",
329 child_process);
330 tst_exit();
332 tid = id;
333 sigrelse(SIGTERM);
335 exit_status = PASS;
337 for (i = 0; i < nkids; i++) {
338 fflush(stdout);
339 if ((pid = FORK_OR_VFORK()) < 0) {
340 tst_resm(TWARN,
341 "Fork failure in first child of child group %d",
342 child_process);
343 cleanup_msgqueue(i, tid);
344 tst_exit();
346 /* First child does this */
347 if (pid == 0) {
348 #ifdef UCLINUX
349 if (self_exec(argv0, "nddd", 2, key, getpid(),
350 child_process) < 0) {
351 tst_resm(TWARN, "self_exec failed");
352 cleanup_msgqueue(i, tid);
353 tst_exit();
355 #else
356 procstat = 2;
357 exit(doreader(key, getpid(), child_process));
358 #endif
360 rkidarray[i] = pid;
361 fflush(stdout);
362 if ((pid = FORK_OR_VFORK()) < 0) {
363 tst_resm(TWARN,
364 "Fork failure in first child of child group %d",
365 child_process);
367 * Kill the reader child process
369 (void)kill(rkidarray[i], SIGKILL);
371 cleanup_msgqueue(i, tid);
372 tst_exit();
374 /* Second child does this */
375 if (pid == 0) {
376 #ifdef UCLINUX
377 if (self_exec(argv0, "nddd", 3, key, rkidarray[i],
378 child_process) < 0) {
379 tst_resm(TWARN, "\tFork failure in first child "
380 "of child group %d \n", child_process);
382 * Kill the reader child process
384 (void)kill(rkidarray[i], SIGKILL);
386 cleanup_msgqueue(i, tid);
387 tst_exit();
389 #else
390 procstat = 2;
391 exit(dowriter(key, rkidarray[i], child_process));
392 #endif
394 wkidarray[i] = pid;
396 /* Parent does this */
397 count = 0;
398 while (1) {
399 if ((wait(&status)) > 0) {
400 if (status >> 8 != PASS) {
401 tst_resm(TFAIL,
402 "Child exit status = %d from child group %d",
403 status >> 8, child_process);
404 for (i = 0; i < nkids; i++) {
405 kill(rkidarray[i], SIGTERM);
406 kill(wkidarray[i], SIGTERM);
408 if (msgctl(tid, IPC_RMID, 0) < 0) {
409 tst_resm(TFAIL|TERRNO,
410 "Msgctl error");
412 tst_exit();
414 count++;
415 } else {
416 if (errno != EINTR) {
417 break;
421 /* Make sure proper number of children exited */
422 if (count != (nkids * 2)) {
423 tst_resm(TFAIL,
424 "Wrong number of children exited in child group %d, Saw %d Expected %d",
425 child_process, count, (nkids * 2));
426 if (msgctl(tid, IPC_RMID, 0) < 0) {
427 tst_resm(TFAIL|TERRNO, "Msgctl error");
429 tst_exit();
431 if (msgctl(id, IPC_RMID, 0) < 0) {
432 tst_resm(TFAIL|TERRNO, "Msgctl failure in child group %d",
433 child_process);
434 tst_exit();
436 exit(exit_status);
439 int doreader(key, type, child)
440 int type, child;
441 long key;
443 int i, size;
444 int id;
446 if ((id = msgget(key, 0)) < 0) {
447 tst_resm(TFAIL|TERRNO,
448 "Msgget error in reader of child group %d",
449 child);
450 tst_exit();
452 if (id != tid) {
453 tst_resm(TFAIL,
454 "Message queue mismatch in reader of child group %d for message queue id %d",
455 child, id);
456 tst_exit();
458 for (i = 0; i < nreps; i++) {
459 if ((size = msgrcv(id, &buffer, 100, type, 0)) < 0) {
460 tst_resm(TFAIL|TERRNO,
461 "Msgrcv error in child %d, read # = %d",
462 (i + 1), child);
463 tst_exit();
465 if (buffer.type != type) {
466 tst_resm(TFAIL,
467 "Size mismatch in child %d, read # = %d",
468 child, (i + 1));
469 tst_resm(TFAIL,
470 "\tfor message size got %d expected %d",
471 size, buffer.data.len);
472 tst_exit();
474 if (buffer.data.len + 1 != size) {
475 tst_resm(TFAIL,
476 "Size mismatch in child %d, read # = %d, size = %d, expected = %d",
477 child, (i + 1), buffer.data.len, size);
478 tst_exit();
480 if (verify(buffer.data.pbytes, (key % 255), size - 1, child)) {
481 tst_resm(TFAIL, "in child %d read # = %d,key = %lx",
482 child, (i + 1), key);
483 tst_exit();
485 key++;
487 exit(PASS);
490 int dowriter(key, type, child)
491 int type, child;
492 long key;
494 int i, size;
495 int id;
497 if ((id = msgget(key, 0)) < 0) {
498 tst_resm(TFAIL|TERRNO,
499 "Msgget error in writer of child group %d",
500 child);
501 tst_exit();
503 if (id != tid) {
504 tst_resm(TFAIL,
505 "Message queue mismatch in writer of child group %d",
506 child);
507 tst_resm(TFAIL, "\tfor message queue id %d expected %d", id,
508 tid);
509 tst_exit();
512 for (i = 0; i < nreps; i++) {
513 do {
514 size = (lrand48() % 99);
515 } while (size == 0);
516 fill_buffer(buffer.data.pbytes, (key % 255), size);
517 buffer.data.len = size;
518 buffer.type = type;
519 if (msgsnd(id, &buffer, size + 1, 0) < 0) {
520 tst_resm(TFAIL|TERRNO,
521 "Msgsnd error in child %d, key = %lx",
522 child, key);
523 tst_exit();
525 key++;
527 exit(PASS);
530 int fill_buffer(buf, val, size)
531 register char *buf;
532 char val;
533 register int size;
535 register int i;
537 for (i = 0; i < size; i++)
538 buf[i] = val;
539 return 0;
543 * verify()
544 * Check a buffer for correct values.
547 int verify(buf, val, size, child)
548 register char *buf;
549 char val;
550 register int size;
551 int child;
553 while (size-- > 0)
554 if (*buf++ != val) {
555 tst_resm(TWARN,
556 "Verify error in child %d, *buf = %x, val = %x, size = %d",
557 child, *buf, val, size);
558 return (FAIL);
560 return (PASS);
563 /* ARGSUSED */
564 void term(int sig)
566 int i;
568 if (procstat == 0) {
569 #ifdef DEBUG
570 tst_resm(TINFO, "SIGTERM signal received, test killing kids");
571 #endif
572 for (i = 0; i < nprocs; i++) {
573 if (pidarray[i] > 0) {
574 if (kill(pidarray[i], SIGTERM) < 0) {
575 tst_resm(TBROK,
576 "Kill failed to kill child %d",
578 exit(FAIL);
582 return;
585 if (procstat == 2) {
586 fflush(stdout);
587 exit(PASS);
590 if (tid == -1) {
591 exit(FAIL);
593 for (i = 0; i < nkids; i++) {
594 if (rkidarray[i] > 0)
595 kill(rkidarray[i], SIGTERM);
596 if (wkidarray[i] > 0)
597 kill(wkidarray[i], SIGTERM);
601 /***************************************************************
602 * setup() - performs all ONE TIME setup for this test.
603 *****************************************************************/
604 void setup()
606 int nr_msgqs;
608 tst_tmpdir();
609 /* You will want to enable some signal handling so you can capture
610 * unexpected signals like SIGSEGV.
612 tst_sig(FORK, DEF_HANDLER, cleanup);
614 /* Pause if that option was specified */
615 /* One cavet that hasn't been fixed yet. TEST_PAUSE contains the code to
616 * fork the test with the -c option. You want to make sure you do this
617 * before you create your temporary directory.
619 TEST_PAUSE;
621 nr_msgqs = get_max_msgqueues();
622 if (nr_msgqs < 0)
623 cleanup();
625 nr_msgqs -= get_used_msgqueues();
626 if (nr_msgqs <= 0) {
627 tst_resm(TBROK,
628 "Max number of message queues already used, cannot create more.");
629 cleanup();
633 * Since msgmni scales to the memory size, it may reach huge values
634 * that are not necessary for this test.
635 * That's why we define NR_MSGQUEUES as a high boundary for it.
637 MSGMNI = min(nr_msgqs, NR_MSGQUEUES);
640 /***************************************************************
641 * cleanup() - performs all ONE TIME cleanup for this test at
642 * completion or premature exit.
643 ****************************************************************/
644 void cleanup()
646 int status;
648 * print timing stats if that option was specified.
649 * print errno log if that option was specified.
651 TEST_CLEANUP;
654 * Remove the message queue from the system
656 #ifdef DEBUG
657 tst_resm(TINFO, "Removing the message queue");
658 #endif
659 fflush(stdout);
660 (void)msgctl(tid, IPC_RMID, (struct msqid_ds *)NULL);
661 if ((status = msgctl(tid, IPC_STAT, (struct msqid_ds *)NULL)) != -1) {
662 (void)msgctl(tid, IPC_RMID, (struct msqid_ds *)NULL);
663 tst_resm(TFAIL, "msgctl(tid, IPC_RMID) failed");
664 tst_exit();
667 fflush(stdout);
668 tst_rmdir();
669 /* exit with return code appropriate for results */
670 tst_exit();