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