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 "../lib/libmsgctl.h"
43
44 char *TCID = "msgstress01";
45 int 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
54 static key_t keyarray[MAXNPROCS];
55 static int pidarray[MAXNPROCS];
56 static int tid;
57 static int MSGMNI, nprocs, nreps;
58 static int procstat;
59 static int mykid;
60
61 void setup(void);
62 void cleanup(void);
63
64 static int dotest(key_t key, int child_process);
65 static void sig_handler();
66
67 static char *opt_nprocs;
68 static char *opt_nreps;
69
70 static option_t options[] = {
71 {"n:", NULL, &opt_nprocs},
72 {"l:", NULL, &opt_nreps},
73 {NULL, NULL, NULL},
74 };
75
usage(void)76 static void usage(void)
77 {
78 printf(" -n Number of processes\n");
79 printf(" -l Number of iterations\n");
80 }
81
main(int argc,char ** argv)82 int 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
dotest(key_t key,int child_process)202 static 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
sig_handler(void)253 static void sig_handler(void)
254 {
255 }
256
setup(void)257 void 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
cleanup(void)286 void 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