1 /* 2 * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. 3 * Copyright (c) 2012 Wanlong Gao <gaowanlong (at) cn.fujitsu.com> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of version 2 of the GNU General Public License as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it would be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 * 13 * You should have received a copy of the GNU General Public License along 14 * with this program; if not, write the Free Software Foundation, Inc., 15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 16 * 17 */ 18 /* 19 * TEST1 20 * ----- 21 * Call clone() with all resources shared. 22 * 23 * CHILD: 24 * modify the shared resources 25 * return 1 on success 26 * PARENT: 27 * wait for child to finish 28 * verify that the shared resourses are modified 29 * return 1 on success 30 * If parent & child returns successfully 31 * test passed 32 * else 33 * test failed 34 * 35 * TEST2 36 * ----- 37 * Call clone() with no resources shared. 38 * 39 * CHILD: 40 * modify the resources in child's address space 41 * return 1 on success 42 * PARENT: 43 * wait for child to finish 44 * verify that the parent's resourses are not modified 45 * return 1 on success 46 * If parent & child returns successfully 47 * test passed 48 * else 49 * test failed 50 */ 51 52 #if defined UCLINUX && !__THROW 53 /* workaround for libc bug */ 54 #define __THROW 55 #endif 56 57 #define _GNU_SOURCE 58 59 #include <errno.h> 60 #include <fcntl.h> 61 #include <sys/wait.h> 62 #include <sys/types.h> 63 #include <sys/syscall.h> 64 #include <sched.h> 65 #include "test.h" 66 67 #define FLAG_ALL (CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|SIGCHLD) 68 #define FLAG_NONE SIGCHLD 69 #define PARENT_VALUE 1 70 #define CHILD_VALUE 2 71 #define TRUE 1 72 #define FALSE 0 73 74 #include "clone_platform.h" 75 76 static void setup(void); 77 static int test_setup(void); 78 static void cleanup(void); 79 static void test_cleanup(void); 80 static int child_fn(); 81 static int parent_test1(void); 82 static int parent_test2(void); 83 static int test_VM(void); 84 static int test_FS(void); 85 static int test_FILES(void); 86 static int test_SIG(void); 87 static int modified_VM(void); 88 static int modified_FS(void); 89 static int modified_FILES(void); 90 static int modified_SIG(void); 91 static void sig_child_defined_handler(int); 92 static void sig_default_handler(); 93 94 static int fd_parent; 95 static char file_name[25]; 96 static int parent_variable; 97 static char cwd_parent[FILENAME_MAX]; 98 static int parent_got_signal, child_pid; 99 100 char *TCID = "clone02"; 101 102 struct test_case_t { 103 int flags; 104 int (*parent_fn) (); 105 } test_cases[] = { 106 { 107 FLAG_ALL, parent_test1}, { 108 FLAG_NONE, parent_test2} 109 }; 110 111 int TST_TOTAL = sizeof(test_cases) / sizeof(test_cases[0]); 112 113 int main(int ac, char **av) 114 { 115 116 int lc; 117 void *child_stack; 118 int status, i; 119 120 tst_parse_opts(ac, av, NULL, NULL); 121 122 setup(); 123 124 child_stack = malloc(CHILD_STACK_SIZE); 125 if (child_stack == NULL) 126 tst_brkm(TBROK, cleanup, "Cannot allocate stack for child"); 127 128 for (lc = 0; TEST_LOOPING(lc); lc++) { 129 tst_count = 0; 130 131 for (i = 0; i < TST_TOTAL; ++i) { 132 if (test_setup() != 0) { 133 tst_resm(TWARN, "test_setup() failed," 134 "skipping this test case"); 135 continue; 136 } 137 138 /* Test the system call */ 139 TEST(ltp_clone(test_cases[i].flags, child_fn, NULL, 140 CHILD_STACK_SIZE, child_stack)); 141 142 /* check return code */ 143 if (TEST_RETURN == -1) { 144 tst_resm(TFAIL | TTERRNO, "clone() failed"); 145 /* Cleanup & continue with next test case */ 146 test_cleanup(); 147 continue; 148 } 149 150 /* Wait for child to finish */ 151 if ((wait(&status)) == -1) { 152 tst_resm(TWARN | TERRNO, 153 "wait failed; skipping testcase"); 154 /* Cleanup & continue with next test case */ 155 test_cleanup(); 156 continue; 157 } 158 159 if (WTERMSIG(status)) 160 tst_resm(TWARN, "child exitied with signal %d", 161 WTERMSIG(status)); 162 163 /* 164 * Check the return value from child function and 165 * parent function. If both functions returned 166 * successfully, test passed, else failed 167 */ 168 if (WIFEXITED(status) && WEXITSTATUS(status) == 0 && 169 test_cases[i].parent_fn()) 170 tst_resm(TPASS, "Test Passed"); 171 else 172 tst_resm(TFAIL, "Test Failed"); 173 174 /* Do test specific cleanup */ 175 test_cleanup(); 176 } 177 } 178 179 free(child_stack); 180 181 cleanup(); 182 tst_exit(); 183 } 184 185 static void setup(void) 186 { 187 tst_sig(FORK, DEF_HANDLER, cleanup); 188 TEST_PAUSE; 189 tst_tmpdir(); 190 191 /* Get unique file name for each child process */ 192 if ((sprintf(file_name, "parent_file_%ld", syscall(__NR_gettid))) <= 0) 193 tst_brkm(TBROK | TERRNO, cleanup, "sprintf() failed"); 194 } 195 196 static void cleanup(void) 197 { 198 if (unlink(file_name) == -1) 199 tst_resm(TWARN | TERRNO, "unlink(%s) failed", file_name); 200 tst_rmdir(); 201 } 202 203 static int test_setup(void) 204 { 205 206 struct sigaction def_act; 207 208 /* Save current working directory of parent */ 209 if (getcwd(cwd_parent, sizeof(cwd_parent)) == NULL) { 210 tst_resm(TWARN | TERRNO, "getcwd() failed in test_setup()"); 211 return -1; 212 } 213 214 /* 215 * Set value for parent_variable in parent, which will be 216 * changed by child in test_VM(), for testing CLONE_VM flag 217 */ 218 parent_variable = PARENT_VALUE; 219 220 /* 221 * Open file from parent, which will be closed by 222 * child in test_FILES(), used for testing CLONE_FILES flag 223 */ 224 fd_parent = open(file_name, O_CREAT | O_RDWR, 0777); 225 if (fd_parent == -1) { 226 tst_resm(TWARN | TERRNO, "open() failed in test_setup()"); 227 return -1; 228 } 229 230 /* 231 * set parent_got_signal to FALSE, used for testing 232 * CLONE_SIGHAND flag 233 */ 234 parent_got_signal = FALSE; 235 236 /* Setup signal handler for SIGUSR2 */ 237 def_act.sa_handler = sig_default_handler; 238 def_act.sa_flags = SA_RESTART; 239 sigemptyset(&def_act.sa_mask); 240 241 if (sigaction(SIGUSR2, &def_act, NULL) == -1) { 242 tst_resm(TWARN | TERRNO, "sigaction() failed in test_setup()"); 243 return -1; 244 } 245 246 return 0; 247 } 248 249 static void test_cleanup(void) 250 { 251 252 /* Restore parent's working directory */ 253 if (chdir(cwd_parent) == -1) { 254 /* 255 * we have to exit here 256 * 257 * XXX (garrcoop): why??? 258 */ 259 tst_brkm(TBROK | TERRNO, cleanup, 260 "chdir() failed in test_cleanup()"); 261 } 262 263 } 264 265 static int child_fn(void) 266 { 267 268 /* save child pid */ 269 child_pid = syscall(__NR_gettid); 270 271 if (test_VM() == 0 && test_FILES() == 0 && test_FS() == 0 && 272 test_SIG() == 0) 273 exit(0); 274 exit(1); 275 } 276 277 static int parent_test1(void) 278 { 279 280 /* 281 * For first test case (with all flags set), all resources are 282 * shared between parent and child. So whatever changes made by 283 * child should get reflected in parent also. modified_*() 284 * functions check this. All of them should return 1 for 285 * parent_test1() to return 1 286 */ 287 288 if (modified_VM() && modified_FILES() && modified_FS() && 289 modified_SIG()) 290 return 0; 291 return -1; 292 } 293 294 static int parent_test2(void) 295 { 296 297 /* 298 * For second test case (with no resouce shared), all of the 299 * modified_*() functions should return 0 for parent_test2() 300 * to return 1 301 */ 302 if (modified_VM() || modified_FILES() || modified_FS() || 303 modified_SIG()) 304 return 0; 305 306 return -1; 307 } 308 309 /* 310 * test_VM() - function to change parent_variable from child's 311 * address space. If CLONE_VM flag is set, child shares 312 * the memory space with parent so this will be visible 313 * to parent also. 314 */ 315 316 static int test_VM(void) 317 { 318 parent_variable = CHILD_VALUE; 319 return 0; 320 } 321 322 /* 323 * test_FILES() - This function closes a file descriptor opened by 324 * parent. If CLONE_FILES flag is set, the parent and 325 * the child process share the same file descriptor 326 * table. so this affects the parent also 327 */ 328 static int test_FILES(void) 329 { 330 if (close(fd_parent) == -1) { 331 tst_resm(TWARN | TERRNO, "close failed in test_FILES"); 332 return -1; 333 } 334 return 0; 335 } 336 337 /* 338 * test_FS() - This function changes the current working directory 339 * of the child process. If CLONE_FS flag is set, this 340 * will be visible to parent also. 341 */ 342 static int test_FS(void) 343 { 344 char *test_tmpdir; 345 int rval; 346 347 test_tmpdir = tst_get_tmpdir(); 348 if (test_tmpdir == NULL) { 349 tst_resm(TWARN | TERRNO, "tst_get_tmpdir failed"); 350 rval = -1; 351 } else if (chdir(test_tmpdir) == -1) { 352 tst_resm(TWARN | TERRNO, "chdir failed in test_FS"); 353 rval = -1; 354 } else { 355 rval = 0; 356 } 357 358 free(test_tmpdir); 359 return rval; 360 } 361 362 /* 363 * test_SIG() - This function changes the signal handler for SIGUSR2 364 * signal for child. If CLONE_SIGHAND flag is set, this 365 * affects parent also. 366 */ 367 static int test_SIG(void) 368 { 369 370 struct sigaction new_act; 371 372 new_act.sa_handler = sig_child_defined_handler; 373 new_act.sa_flags = SA_RESTART; 374 sigemptyset(&new_act.sa_mask); 375 376 /* Set signal handler to sig_child_defined_handler */ 377 if (sigaction(SIGUSR2, &new_act, NULL) == -1) { 378 tst_resm(TWARN | TERRNO, "signal failed in test_SIG"); 379 return -1; 380 } 381 382 /* Send SIGUSR2 signal to parent */ 383 if (kill(getppid(), SIGUSR2) == -1) { 384 tst_resm(TWARN | TERRNO, "kill failed in test_SIG"); 385 return -1; 386 } 387 388 return 0; 389 } 390 391 /* 392 * modified_VM() - This function is called by parent process to check 393 * whether child's modification to parent_variable 394 * is visible to parent 395 */ 396 397 static int modified_VM(void) 398 { 399 400 if (parent_variable == CHILD_VALUE) 401 /* child has modified parent_variable */ 402 return 1; 403 404 return 0; 405 } 406 407 /* 408 * modified_FILES() - This function checks for file descriptor table 409 * modifications done by child 410 */ 411 static int modified_FILES(void) 412 { 413 char buff[20]; 414 415 if (((read(fd_parent, buff, sizeof(buff))) == -1) && (errno == EBADF)) 416 /* Child has closed this file descriptor */ 417 return 1; 418 419 /* close fd_parent */ 420 if ((close(fd_parent)) == -1) 421 tst_resm(TWARN | TERRNO, "close() failed in modified_FILES()"); 422 423 return 0; 424 } 425 426 /* 427 * modified_FS() - This function checks parent's current working directory 428 * to see whether its modified by child or not. 429 */ 430 static int modified_FS(void) 431 { 432 char cwd[FILENAME_MAX]; 433 434 if ((getcwd(cwd, sizeof(cwd))) == NULL) 435 tst_resm(TWARN | TERRNO, "getcwd() failed"); 436 437 if (!(strcmp(cwd, cwd_parent))) 438 /* cwd hasn't changed */ 439 return 0; 440 441 return 1; 442 } 443 444 /* 445 * modified_SIG() - This function checks whether child has changed 446 * parent's signal handler for signal, SIGUSR2 447 */ 448 static int modified_SIG(void) 449 { 450 451 if (parent_got_signal) 452 /* 453 * parent came through sig_child_defined_handler() 454 * this means child has changed parent's handler 455 */ 456 return 1; 457 458 return 0; 459 } 460 461 /* 462 * sig_child_defined_handler() - Signal handler installed by child 463 */ 464 static void sig_child_defined_handler(int pid) 465 { 466 if ((syscall(__NR_gettid)) == child_pid) 467 /* Child got signal, give warning */ 468 tst_resm(TWARN, "Child got SIGUSR2 signal"); 469 else 470 parent_got_signal = TRUE; 471 } 472 473 /* sig_default_handler() - Default handler for parent */ 474 static void sig_default_handler(void) 475 { 476 } 477