blob: 296df26b182c26de1b314bee4a2f0575660b5be6 [file] [log] [blame]
nstraze3219812003-07-14 17:06:37 +00001/*
2 * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * You should have received a copy of the GNU General Public License along
13 * with this program; if not, write the Free Software Foundation, Inc., 59
14 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
15 *
16 */
17/**********************************************************
18 *
19 * TEST IDENTIFIER : clone02
20 *
21 * EXECUTED BY : anyone
22 *
23 * TEST TITLE : Functionality tests for clone(2)
24 *
25 * TEST CASE TOTAL : 2
26 *
27 * AUTHOR : Saji Kumar.V.R <saji.kumar@wipro.com>
28 *
29 * SIGNALS
30 * Uses SIGUSR1 to pause before test if option set.
31 * (See the parse_opts(3) man page).
32 *
33 * DESCRIPTION
34 *
35 * Setup:
36 * Setup signal handling.
37 * Pause for SIGUSR1 if option specified.
38 * generate a unique file name fore each test instance
39 *
40 * Test:
41 * Loop if the proper options are given.
42 * TEST1
43 * -----
44 * Call clone() with all resources shared.
45 *
46 * CHILD:
47 * modify the shared resources
48 * return 1 on success
49 * PARENT:
50 * wait for child to finish
51 * verify that the shared resourses are modified
52 * return 1 on success
53 * If parent & child returns successfully
54 * test passed
55 * else
56 * test failed
57 *
58 * TEST2
59 * -----
60 * Call clone() with no resources shared.
61 *
62 * CHILD:
63 * modify the resources in child's address space
64 * return 1 on success
65 * PARENT:
66 * wait for child to finish
67 * verify that the parent's resourses are not modified
68 * return 1 on success
69 * If parent & child returns successfully
70 * test passed
71 * else
72 * test failed
73 * Cleanup:
74 * Print errno log and/or timing stats if options given
75 *
76 * USAGE: <for command-line>
77 * clone02 [-c n] [-e] [-i n] [-I x] [-P x] [-t] [-h] [-f] [-p]
78 * where, -c n : Run n copies concurrently.
79 * -e : Turn on errno logging.
80 * -h : Show help screen
81 * -f : Turn off functional testing
82 * -i n : Execute test n times.
83 * -I x : Execute test for x seconds.
84 * -p : Pause for SIGUSR1 before starting
85 * -P x : Pause for x seconds between iterations.
86 * -t : Turn on syscall timing.
87 *
mridgefc9dd8c2004-05-11 21:37:01 +000088 *
89 * MODIFIED: - mridge@us.ibm.com -- changed getpid to syscall(get thread ID) for unique ID on NPTL threading
90 *
91 *
nstraze3219812003-07-14 17:06:37 +000092 ****************************************************************/
93
robbiewd34d5812005-07-11 22:28:09 +000094#if defined UCLINUX && !__THROW
95/* workaround for libc bug */
96#define __THROW
97#endif
98
nstraze3219812003-07-14 17:06:37 +000099#include <errno.h>
100#include <sched.h>
101#include <fcntl.h>
102#include <sys/wait.h>
mridgefc9dd8c2004-05-11 21:37:01 +0000103#include <sys/types.h>
104#include <sys/syscall.h>
105#include <linux/unistd.h>
nstraze3219812003-07-14 17:06:37 +0000106#include "test.h"
107#include "usctest.h"
108
robbiewb8bd30c2003-07-31 20:41:11 +0000109#define FLAG_ALL CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD
nstraze3219812003-07-14 17:06:37 +0000110#define FLAG_NONE SIGCHLD
111#define PARENT_VALUE 1
112#define CHILD_VALUE 2
113#define TRUE 1
114#define FALSE 0
115
nstraz882ee2d2003-07-16 17:40:32 +0000116#include "clone_platform.h"
nstraze3219812003-07-14 17:06:37 +0000117
118static void setup();
119static int test_setup();
120static void cleanup();
121static void test_cleanup();
122static int child_fn();
123static int parent_test1();
124static int parent_test2();
125static int test_VM();
126static int test_FS();
127static int test_FILES();
128static int test_SIG();
129static int modified_VM();
130static int modified_FS();
131static int modified_FILES();
132static int modified_SIG();
133static void sig_child_defined_handler();
134static void sig_default_handler();
135
136
137static int fd_parent;
138static char file_name[25];
139static int parent_variable;
140static char cwd_parent[FILENAME_MAX];
141static int parent_got_signal, child_pid;
142
143char *TCID="clone02"; /* Test program identifier. */
144extern int Tst_count; /* Test Case counter for tst_* routines */
145
146struct test_case_t {
147 int flags;
148 int (*parent_fn)();
149} test_cases[] = {
150 { FLAG_ALL, parent_test1},
151 { FLAG_NONE, parent_test2}
152};
153
154int TST_TOTAL = sizeof(test_cases) / sizeof(test_cases[0]);
155
156
mridgefc9dd8c2004-05-11 21:37:01 +0000157
nstraze3219812003-07-14 17:06:37 +0000158int
159main(int ac, char **av)
160{
161
162 int lc; /* loop counter */
163 char *msg; /* message returned from parse_opts */
164 void *child_stack; /* stack for child */
165 int status, i;
166
167 /* parse standard options */
168 if ((msg=parse_opts(ac, av, (option_t *)NULL, NULL)) != (char *)NULL) {
169 tst_brkm(TBROK, tst_exit, "OPTION PARSING ERROR - %s", msg);
170 }
171
172 /* perform global setup for test */
173 setup();
174
175 /* Allocate stack for child */
176 if((child_stack = (void *) malloc(CHILD_STACK_SIZE)) == NULL) {
177 tst_brkm(TBROK, cleanup, "Cannot allocate stack for child");
178 }
179
180 /* check looping state if -c option given */
181 for (lc=0; TEST_LOOPING(lc); lc++) {
182
183 /* reset Tst_count in case we are looping. */
184 Tst_count=0;
185
186 for (i=0; i<TST_TOTAL; ++i) {
187
188 /*Do test specific setup */
189 if (!(test_setup())) {
190 tst_resm(TWARN, "test_setup() failed,"
191 "skipping this test case");
192 continue;
193 }
194
195 /* Test the system call */
mridgec23070c2005-01-05 20:52:41 +0000196#if defined(__hppa__)
nstraze3219812003-07-14 17:06:37 +0000197 TEST(clone(child_fn, child_stack,
198 test_cases[i].flags, NULL));
nstraz882ee2d2003-07-16 17:40:32 +0000199#elif defined(__ia64__)
200 TEST(clone2(child_fn, child_stack,
201 CHILD_STACK_SIZE, test_cases[i].flags, NULL,
202 NULL, NULL, NULL));
nstraze3219812003-07-14 17:06:37 +0000203#else
204 TEST(clone(child_fn, child_stack + CHILD_STACK_SIZE,
205 test_cases[i].flags, NULL));
206#endif
207
208 /* check return code */
209 if (TEST_RETURN == -1) {
210 tst_resm(TFAIL, "clone() Failed, errno = %d :"
211 " %s", TEST_ERRNO,
212 strerror(TEST_ERRNO));
213 /* Cleanup & continue with next test case */
214 test_cleanup();
215 continue;
216 }
217
218 /* Wait for child to finish */
219 if ((wait(&status)) < 0) {
220 tst_resm(TWARN, "wait() failed, skipping this"
221 " test case");
222 /* Cleanup & continue with next test case */
223 test_cleanup();
224 continue;
225 }
robbieweed7cee2005-03-21 21:58:10 +0000226
227 if (WTERMSIG(status)) {
228 tst_resm(TWARN, "child exitied with signal %d", WTERMSIG(status));
229 }
nstraze3219812003-07-14 17:06:37 +0000230
231 /*
232 * Check the return value from child function and
233 * parent function. If both functions returned
234 * successfully, test passed, else failed
235 */
236 if ((WIFEXITED(status)) && (WEXITSTATUS(status)) &&
237 (test_cases[i].parent_fn())) {
238 tst_resm(TPASS, "Test Passed");
239 } else {
240 tst_resm(TFAIL, "Test Failed");
241 }
242
243 /* Do test specific cleanup */
244 test_cleanup();
245 }
246 } /* End for TEST_LOOPING */
247
248 free(child_stack);
robbiewb8bd30c2003-07-31 20:41:11 +0000249
nstraze3219812003-07-14 17:06:37 +0000250 /* cleanup and exit */
251 cleanup();
252
253 /*NOTREACHED*/
254 return 0;
255
256} /* End main */
257
258/* setup() - performs all ONE TIME setup for this test */
259void
260setup()
261{
262
263 /* capture signals */
264 tst_sig(FORK, DEF_HANDLER, cleanup);
265
266 /* Pause if that option was specified */
267 TEST_PAUSE;
268
269 /* Create temporary directory and 'cd' to it. */
270 tst_tmpdir();
271
272 /* Get unique file name for each child process */
mridgefc9dd8c2004-05-11 21:37:01 +0000273 if ((sprintf(file_name, "parent_file_%ld", syscall(__NR_gettid))) <= 0 ) {
nstraze3219812003-07-14 17:06:37 +0000274 tst_brkm(TBROK, cleanup, "sprintf() failed");
275 }
276
277} /* End setup() */
278
279
280/*
281 *cleanup() - performs all ONE TIME cleanup for this test at
282 * completion or premature exit.
283 */
284void
285cleanup()
286{
287
288 /*
289 * print timing stats if that option was specified.
290 * print errno log if that option was specified.
291 */
292 TEST_CLEANUP;
293
294 /* Remove temperory file */
295 if ((unlink(file_name)) == -1 ) {
296 tst_resm(TWARN, "Couldn't delete file, %s", file_name);
297 }
robbiewb8bd30c2003-07-31 20:41:11 +0000298 chdir("/tmp");
299 remove(cwd_parent);
300
nstraze3219812003-07-14 17:06:37 +0000301 /* exit with return code appropriate for results */
302 tst_exit();
303} /* End cleanup() */
304
305/*
306 * test_setup() - test specific setup function
307 */
308int
309test_setup()
310{
311
312 struct sigaction def_act;
313
314 /* Save current working directory of parent */
315 if ((getcwd(cwd_parent, sizeof(cwd_parent))) == NULL) {
316 tst_resm(TWARN, "getcwd() failed in test_setup()");
317 return 0;
318 }
319
320 /*
321 * Set value for parent_variable in parent, which will be
322 * changed by child in test_VM(), for testing CLONE_VM flag
323 */
324 parent_variable = PARENT_VALUE;
325
326 /*
327 * Open file from parent, which will be closed by
328 * child in test_FILES(), used for testing CLONE_FILES flag
329 */
330 if ((fd_parent = open(file_name, O_CREAT | O_RDWR)) == -1) {
331 tst_resm(TWARN, "open() failed in test_setup()");
332 return 0;
333 }
334
335 /*
336 * set parent_got_signal to FALSE, used for testing
337 * CLONE_SIGHAND flag
338 */
339 parent_got_signal = FALSE;
340
341 /* Setup signal handler for SIGUSR2 */
342 def_act.sa_handler = sig_default_handler;
343 def_act.sa_flags = SA_RESTART;
344
345 if ((sigaction(SIGUSR2, &def_act, NULL)) == -1) {
346 tst_resm(TWARN, "sigaction() failed in test_setup()\n");
347 return 0;
348 }
349
350 /* test_setup() returns success*/
351 return 1;
352}
353
354/*
355 * test_cleanup() - test specific cleanup function
356 */
357void
358test_cleanup()
359{
360
361 /* Restore parent's working directory */
362 if ((chdir(cwd_parent)) == -1) {
363 /* we have to exit here */
364 tst_brkm(TBROK, cleanup, "chdir() failed in test_cleanup()");
365 }
366
367}
368
369/*
370 * child_fn() - child function
371 */
372int
373child_fn()
374{
375
376 /* save child pid */
mridgefc9dd8c2004-05-11 21:37:01 +0000377 child_pid = syscall(__NR_gettid);
378
379 /*child_pid = getpid(); changed to above to work on POSIX threads -- NPTL */
nstraze3219812003-07-14 17:06:37 +0000380
381 if (test_VM() && test_FILES() && test_FS() && test_SIG()) {
robbiewb8bd30c2003-07-31 20:41:11 +0000382 exit(1);
nstraze3219812003-07-14 17:06:37 +0000383 }
robbiewb8bd30c2003-07-31 20:41:11 +0000384 exit(0);
nstraze3219812003-07-14 17:06:37 +0000385}
386
387/*
388 * parent_test1() - parent function for test1
389 */
390int
391parent_test1()
392{
393
394 /*
395 * For first test case (with all flags set), all resources are
396 * shared between parent and child. So whatever changes made by
397 * child should get reflected in parent also. modified_*()
398 * functions check this. All of them should return 1 for
399 * parent_test1() to return 1
400 */
401
402 if(modified_VM() && modified_FILES() && modified_FS() &&
403 modified_SIG()) {
404 return 1;
405 }
406 return 0;
407}
408
409/*
410 * parent_test2 - parent function for test 2
411 */
412int
413parent_test2()
414{
415
416 /*
417 * For second test case (with no resouce shared), all of the
418 * modified_*() functions should return 0 for parent_test2()
419 * to return 1
420 */
421
422 if ( modified_VM() || modified_FILES() || modified_FS() ||
423 modified_SIG()) {
424 return 0;
425 }
426 return 1;
427}
428
429/*
430 * test_VM() - function to change parent_variable from child's
431 * address space. If CLONE_VM flag is set, child shares
432 * the memory space with parent so this will be visible
433 * to parent also.
434 */
435
436int
437test_VM()
438{
439 parent_variable = CHILD_VALUE;
440 return 1;
441}
442
443/*
444 * test_FILES() - This function closes a file descriptor opened by
445 * parent. If CLONE_FILES flag is set, the parent and
446 * the child process share the same file descriptor
447 * table. so this affects the parent also
448 */
449int
450test_FILES()
451{
452 if ((close(fd_parent)) == -1) {
453 tst_resm(TWARN, "close() failed in test_FILES()");
454 return 0;
455 }
456 return 1;
457}
458
459/*
460 * test_FS() - This function changes the current working directory
461 * of the child process. If CLONE_FS flag is set, this
462 * will be visible to parent also.
463 */
464int
465test_FS()
466{
467 if((chdir("/tmp")) == -1) {
468 tst_resm(TWARN, "chdir() failed in test_FS()");
469 return 0;
470 }
471 return 1;
472}
473
474/*
475 * test_SIG() - This function changes the signal handler for SIGUSR2
476 * signal for child. If CLONE_SIGHAND flag is set, this
477 * affects parent also.
478 */
479int
480test_SIG()
481{
482
483 struct sigaction new_act;
484
485 new_act.sa_handler = sig_child_defined_handler;
486 new_act.sa_flags = SA_RESTART;
487
488 /* Set signal handler to sig_child_defined_handler */
489 if ((sigaction(SIGUSR2, &new_act, NULL)) == -1) {
490 tst_resm(TWARN, "signal() failed in test_SIG()");
491 return 0;
492 }
493
494 /* Send SIGUSR2 signal to parent */
495 if ((kill(getppid(), SIGUSR2)) == -1) {
496 tst_resm(TWARN, "kill() failed in test_SIG()");
497 return 0;
498 }
499 return 1;
500}
501
502/*
503 * modified_VM() - This function is called by parent process to check
504 * whether child's modification to parent_variable
505 * is visible to parent
506 */
507
508int
509modified_VM()
510{
511
512 if (parent_variable == CHILD_VALUE) {
513 /* child has modified parent_variable */
514 return 1;
515 }
516 return 0;
517}
518
519/*
520 * modified_FILES() - This function checks for file descriptor table
521 * modifications done by child
522 */
523int
524modified_FILES()
525{
526 char buff[20];
527
528 if (((read(fd_parent, buff, sizeof(buff))) == -1) &&
529 (errno == EBADF)) {
530 /* Child has closed this file descriptor */
531 return 1;
532 }
533
534 /* close fd_parent*/
535 if ((close(fd_parent)) == -1) {
536 tst_resm(TWARN, "close() failed in modified_FILES()");
537 }
538
539 return 0;
540}
541
542/*
543 * modified_FS() - This function checks parent's current working directory
544 * to see whether its modified by child or not.
545 */
546int
547modified_FS()
548{
549 char cwd[FILENAME_MAX];
550
551 if ((getcwd(cwd, sizeof(cwd))) == NULL) {
552 tst_resm(TWARN, "getcwd() failed \n");
553 }
554
555 if ( !(strcmp(cwd, cwd_parent) )) {
556 /* cwd hasn't changed */
557 return 0;
558 }
559 return 1;
560}
561
562/*
563 * modified_SIG() - This function checks whether child has changed
564 * parent's signal handler for signal, SIGUSR2
565 */
566int
567modified_SIG()
568{
569
570 if (parent_got_signal) {
571 /*
572 * parent came through sig_child_defined_handler()
573 * this means child has changed parent's handler
574 */
575 return 1;
576 }
577 return 0;
578}
579
580/*
581 * sig_child_defined_handler() - Signal handler installed by child
582 */
583void
584sig_child_defined_handler(int pid)
585{
mridgefc9dd8c2004-05-11 21:37:01 +0000586 if((syscall(__NR_gettid)) == child_pid) {
nstraze3219812003-07-14 17:06:37 +0000587 /* Child got signal, give warning */
588 tst_resm(TWARN, "Child got SIGUSR2 signal");
589 } else {
590 parent_got_signal = TRUE;
591 }
592}
593
594/* sig_default_handler() - Default handler for parent */
595void
596sig_default_handler()
597{
598}