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/06/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 <signal.h> 28#include <errno.h> 29#include <string.h> 30#include <fcntl.h> 31#include <stdlib.h> 32#include <stdio.h> 33#include <unistd.h> 34#include <values.h> 35#include <sys/types.h> 36#include <sys/wait.h> 37#include <sys/stat.h> 38#include <sys/ipc.h> 39#include <sys/msg.h> 40#include "test.h" 41#include "ipcmsg.h" 42#include "libmsgctl.h" 43 44char *TCID = "msgstress01"; 45int TST_TOTAL = 1; 46 47#ifndef CONFIG_COLDFIRE 48#define MAXNPROCS 1000000 /* This value is set to an arbitrary high limit. */ 49#else 50#define MAXNPROCS 100000 /* Coldfire can't deal with 1000000 */ 51#endif 52#define MAXNREPS 100000 53 54static key_t keyarray[MAXNPROCS]; 55static int pidarray[MAXNPROCS]; 56static int tid; 57static int MSGMNI, nprocs, nreps; 58static int procstat; 59static int mykid; 60 61void setup(void); 62void cleanup(void); 63 64static int dotest(key_t key, int child_process); 65static void sig_handler(); 66 67static char *opt_nprocs; 68static char *opt_nreps; 69 70static option_t options[] = { 71 {"n:", NULL, &opt_nprocs}, 72 {"l:", NULL, &opt_nreps}, 73 {NULL, NULL, NULL}, 74}; 75 76static void usage(void) 77{ 78 printf(" -n Number of processes\n"); 79 printf(" -l Number of iterations\n"); 80} 81 82int main(int argc, char **argv) 83{ 84 int i, j, ok, pid; 85 int count, status; 86 struct sigaction act; 87 88 tst_parse_opts(argc, argv, options, usage); 89 90 setup(); 91 92 nreps = MAXNREPS; 93 nprocs = MSGMNI; 94 95 if (opt_nreps) { 96 nreps = atoi(opt_nreps); 97 if (nreps > MAXNREPS) { 98 tst_resm(TINFO, 99 "Requested number of iterations too large, " 100 "setting to Max. of %d", MAXNREPS); 101 nreps = MAXNREPS; 102 } 103 } 104 105 if (opt_nprocs) { 106 nprocs = atoi(opt_nprocs); 107 if (nprocs > MSGMNI) { 108 tst_resm(TINFO, 109 "Requested number of processes too large, " 110 "setting to Max. of %d", MSGMNI); 111 nprocs = MSGMNI; 112 } 113 } 114 115 srand(getpid()); 116 tid = -1; 117 118 /* Setup signal handling routine */ 119 memset(&act, 0, sizeof(act)); 120 act.sa_handler = sig_handler; 121 sigemptyset(&act.sa_mask); 122 sigaddset(&act.sa_mask, SIGTERM); 123 if (sigaction(SIGTERM, &act, NULL) < 0) { 124 tst_brkm(TFAIL, NULL, "Sigset SIGTERM failed"); 125 } 126 /* Set up array of unique keys for use in allocating message 127 * queues 128 */ 129 for (i = 0; i < nprocs; i++) { 130 ok = 1; 131 do { 132 /* Get random key */ 133 keyarray[i] = (key_t) rand(); 134 /* Make sure key is unique and not private */ 135 if (keyarray[i] == IPC_PRIVATE) { 136 ok = 0; 137 continue; 138 } 139 for (j = 0; j < i; j++) { 140 if (keyarray[j] == keyarray[i]) { 141 ok = 0; 142 break; 143 } 144 ok = 1; 145 } 146 } while (ok == 0); 147 } 148 149 /* Fork a number of processes, each of which will 150 * create a message queue with one reader/writer 151 * pair which will read and write a number (iterations) 152 * of random length messages with specific values. 153 */ 154 155 for (i = 0; i < nprocs; i++) { 156 fflush(stdout); 157 if ((pid = FORK_OR_VFORK()) < 0) { 158 tst_brkm(TFAIL, 159 NULL, 160 "\tFork failed (may be OK if under stress)"); 161 } 162 /* Child does this */ 163 if (pid == 0) { 164 procstat = 1; 165 exit(dotest(keyarray[i], i)); 166 } 167 pidarray[i] = pid; 168 } 169 170 count = 0; 171 while (1) { 172 if ((wait(&status)) > 0) { 173 if (status >> 8 != 0) { 174 tst_brkm(TFAIL, NULL, 175 "Child exit status = %d", 176 status >> 8); 177 } 178 count++; 179 } else { 180 if (errno != EINTR) { 181 break; 182 } 183#ifdef DEBUG 184 tst_resm(TINFO, "Signal detected during wait"); 185#endif 186 } 187 } 188 /* Make sure proper number of children exited */ 189 if (count != nprocs) { 190 tst_brkm(TFAIL, 191 NULL, 192 "Wrong number of children exited, Saw %d, Expected %d", 193 count, nprocs); 194 } 195 196 tst_resm(TPASS, "Test ran successfully!"); 197 198 cleanup(); 199 tst_exit(); 200} 201 202static int dotest(key_t key, int child_process) 203{ 204 int id, pid; 205 int ret, status; 206 207 sighold(SIGTERM); 208 TEST(msgget(key, IPC_CREAT | S_IRUSR | S_IWUSR)); 209 if (TEST_RETURN < 0) { 210 printf("msgget() error in child %d: %s\n", 211 child_process, strerror(TEST_ERRNO)); 212 213 return FAIL; 214 } 215 tid = id = TEST_RETURN; 216 sigrelse(SIGTERM); 217 218 fflush(stdout); 219 if ((pid = FORK_OR_VFORK()) < 0) { 220 printf("\tFork failed (may be OK if under stress)\n"); 221 TEST(msgctl(tid, IPC_RMID, 0)); 222 if (TEST_RETURN < 0) { 223 printf("mscgtl() error in cleanup: %s\n", 224 strerror(TEST_ERRNO)); 225 } 226 return FAIL; 227 } 228 /* Child does this */ 229 if (pid == 0) 230 exit(doreader(key, id, 1, child_process, nreps)); 231 /* Parent does this */ 232 mykid = pid; 233 procstat = 2; 234 ret = dowriter(key, id, 1, child_process, nreps); 235 wait(&status); 236 237 if (ret != PASS) 238 exit(FAIL); 239 240 if ((!WIFEXITED(status) || (WEXITSTATUS(status) != PASS))) 241 exit(FAIL); 242 243 TEST(msgctl(id, IPC_RMID, 0)); 244 if (TEST_RETURN < 0) { 245 printf("msgctl() errno %d: %s\n", 246 TEST_ERRNO, strerror(TEST_ERRNO)); 247 248 return FAIL; 249 } 250 return PASS; 251} 252 253static void sig_handler(void) 254{ 255} 256 257void setup(void) 258{ 259 int nr_msgqs; 260 261 tst_tmpdir(); 262 263 tst_sig(FORK, DEF_HANDLER, cleanup); 264 265 TEST_PAUSE; 266 267 nr_msgqs = get_max_msgqueues(); 268 if (nr_msgqs < 0) 269 cleanup(); 270 271 nr_msgqs -= get_used_msgqueues(); 272 if (nr_msgqs <= 0) { 273 tst_resm(TBROK, 274 "Max number of message queues already used, cannot create more."); 275 cleanup(); 276 } 277 278 /* 279 * Since msgmni scales to the memory size, it may reach huge values 280 * that are not necessary for this test. 281 * That's why we define NR_MSGQUEUES as a high boundary for it. 282 */ 283 MSGMNI = MIN(nr_msgqs, NR_MSGQUEUES); 284} 285 286void cleanup(void) 287{ 288 int status; 289 290#ifdef DEBUG 291 tst_resm(TINFO, "Removing the message queue"); 292#endif 293 (void)msgctl(tid, IPC_RMID, NULL); 294 if ((status = msgctl(tid, IPC_STAT, NULL)) != -1) { 295 (void)msgctl(tid, IPC_RMID, NULL); 296 tst_resm(TFAIL, "msgctl(tid, IPC_RMID) failed"); 297 298 } 299 300 tst_rmdir(); 301} 302