4 * Copyright Ralph Boehme <slow@samba.org> 2017
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "system/filesys.h"
24 #include "system/wait.h"
25 #include "system/select.h"
26 #include "libcli/util/ntstatus.h"
27 #include "torture/torture.h"
28 #include "lib/util/data_blob.h"
29 #include "torture/local/proto.h"
30 #include "lib/util/tfork.h"
31 #include "lib/util/samba_util.h"
32 #include "lib/util/sys_rw.h"
37 static bool test_tfork_simple(struct torture_context
*tctx
)
39 pid_t parent
= getpid();
40 struct tfork
*t
= NULL
;
46 torture_fail(tctx
, "tfork failed\n");
49 child
= tfork_child_pid(t
);
51 torture_comment(tctx
, "my parent pid is %d\n", parent
);
52 torture_assert(tctx
, getpid() != parent
, "tfork failed\n");
56 ret
= tfork_destroy(&t
);
57 torture_assert(tctx
, ret
== 0, "tfork_destroy failed\n");
62 static bool test_tfork_status(struct torture_context
*tctx
)
64 struct tfork
*t
= NULL
;
71 torture_fail(tctx
, "tfork failed\n");
74 child
= tfork_child_pid(t
);
79 status
= tfork_status(&t
, true);
81 torture_fail(tctx
, "tfork_status failed\n");
84 torture_assert_goto(tctx
, WIFEXITED(status
) == true, ok
, done
,
86 torture_assert_goto(tctx
, WEXITSTATUS(status
) == 123, ok
, done
,
89 torture_comment(tctx
, "exit status [%d]\n", WEXITSTATUS(status
));
95 static bool test_tfork_sigign(struct torture_context
*tctx
)
97 struct tfork
*t
= NULL
;
104 act
= (struct sigaction
) {
105 .sa_flags
= SA_NOCLDWAIT
,
106 .sa_handler
= SIG_IGN
,
109 ret
= sigaction(SIGCHLD
, &act
, NULL
);
110 torture_assert_goto(tctx
, ret
== 0, ok
, done
, "sigaction failed\n");
114 torture_fail(tctx
, "tfork failed\n");
117 child
= tfork_child_pid(t
);
125 torture_fail(tctx
, "fork failed\n");
132 status
= tfork_status(&t
, true);
134 torture_fail(tctx
, "tfork_status failed\n");
137 torture_assert_goto(tctx
, WIFEXITED(status
) == true, ok
, done
,
139 torture_assert_goto(tctx
, WEXITSTATUS(status
) == 123, ok
, done
,
141 torture_comment(tctx
, "exit status [%d]\n", WEXITSTATUS(status
));
147 static void sigchld_handler1(int signum
, siginfo_t
*si
, void *u
)
152 if (signum
!= SIGCHLD
) {
156 pid
= waitpid(si
->si_pid
, &status
, 0);
157 if (pid
!= si
->si_pid
) {
162 static bool test_tfork_sighandler(struct torture_context
*tctx
)
164 struct tfork
*t
= NULL
;
165 struct sigaction act
;
166 struct sigaction oldact
;
172 act
= (struct sigaction
) {
173 .sa_flags
= SA_SIGINFO
,
174 .sa_sigaction
= sigchld_handler1
,
177 ret
= sigaction(SIGCHLD
, &act
, &oldact
);
178 torture_assert_goto(tctx
, ret
== 0, ok
, done
, "sigaction failed\n");
182 torture_fail(tctx
, "tfork failed\n");
185 child
= tfork_child_pid(t
);
193 torture_fail(tctx
, "fork failed\n");
200 status
= tfork_status(&t
, true);
202 torture_fail(tctx
, "tfork_status failed\n");
205 torture_assert_goto(tctx
, WIFEXITED(status
) == true, ok
, done
,
207 torture_assert_goto(tctx
, WEXITSTATUS(status
) == 123, ok
, done
,
209 torture_comment(tctx
, "exit status [%d]\n", WEXITSTATUS(status
));
212 sigaction(SIGCHLD
, &oldact
, NULL
);
217 static bool test_tfork_process_hierarchy(struct torture_context
*tctx
)
219 struct tfork
*t
= NULL
;
220 pid_t pid
= getpid();
222 pid_t pgid
= getpgid(0);
223 pid_t sid
= getsid(0);
224 char *procpath
= NULL
;
230 procpath
= talloc_asprintf(tctx
, "/proc/%d/status", getpid());
231 torture_assert_not_null(tctx
, procpath
, "talloc_asprintf failed\n");
233 ret
= stat(procpath
, &st
);
234 TALLOC_FREE(procpath
);
236 if (errno
== ENOENT
) {
237 torture_skip(tctx
, "/proc missing\n");
239 torture_fail(tctx
, "stat failed\n");
244 torture_fail(tctx
, "tfork failed\n");
247 child
= tfork_child_pid(t
);
255 torture_assert_goto(tctx
, pgid
== getpgid(0), ok
, child_fail
, "tfork failed\n");
256 torture_assert_goto(tctx
, sid
== getsid(0), ok
, child_fail
, "tfork failed\n");
258 cmd
= talloc_asprintf(tctx
, "cat /proc/%d/status | awk '/^PPid:/ {print $2}'", getppid());
259 torture_assert_goto(tctx
, cmd
!= NULL
, ok
, child_fail
, "talloc_asprintf failed\n");
261 fp
= popen(cmd
, "r");
262 torture_assert_goto(tctx
, fp
!= NULL
, ok
, child_fail
, "popen failed\n");
264 p
= fgets(line
, sizeof(line
) - 1, fp
);
266 torture_assert_goto(tctx
, p
!= NULL
, ok
, child_fail
, "popen failed\n");
268 ret
= sscanf(line
, "%d", &ppid
);
269 torture_assert_goto(tctx
, ret
== 1, ok
, child_fail
, "sscanf failed\n");
270 torture_assert_goto(tctx
, ppid
== pid
, ok
, child_fail
, "process hierarchy not rooted at caller\n");
278 status
= tfork_status(&t
, true);
280 torture_fail(tctx
, "tfork_status failed\n");
283 torture_assert_goto(tctx
, WIFEXITED(status
) == true, ok
, done
,
285 torture_assert_goto(tctx
, WEXITSTATUS(status
) == 0, ok
, done
,
287 torture_comment(tctx
, "exit status [%d]\n", WEXITSTATUS(status
));
293 static bool test_tfork_pipe(struct torture_context
*tctx
)
295 struct tfork
*t
= NULL
;
305 torture_assert(tctx
, ret
== 0, "pipe failed\n");
307 ret
= pipe(&down
[0]);
308 torture_assert(tctx
, ret
== 0, "pipe failed\n");
312 torture_fail(tctx
, "tfork failed\n");
315 child
= tfork_child_pid(t
);
320 ret
= read(down
[0], &c
, 1);
321 torture_assert_goto(tctx
, ret
== 1, ok
, child_fail
, "read failed\n");
322 torture_assert_goto(tctx
, c
== 1, ok
, child_fail
, "read failed\n");
324 ret
= write(up
[1], &(char){2}, 1);
325 torture_assert_goto(tctx
, ret
== 1, ok
, child_fail
, "write failed\n");
336 ret
= write(down
[1], &(char){1}, 1);
337 torture_assert(tctx
, ret
== 1, "read failed\n");
339 ret
= read(up
[0], &c
, 1);
340 torture_assert(tctx
, ret
== 1, "read failed\n");
341 torture_assert(tctx
, c
== 2, "read failed\n");
343 status
= tfork_status(&t
, true);
345 torture_fail(tctx
, "tfork_status failed\n");
348 torture_assert_goto(tctx
, WIFEXITED(status
) == true, ok
, done
,
350 torture_assert_goto(tctx
, WEXITSTATUS(status
) == 0, ok
, done
,
356 static bool test_tfork_twice(struct torture_context
*tctx
)
358 struct tfork
*t
= NULL
;
367 torture_assert(tctx
, ret
== 0, "pipe failed\n");
371 torture_fail(tctx
, "tfork failed\n");
374 child
= tfork_child_pid(t
);
380 torture_fail(tctx
, "tfork failed\n");
383 child
= tfork_child_pid(t
);
387 ret
= write(up
[1], &pid
, sizeof(pid_t
));
388 torture_assert_goto(tctx
, ret
== sizeof(pid_t
), ok
, child_fail
, "write failed\n");
401 ret
= read(up
[0], &pid
, sizeof(pid_t
));
402 torture_assert(tctx
, ret
== sizeof(pid_t
), "read failed\n");
404 status
= tfork_status(&t
, true);
405 torture_assert_goto(tctx
, status
!= -1, ok
, done
, "tfork_status failed\n");
407 torture_assert_goto(tctx
, WIFEXITED(status
) == true, ok
, done
,
409 torture_assert_goto(tctx
, WEXITSTATUS(status
) == 0, ok
, done
,
415 static void *tfork_thread(void *p
)
417 struct tfork
*t
= NULL
;
420 uint64_t tid
= (uint64_t)pthread_self();
421 uint64_t *result
= NULL
;
435 child
= tfork_child_pid(t
);
441 nwritten
= sys_write(up
[1], &tid
, sizeof(uint64_t));
442 if (nwritten
!= sizeof(uint64_t)) {
449 result
= malloc(sizeof(uint64_t));
450 if (result
== NULL
) {
454 nread
= sys_read(up
[0], result
, sizeof(uint64_t));
455 if (nread
!= sizeof(uint64_t)) {
459 status
= tfork_status(&t
, true);
464 pthread_exit(result
);
467 static bool test_tfork_threads(struct torture_context
*tctx
)
471 const int num_threads
= 64;
472 pthread_t threads
[num_threads
];
477 torture_skip(tctx
, "no pthread support\n");
481 * Be nasty and taste for the worst case: ensure all threads start with
482 * SIGCHLD unblocked so we have the most fun with SIGCHLD being
483 * delivered to a random thread. :)
486 sigaddset(&set
, SIGCHLD
);
488 ret
= pthread_sigmask(SIG_UNBLOCK
, &set
, NULL
);
490 ret
= sigprocmask(SIG_UNBLOCK
, &set
, NULL
);
496 for (i
= 0; i
< num_threads
; i
++) {
497 ret
= pthread_create(&threads
[i
], NULL
, tfork_thread
, NULL
);
498 torture_assert_goto(tctx
, ret
== 0, ok
, done
,
499 "pthread_create failed\n");
502 for (i
= 0; i
< num_threads
; i
++) {
506 ret
= pthread_join(threads
[i
], &p
);
507 torture_assert_goto(tctx
, ret
== 0, ok
, done
,
508 "pthread_join failed\n");
509 result
= (uint64_t *)p
;
510 torture_assert_goto(tctx
, *result
== (uint64_t)threads
[i
] + 1,
511 ok
, done
, "thread failed\n");
519 static bool test_tfork_cmd_send(struct torture_context
*tctx
)
521 struct tevent_context
*ev
= NULL
;
522 struct tevent_req
*req
= NULL
;
523 const char *cmd
[2] = { NULL
, NULL
};
526 ev
= tevent_context_init(tctx
);
527 torture_assert_goto(tctx
, ev
!= NULL
, ok
, done
,
528 "tevent_context_init failed\n");
530 cmd
[0] = talloc_asprintf(tctx
, "%s/testprogs/blackbox/tfork.sh", SRCDIR
);
531 torture_assert_goto(tctx
, cmd
[0] != NULL
, ok
, done
,
532 "talloc_asprintf failed\n");
534 req
= samba_runcmd_send(tctx
, ev
, timeval_zero(), 0, 0,
536 torture_assert_goto(tctx
, req
!= NULL
, ok
, done
,
537 "samba_runcmd_send failed\n");
539 ok
= tevent_req_poll(req
, ev
);
540 torture_assert_goto(tctx
, ok
, ok
, done
, "tevent_req_poll failed\n");
542 torture_comment(tctx
, "samba_runcmd_send test finished\n");
551 * Test to ensure that the event_fd becomes readable after
552 * a tfork_process terminates.
554 static bool test_tfork_event_file_handle(struct torture_context
*tctx
)
558 struct tfork
*t1
= NULL
;
560 struct pollfd poll1
[] = {
567 struct tfork
*t2
= NULL
;
569 struct pollfd poll2
[] = {
579 torture_fail(tctx
, "tfork failed\n");
583 child1
= tfork_child_pid(t1
);
586 * Parent process will kill this with a SIGTERM
587 * so 10 seconds should be plenty
592 poll1
[0].fd
= tfork_event_fd(t1
);
596 torture_fail(tctx
, "tfork failed\n");
599 child2
= tfork_child_pid(t2
);
602 * Parent process will kill this with a SIGTERM
603 * so 10 seconds should be plenty
608 poll2
[0].fd
= tfork_event_fd(t2
);
611 * Have forked two process and are in the master process
612 * Expect that both event_fds are unreadable
615 ok
= !(poll1
[0].revents
& POLLIN
);
616 torture_assert_goto(tctx
, ok
, ok
, done
,
617 "tfork process 1 event fd readable\n");
619 ok
= !(poll2
[0].revents
& POLLIN
);
620 torture_assert_goto(tctx
, ok
, ok
, done
,
621 "tfork process 1 event fd readable\n");
623 /* Kill the first child process */
624 kill(child1
, SIGKILL
);
628 * Have killed the first child, so expect it's event_fd to have gone
633 ok
= (poll1
[0].revents
& POLLIN
);
634 torture_assert_goto(tctx
, ok
, ok
, done
,
635 "tfork process 1 event fd not readable\n");
637 ok
= !(poll2
[0].revents
& POLLIN
);
638 torture_assert_goto(tctx
, ok
, ok
, done
,
639 "tfork process 2 event fd readable\n");
641 /* Kill the secind child process */
642 kill(child2
, SIGKILL
);
645 * Have killed the children, so expect their event_fd's to have gone
650 ok
= (poll1
[0].revents
& POLLIN
);
651 torture_assert_goto(tctx
, ok
, ok
, done
,
652 "tfork process 1 event fd not readable\n");
654 ok
= (poll2
[0].revents
& POLLIN
);
655 torture_assert_goto(tctx
, ok
, ok
, done
,
656 "tfork process 2 event fd not readable\n");
666 * Test to ensure that the status calls behave as expected after a process
669 * As the parent process owns the status fd's they get passed to all
670 * subsequent children after a tfork. So it's possible for another
671 * child process to hold the status pipe open.
673 * The event fd needs to be left open by tfork, as a close in the status
674 * code can cause issues in tevent code.
677 static bool test_tfork_status_handle(struct torture_context
*tctx
)
681 struct tfork
*t1
= NULL
;
684 struct tfork
*t2
= NULL
;
695 torture_fail(tctx
, "tfork failed\n");
699 child1
= tfork_child_pid(t1
);
702 * Parent process will kill this with a SIGTERM
703 * so 10 seconds should be plenty
708 ev1_fd
= tfork_event_fd(t1
);
712 torture_fail(tctx
, "tfork failed\n");
715 child2
= tfork_child_pid(t2
);
718 * Parent process will kill this with a SIGTERM
719 * so 10 seconds should be plenty
724 ev2_fd
= tfork_event_fd(t2
);
727 * Have forked two process and are in the master process
728 * expect that the status call will block, and hence return -1
729 * as the processes are still running
730 * The event fd's should be open.
732 status
= tfork_status(&t1
, false);
734 torture_assert_goto(tctx
, ok
, ok
, done
,
735 "tfork status available for non terminated "
737 /* Is the event fd open? */
740 torture_assert_goto(tctx
, ok
, ok
, done
,
741 "tfork process 1 event fd is not open");
743 status
= tfork_status(&t2
, false);
745 torture_assert_goto(tctx
, ok
, ok
, done
,
746 "tfork status available for non terminated "
748 /* Is the event fd open? */
751 torture_assert_goto(tctx
, ok
, ok
, done
,
752 "tfork process 2 event fd is not open");
755 * Kill the first process, it's status should be readable
756 * and it's event_fd should be open
757 * The second process's status should be unreadable.
759 kill(child1
, SIGTERM
);
761 status
= tfork_status(&t1
, false);
763 torture_assert_goto(tctx
, ok
, ok
, done
,
764 "tfork status for child 1 not available after "
766 /* Is the event fd open? */
769 torture_assert_goto(tctx
, ok
, ok
, done
,
770 "tfork process 1 event fd is not open");
772 status
= tfork_status(&t2
, false);
774 torture_assert_goto(tctx
, ok
, ok
, done
,
775 "tfork status available for child 2 after "
776 "termination of child 1\n");
779 * Kill the second process, it's status should be readable
781 kill(child2
, SIGTERM
);
783 status
= tfork_status(&t2
, false);
785 torture_assert_goto(tctx
, ok
, ok
, done
,
786 "tfork status for child 2 not available after "
789 /* Check that the event fd's are still open */
790 /* Is the event fd open? */
793 torture_assert_goto(tctx
, ok
, ok
, done
,
794 "tfork process 1 event fd is not open");
795 /* Is the event fd open? */
798 torture_assert_goto(tctx
, ok
, ok
, done
,
799 "tfork process 2 event fd is not open");
805 struct torture_suite
*torture_local_tfork(TALLOC_CTX
*mem_ctx
)
807 struct torture_suite
*suite
=
808 torture_suite_create(mem_ctx
, "tfork");
810 torture_suite_add_simple_test(suite
,
814 torture_suite_add_simple_test(suite
,
818 torture_suite_add_simple_test(suite
,
822 torture_suite_add_simple_test(suite
,
824 test_tfork_sighandler
);
826 torture_suite_add_simple_test(suite
,
827 "tfork_process_hierarchy",
828 test_tfork_process_hierarchy
);
830 torture_suite_add_simple_test(suite
,
834 torture_suite_add_simple_test(suite
,
838 torture_suite_add_simple_test(suite
,
842 torture_suite_add_simple_test(suite
,
844 test_tfork_cmd_send
);
846 torture_suite_add_simple_test(suite
,
847 "tfork_event_file_handle",
848 test_tfork_event_file_handle
);
850 torture_suite_add_simple_test(suite
,
851 "tfork_status_handle",
852 test_tfork_status_handle
);