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 (at) us.ibm.com */ 21 /* 10/30/2002 Port to LTP dbarrera (at) 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 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 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 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 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 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 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 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 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 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 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 448 void setup(void) 449 { 450 tst_sig(NOFORK, DEF_HANDLER, cleanup); 451 452 TEST_PAUSE; 453 454 tst_tmpdir(); 455 } 456 457 void cleanup(void) 458 { 459 tst_rmdir(); 460 } 461