msgctl08.c revision 7d0a4a57fbcd47f72b67c08df532e8ef47f6fdae
1/* 2 * 3 * Copyright (c) International Business Machines Corp., 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 20/* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ 21/* 11/06/2002 Port to LTP dbarrera@us.ibm.com */ 22 23/* 24 * NAME 25 * msgctl08 26 * 27 * CALLS 28 * msgget(2) msgctl(2) 29 * 30 * ALGORITHM 31 * Get and manipulate a message queue. 32 * 33 * RESTRICTIONS 34 * 35 */ 36 37#define _XOPEN_SOURCE 500 38#include <signal.h> 39#include <errno.h> 40#include <string.h> 41#include <fcntl.h> 42#include <stdlib.h> 43#include <stdio.h> 44#include <unistd.h> 45#include <values.h> 46#include <sys/types.h> 47#include <sys/wait.h> 48#include <sys/stat.h> 49#include <sys/ipc.h> 50#include <sys/msg.h> 51#include "test.h" 52#include "usctest.h" 53#include "ipcmsg.h" 54 55void setup(); 56void cleanup(); 57/* 58 * * * * These globals must be defined in the test. 59 * * * */ 60 61char *TCID = "msgctl08"; /* Test program identifier. */ 62int TST_TOTAL = 1; /* Total number of test cases. */ 63extern int Tst_count; /* Test Case counter for tst_* routines */ 64 65int exp_enos[] = { 0 }; /* List must end with 0 */ 66 67#ifndef CONFIG_COLDFIRE 68#define MAXNPROCS 1000000 /* This value is set to an arbitrary high limit. */ 69#else 70#define MAXNPROCS 100000 /* Coldfire can't deal with 1000000 */ 71#endif 72#define MAXNREPS 100000 73#define FAIL 1 74#define PASS 0 75 76key_t keyarray[MAXNPROCS]; 77 78struct { 79 long type; 80 struct { 81 char len; 82 char pbytes[99]; 83 } data; 84} buffer; 85 86int pidarray[MAXNPROCS]; 87int tid; 88int MSGMNI, nprocs, nreps; 89int procstat; 90int dotest(key_t key, int child_process); 91int doreader(int id, long key, int child); 92int dowriter(int id, long key, int child); 93int fill_buffer(register char *buf, char val, register int size); 94int verify(register char *buf, char val, register int size, int child); 95void sig_handler(); /* signal catching function */ 96int mykid; 97#ifdef UCLINUX 98static char *argv0; 99 100void do_child_1_uclinux(); 101static key_t key_uclinux; 102static int i_uclinux; 103 104void do_child_2_uclinux(); 105static int id_uclinux; 106static int child_process_uclinux; 107#endif 108 109/*-----------------------------------------------------------------*/ 110int main(argc, argv) 111int argc; 112char *argv[]; 113{ 114 register int i, j, ok, pid; 115 int count, status; 116 struct sigaction act; 117 118#ifdef UCLINUX 119 char *msg; /* message returned from parse_opts */ 120 121 argv0 = argv[0]; 122 123 /* parse standard options */ 124 if ((msg = 125 parse_opts(argc, argv, NULL, NULL)) != NULL) { 126 tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); 127 } 128 129 maybe_run_child(&do_child_1_uclinux, "ndd", 1, &key_uclinux, 130 &i_uclinux); 131 maybe_run_child(&do_child_2_uclinux, "nddd", 2, &id_uclinux, 132 &key_uclinux, &child_process_uclinux); 133#endif 134 135 setup(); 136 137 if (argc == 1) { 138 /* Set default parameters */ 139 nreps = MAXNREPS; 140 nprocs = MSGMNI; 141 } else if (argc == 3) { 142 if (atoi(argv[1]) > MAXNREPS) { 143 tst_resm(TCONF, 144 "Requested number of iterations too large, setting to Max. of %d", 145 MAXNREPS); 146 nreps = MAXNREPS; 147 } else { 148 nreps = atoi(argv[1]); 149 } 150 if (atoi(argv[2]) > MSGMNI) { 151 tst_resm(TCONF, 152 "Requested number of processes too large, setting to Max. of %d", 153 MSGMNI); 154 nprocs = MSGMNI; 155 } else { 156 nprocs = atoi(argv[2]); 157 } 158 } else { 159 tst_resm(TCONF, 160 " Usage: %s [ number of iterations number of processes ]", 161 argv[0]); 162 tst_exit(); 163 } 164 165 srand(getpid()); 166 tid = -1; 167 168 /* Setup signal handleing routine */ 169 memset(&act, 0, sizeof(act)); 170 act.sa_handler = sig_handler; 171 sigemptyset(&act.sa_mask); 172 sigaddset(&act.sa_mask, SIGTERM); 173 if (sigaction(SIGTERM, &act, NULL) < 0) { 174 tst_resm(TFAIL, "Sigset SIGTERM failed"); 175 tst_exit(); 176 } 177 /* Set up array of unique keys for use in allocating message 178 * queues 179 */ 180 for (i = 0; i < nprocs; i++) { 181 ok = 1; 182 do { 183 /* Get random key */ 184 keyarray[i] = (key_t) rand(); 185 /* Make sure key is unique and not private */ 186 if (keyarray[i] == IPC_PRIVATE) { 187 ok = 0; 188 continue; 189 } 190 for (j = 0; j < i; j++) { 191 if (keyarray[j] == keyarray[i]) { 192 ok = 0; 193 break; 194 } 195 ok = 1; 196 } 197 } while (ok == 0); 198 } 199 200 /* Fork a number of processes, each of which will 201 * create a message queue with one reader/writer 202 * pair which will read and write a number (iterations) 203 * of random length messages with specific values. 204 */ 205 206 for (i = 0; i < nprocs; i++) { 207 fflush(stdout); 208 if ((pid = FORK_OR_VFORK()) < 0) { 209 tst_resm(TFAIL, 210 "\tFork failed (may be OK if under stress)"); 211 tst_exit(); 212 } 213 /* Child does this */ 214 if (pid == 0) { 215#ifdef UCLINUX 216 if (self_exec(argv[0], "ndd", 1, keyarray[i], i) < 0) { 217 tst_resm(TFAIL, "\tself_exec failed"); 218 tst_exit(); 219 } 220#else 221 procstat = 1; 222 exit(dotest(keyarray[i], 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 != 0) { 232 tst_resm(TFAIL, "Child exit status = %d", 233 status >> 8); 234 tst_exit(); 235 } 236 count++; 237 } else { 238 if (errno != EINTR) { 239 break; 240 } 241#ifdef DEBUG 242 tst_resm(TINFO, "Signal detected during wait"); 243#endif 244 } 245 } 246 /* Make sure proper number of children exited */ 247 if (count != nprocs) { 248 tst_resm(TFAIL, 249 "Wrong number of children exited, Saw %d, Expected %d", 250 count, nprocs); 251 tst_exit(); 252 } 253 254 tst_resm(TPASS, "msgctl08 ran successfully!"); 255 256 cleanup(); 257 return (0); 258 259} 260 261/*--------------------------------------------------------------------*/ 262 263#ifdef UCLINUX 264void do_child_1_uclinux() 265{ 266 procstat = 1; 267 exit(dotest(key_uclinux, i_uclinux)); 268} 269 270void do_child_2_uclinux() 271{ 272 exit(doreader(id_uclinux, key_uclinux % 255, child_process_uclinux)); 273} 274#endif 275 276int dotest(key, child_process) 277key_t key; 278int child_process; 279{ 280 int id, pid; 281 282 sighold(SIGTERM); 283 TEST(msgget(key, IPC_CREAT | S_IRUSR | S_IWUSR)); 284 if (TEST_RETURN < 0) { 285 tst_resm(TFAIL|TTERRNO, "Msgget error in child %d", 286 child_process); 287 tst_exit(); 288 } 289 tid = id = TEST_RETURN; 290 sigrelse(SIGTERM); 291 292 fflush(stdout); 293 if ((pid = FORK_OR_VFORK()) < 0) { 294 tst_resm(TWARN, "\tFork failed (may be OK if under stress)"); 295 TEST(msgctl(tid, IPC_RMID, 0)); 296 if (TEST_RETURN < 0) { 297 tst_resm(TFAIL|TTERRNO, "Msgctl error in cleanup"); 298 } 299 tst_exit(); 300 } 301 /* Child does this */ 302 if (pid == 0) { 303#ifdef UCLINUX 304 if (self_exec(argv0, "nddd", 2, id, key, child_process) < 0) { 305 tst_resm(TWARN, "self_exec failed"); 306 TEST(msgctl(tid, IPC_RMID, 0)); 307 if (TEST_RETURN < 0) { 308 tst_resm(TFAIL|TTERRNO, "Msgctl error in cleanup"); 309 } 310 tst_exit(); 311 } 312#else 313 exit(doreader(id, key % 255, child_process)); 314#endif 315 } 316 /* Parent does this */ 317 mykid = pid; 318 procstat = 2; 319 dowriter(id, key % 255, child_process); 320 wait(0); 321 TEST(msgctl(id, IPC_RMID, 0)); 322 if (TEST_RETURN < 0) { 323 tst_resm(TFAIL, "msgctl errno %d", TEST_ERRNO); 324 tst_exit(); 325 } 326 exit(PASS); 327} 328 329int doreader(id, key, child) 330int id, child; 331long key; 332{ 333 int i, size; 334 335 for (i = 0; i < nreps; i++) { 336 if ((size = msgrcv(id, &buffer, 100, 0, 0)) < 0) { 337 tst_brkm(TBROK|TERRNO, cleanup, 338 "Msgrcv error in child %d, read # = %d", 339 (i + 1), child); 340 tst_exit(); 341 } 342 if (buffer.data.len + 1 != size) { 343 tst_resm(TFAIL, 344 "Size mismatch in child %d, read # = %d", 345 child, (i + 1)); 346 tst_resm(TFAIL, 347 "for message size got %d expected %d", 348 size, buffer.data.len); 349 tst_exit(); 350 } 351 if (verify(buffer.data.pbytes, key, size - 1, child)) { 352 tst_resm(TFAIL, "in child %d read # = %d,key = %lx", 353 child, (i + 1), key); 354 tst_exit(); 355 } 356 key++; 357 } 358 return (0); 359} 360 361int dowriter(id, key, child) 362int id, child; 363long key; 364{ 365 int i, size; 366 367 for (i = 0; i < nreps; i++) { 368 do { 369 size = (rand() % 99); 370 } while (size == 0); 371 fill_buffer(buffer.data.pbytes, key, size); 372 buffer.data.len = size; 373 buffer.type = 1; 374 TEST(msgsnd(id, &buffer, size + 1, 0)); 375 if (TEST_RETURN < 0) { 376 tst_brkm(TBROK|TTERRNO, cleanup, 377 "Msgsnd error in child %d, key = %lx", 378 child, key); 379 } 380 key++; 381 } 382 return (0); 383} 384 385int fill_buffer(buf, val, size) 386register char *buf; 387char val; 388register int size; 389{ 390 register int i; 391 392 for (i = 0; i < size; i++) { 393 buf[i] = val; 394 } 395 396 return (0); 397} 398 399/* 400 * verify() 401 * Check a buffer for correct values. 402 */ 403 404int verify(buf, val, size, child) 405register char *buf; 406char val; 407register int size; 408int child; 409{ 410 while (size-- > 0) { 411 if (*buf++ != val) { 412 tst_resm(TWARN, 413 "Verify error in child %d, *buf = %x, val = %x, size = %d", 414 child, *buf, val, size); 415 return (FAIL); 416 } 417 } 418 return (PASS); 419} 420 421/* 422 * * void 423 * * sig_handler() - signal catching function for 'SIGUSR1' signal. 424 * * 425 * * This is a null function and used only to catch the above signal 426 * * generated in parent process. 427 * */ 428void sig_handler() 429{ 430} 431 432/*************************************************************** 433 * setup() - performs all ONE TIME setup for this test. 434 *****************************************************************/ 435void setup() 436{ 437 int nr_msgqs; 438 439 tst_tmpdir(); 440 441 /* You will want to enable some signal handling so you can capture 442 * unexpected signals like SIGSEGV. 443 */ 444 tst_sig(FORK, DEF_HANDLER, cleanup); 445 446 /* One cavet that hasn't been fixed yet. TEST_PAUSE contains the code to 447 * fork the test with the -c option. You want to make sure you do this 448 * before you create your temporary directory. 449 */ 450 TEST_PAUSE; 451 452 nr_msgqs = get_max_msgqueues(); 453 if (nr_msgqs < 0) 454 cleanup(); 455 456 nr_msgqs -= get_used_msgqueues(); 457 if (nr_msgqs <= 0) { 458 tst_resm(TBROK, 459 "Max number of message queues already used, cannot create more."); 460 cleanup(); 461 } 462 463 /* 464 * Since msgmni scales to the memory size, it may reach huge values 465 * that are not necessary for this test. 466 * That's why we define NR_MSGQUEUES as a high boundary for it. 467 */ 468 MSGMNI = min(nr_msgqs, NR_MSGQUEUES); 469} 470 471/*************************************************************** 472 * cleanup() - performs all ONE TIME cleanup for this test at 473 * completion or premature exit. 474 ****************************************************************/ 475void cleanup() 476{ 477 int status; 478 /* 479 * Remove the message queue from the system 480 */ 481#ifdef DEBUG 482 tst_resm(TINFO, "Removing the message queue"); 483#endif 484 fflush(stdout); 485 (void)msgctl(tid, IPC_RMID, NULL); 486 if ((status = msgctl(tid, IPC_STAT, NULL)) != -1) { 487 (void)msgctl(tid, IPC_RMID, NULL); 488 tst_resm(TFAIL, "msgctl(tid, IPC_RMID) failed"); 489 490 } 491 492 fflush(stdout); 493 /* 494 * print timing stats if that option was specified. 495 * print errno log if that option was specified. 496 */ 497 TEST_CLEANUP; 498 tst_rmdir(); 499 500}