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