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 /* 10/30/2002 Port to LTP dbarrera@us.ibm.com */
22
23 /*
24 * Stress test of mkdir call.
25 *
26 * ALGORITHM
27 * Create multiple processes which create subdirectories in the
28 * same directory multiple times. On exit of all child processes,
29 * make sure all subdirectories can be removed.
30 *
31 * USAGE: mkdir09 -c # -t # -d #
32 * -c = number of children groups
33 * -t = number of seconds to run test
34 * -d = number of directories created in test directory
35 *
36 */
37
38 #include <stdio.h>
39 #include <sys/wait.h>
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <sys/stat.h>
43 #include <sys/mman.h>
44 #include <errno.h>
45 #include <signal.h>
46 #include <unistd.h>
47 #include <setjmp.h>
48 #include "test.h"
49
50 #include <stdlib.h>
51 #include <stdlib.h>
52 #include <string.h>
53
54 #define NCHILD 3
55
56 #define MODE_RWX 07770
57 #define DIR_NAME "./X.%d"
58
59 char *TCID = "mkdir09";
60 int TST_TOTAL = 1;
61
62 char testdir[MAXPATHLEN];
63 int parent_pid, sigchld, sigterm, jump;
64 void term(int sig);
65 void chld(int sig);
66 int *pidlist, child_count;
67 jmp_buf env_buf;
68
69 int getchild(int group, int child, int children);
70 int dochild1(void);
71 int dochild2(void);
72 int dochild3(int group);
73 int massmurder(void);
74 int runtest(void);
75 void setup(void);
76 void cleanup(void);
77
78 static int child_groups = 2;
79 static int test_time = 5;
80 static int nfiles = 5;
81
82 static char *opt_child_groups;
83 static char *opt_test_time;
84 static char *opt_nfiles;
85
86 static option_t options[] = {
87 {"c:", NULL, &opt_child_groups},
88 {"t:", NULL, &opt_test_time},
89 {"d:", NULL, &opt_nfiles},
90 {NULL, NULL, NULL}
91 };
92
usage(void)93 static void usage(void)
94 {
95 printf(" -c Child groups\n");
96 printf(" -t Test runtime\n");
97 printf(" -d Directories\n");
98 }
99
main(int argc,char * argv[])100 int main(int argc, char *argv[])
101 {
102 tst_parse_opts(argc, argv, options, usage);
103
104 if (opt_child_groups)
105 child_groups = atoi(opt_child_groups);
106
107 if (opt_test_time)
108 test_time = atoi(opt_test_time);
109
110 if (opt_nfiles)
111 nfiles = atoi(opt_nfiles);
112
113 setup();
114
115 if (signal(SIGTERM, term) == SIG_ERR) {
116 tst_brkm(TFAIL, cleanup,
117 "Error setting up SIGTERM signal, ERRNO = %d", errno);
118
119 }
120
121 if (signal(SIGCHLD, chld) == SIG_ERR) {
122 tst_brkm(TFAIL, cleanup,
123 "Error setting up SIGCHLD signal, ERRNO = %d", errno);
124
125 }
126
127 runtest();
128 cleanup();
129 tst_exit();
130 }
131
runtest(void)132 int runtest(void)
133 {
134 int i, j;
135 int count, child, status;
136 char tmpdir[MAXPATHLEN];
137
138 /* Create permanent directories with holes in directory structure */
139
140 for (j = 0; j < nfiles; j++) {
141 sprintf(tmpdir, DIR_NAME, j);
142 TEST(mkdir(tmpdir, MODE_RWX));
143
144 if (TEST_RETURN < 0) {
145 tst_brkm(TFAIL, cleanup,
146 "Error creating permanent directories, ERRNO = %d",
147 TEST_ERRNO);
148 }
149 if ((j % NCHILD) != 0) {
150 if (rmdir(tmpdir) < 0) {
151 tst_brkm(TFAIL, cleanup,
152 "Error removing directory, ERRNO = %d",
153 errno);
154 }
155 }
156 }
157
158 parent_pid = getpid();
159
160 /* allocate space for list of child pid's */
161
162 if ((pidlist = malloc((child_groups * NCHILD) * sizeof(int))) ==
163 NULL) {
164 tst_brkm(TWARN, NULL,
165 "\tMalloc failed (may be OK if under stress)");
166 }
167
168 child_count = 0;
169 for (j = 0; j < child_groups; j++) {
170 for (i = 0; i < NCHILD; i++) {
171 getchild(j, i, child_count);
172 child_count++;
173 }
174 }
175
176 /* If signal already received, skip to cleanup */
177
178 if (!sigchld && !sigterm) {
179 if (test_time) {
180 /* To get out of sleep if signal caught */
181 if (!setjmp(env_buf)) {
182 jump++;
183 sleep(test_time);
184 }
185 } else {
186 pause();
187 }
188 }
189
190 /* Reset signals since we are about to clean-up and to avoid
191 * problem with wait call * $
192 * */
193
194 if (signal(SIGTERM, SIG_IGN) == SIG_ERR) {
195 tst_brkm(TFAIL, cleanup,
196 "Error resetting SIGTERM signal, ERRNO = %d", errno);
197 }
198 if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
199 tst_brkm(TFAIL, cleanup,
200 "Error resetting SIGCHLD signal, ERRNO = %d", errno);
201 }
202
203 if (test_time) {
204 sleep(test_time);
205 }
206
207 /* Clean up children */
208 massmurder();
209 /*
210 * Watch children finish and show returns.
211 */
212
213 count = 0;
214 while (1) {
215 if ((child = wait(&status)) > 0) {
216 if (status != 0) {
217 tst_brkm(TWARN,
218 NULL,
219 "\tChild{%d} exited status = %0x",
220 child, status);
221 }
222 count++;
223 } else {
224 if (errno != EINTR) {
225 break;
226 }
227 tst_resm(TINFO, "\tSignal detected during wait");
228 }
229 }
230
231 /*
232 * Make sure correct number of children exited.
233 */
234
235 if (count != child_count) {
236 tst_resm(TWARN, "\tWrong number of children waited on!");
237 tst_brkm(TWARN, NULL, "\tSaw %d, expected %d", count,
238 NCHILD);
239 }
240
241 /* Check for core file in test directory. */
242
243 if (access("core", 0) == 0) {
244 tst_brkm(TWARN, NULL, "\tCore file found in test directory.");
245 }
246
247 /* Remove expected files */
248
249 for (j = 0; j < nfiles; j += NCHILD) {
250 sprintf(tmpdir, DIR_NAME, j);
251 if (rmdir(tmpdir) < 0) {
252 tst_brkm(TWARN,
253 NULL,
254 "\tError removing expected directory, ERRNO = %d",
255 errno);
256 }
257 }
258
259 tst_resm(TPASS, "PASS");
260
261 return 0;
262 }
263
getchild(int group,int child,int children)264 int getchild(int group, int child, int children)
265 {
266 int pid;
267
268 pid = FORK_OR_VFORK();
269
270 if (pid < 0) {
271
272 massmurder(); /* kill the kids */
273 tst_brkm(TBROK, cleanup,
274 "\tFork failed (may be OK if under stress)");
275 } else if (pid == 0) { /* child does this */
276 switch (children % NCHILD) {
277 case 0:
278 dochild1(); /* create existing directories */
279 break; /* so lint won't complain */
280 case 1:
281 dochild2(); /* remove nonexistant directories */
282 break;
283 case 2:
284 dochild3(group); /* create/delete directories */
285 break;
286 default:
287 tst_brkm(TFAIL, cleanup,
288 "Test not inplemented for child %d", child);
289 exit(1);
290 break;
291 }
292 exit(1); /* If child gets here, something wrong */
293 }
294 pidlist[children] = pid;
295 return 0;
296 }
297
term(int sig)298 void term(int sig)
299 {
300 /* Routine to handle SIGTERM signal. */
301
302 if (parent_pid == getpid()) {
303 tst_brkm(TWARN, NULL, "\tsignal SIGTERM received by parent.");
304 }
305 sigterm++;
306 if (jump) {
307 longjmp(env_buf, 1);
308 }
309 }
310
chld(int sig)311 void chld(int sig)
312 {
313 /* Routine to handle SIGCHLD signal. */
314
315 sigchld++;
316 if (jump) {
317 longjmp(env_buf, 1);
318 }
319 }
320
dochild1(void)321 int dochild1(void)
322 {
323 /* Child routine which attempts to create directories in the test
324 * directory that already exist. Runs until a SIGTERM signal is
325 * received. Will exit with an error if it is able to create the
326 * directory or if the expected error is not received.
327 */
328
329 int j;
330 char tmpdir[MAXPATHLEN];
331
332 while (!sigterm) {
333 for (j = 0; j < nfiles; j += NCHILD) {
334 sprintf(tmpdir, DIR_NAME, j);
335 TEST(mkdir(tmpdir, MODE_RWX));
336
337 if (TEST_RETURN < 0) {
338
339 if (TEST_ERRNO != EEXIST) {
340 tst_brkm(TFAIL, cleanup,
341 "MKDIR %s, errno = %d; Wrong error detected.",
342 tmpdir, TEST_ERRNO);
343 exit(1);
344 }
345 } else {
346 tst_brkm(TFAIL, cleanup,
347 "MKDIR %s succeded when it shoud have failed.",
348 tmpdir);
349 exit(1);
350 }
351 }
352 }
353 exit(0);
354 }
355
dochild2(void)356 int dochild2(void)
357 {
358 /* Child routine which attempts to remove directories from the
359 * test directory which do not exist. Runs until a SIGTERM
360 * signal is received. Exits with an error if the proper
361 * error is not detected or if the remove operation is
362 * successful.
363 */
364
365 int j;
366 char tmpdir[MAXPATHLEN];
367
368 while (!sigterm) {
369 for (j = 1; j < nfiles; j += NCHILD) {
370 sprintf(tmpdir, DIR_NAME, j);
371 if (rmdir(tmpdir) < 0) {
372 if (errno != ENOENT) {
373 tst_brkm(TFAIL, cleanup,
374 "RMDIR %s, errno = %d; Wrong error detected.",
375 tmpdir, errno);
376 exit(1);
377 }
378 } else {
379 tst_brkm(TFAIL, cleanup,
380 "RMDIR %s succeded when it should have failed.",
381 tmpdir);
382 exit(1);
383 }
384 }
385 }
386 exit(0);
387 return 0;
388 }
389
dochild3(int group)390 int dochild3(int group)
391 {
392 /* Child routine which creates and deletes directories in the
393 * test directory. Runs until a SIGTERM signal is received, then
394 * cleans up and exits. Detects error if the expected condition
395 * is not encountered.
396 */
397
398 int j;
399
400 char tmpdir[MAXPATHLEN];
401 char tmp[MAXPATHLEN];
402
403 while (!sigterm) {
404 for (j = 2; j < nfiles; j += NCHILD) {
405 strcpy(tmp, DIR_NAME);
406 strcat(tmp, ".%d");
407 sprintf(tmpdir, tmp, j, group);
408
409 TEST(mkdir(tmpdir, MODE_RWX));
410
411 if (TEST_RETURN < 0) {
412 tst_brkm(TFAIL, cleanup,
413 "MKDIR %s, errno = %d; Wrong error detected.",
414 tmpdir, TEST_ERRNO);
415 exit(1);
416 }
417 }
418 for (j = 2; j < nfiles; j += NCHILD) {
419 strcpy(tmp, DIR_NAME);
420 strcat(tmp, ".%d");
421 sprintf(tmpdir, tmp, j, group);
422 if (rmdir(tmpdir) < 0) {
423 tst_brkm(TFAIL, cleanup,
424 "RMDIR %s, errno = %d; Wrong error detected.",
425 tmpdir, errno);
426 exit(1);
427 }
428 }
429 }
430 exit(0);
431 }
432
massmurder(void)433 int massmurder(void)
434 {
435 register int j;
436 for (j = 0; j < child_count; j++) {
437 if (pidlist[j] > 0) {
438 if (kill(pidlist[j], SIGTERM) < 0) {
439 tst_brkm(TFAIL, cleanup,
440 "Error killing child %d, ERRNO = %d",
441 j, errno);
442 }
443 }
444 }
445 return 0;
446 }
447
setup(void)448 void setup(void)
449 {
450 tst_sig(NOFORK, DEF_HANDLER, cleanup);
451
452 TEST_PAUSE;
453
454 tst_tmpdir();
455 }
456
cleanup(void)457 void cleanup(void)
458 {
459 tst_rmdir();
460 }
461