s3/mdssvc: add option "elasticsearch:force_substring_search = yes | no" (default...
[samba.git] / lib / util / tests / tfork.c
blob70ae97583fca26fd384c3e1bd46c9915bf9cfdc8
1 /*
2 * Tests for tfork
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/>.
20 #include "replace.h"
21 #include <talloc.h>
22 #include <tevent.h>
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"
33 #ifdef HAVE_PTHREAD
34 #include <pthread.h>
35 #endif
37 static bool test_tfork_simple(struct torture_context *tctx)
39 pid_t parent = getpid();
40 struct tfork *t = NULL;
41 pid_t child;
42 int ret;
44 t = tfork_create();
45 if (t == NULL) {
46 torture_fail(tctx, "tfork failed\n");
47 return false;
49 child = tfork_child_pid(t);
50 if (child == 0) {
51 torture_comment(tctx, "my parent pid is %d\n", parent);
52 torture_assert(tctx, getpid() != parent, "tfork failed\n");
53 _exit(0);
56 ret = tfork_destroy(&t);
57 torture_assert(tctx, ret == 0, "tfork_destroy failed\n");
59 return true;
62 static bool test_tfork_status(struct torture_context *tctx)
64 struct tfork *t = NULL;
65 int status;
66 pid_t child;
67 bool ok = true;
69 t = tfork_create();
70 if (t == NULL) {
71 torture_fail(tctx, "tfork failed\n");
72 return false;
74 child = tfork_child_pid(t);
75 if (child == 0) {
76 _exit(123);
79 status = tfork_status(&t, true);
80 if (status == -1) {
81 torture_fail(tctx, "tfork_status failed\n");
84 torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
85 "tfork failed\n");
86 torture_assert_goto(tctx, WEXITSTATUS(status) == 123, ok, done,
87 "tfork failed\n");
89 torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status));
91 done:
92 return ok;
95 static bool test_tfork_sigign(struct torture_context *tctx)
97 struct tfork *t = NULL;
98 struct sigaction act;
99 pid_t child;
100 int status;
101 bool ok = true;
102 int ret;
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");
112 t = tfork_create();
113 if (t == NULL) {
114 torture_fail(tctx, "tfork failed\n");
115 return false;
117 child = tfork_child_pid(t);
118 if (child == 0) {
119 sleep(1);
120 _exit(123);
123 child = fork();
124 if (child == -1) {
125 torture_fail(tctx, "fork failed\n");
126 return false;
128 if (child == 0) {
129 _exit(0);
132 status = tfork_status(&t, true);
133 if (status == -1) {
134 torture_fail(tctx, "tfork_status failed\n");
137 torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
138 "tfork failed\n");
139 torture_assert_goto(tctx, WEXITSTATUS(status) == 123, ok, done,
140 "tfork failed\n");
141 torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status));
143 done:
144 return ok;
147 static void sigchld_handler1(int signum, siginfo_t *si, void *u)
149 pid_t pid;
150 int status;
152 if (signum != SIGCHLD) {
153 abort();
156 pid = waitpid(si->si_pid, &status, 0);
157 if (pid != si->si_pid) {
158 abort();
162 static bool test_tfork_sighandler(struct torture_context *tctx)
164 struct tfork *t = NULL;
165 struct sigaction act;
166 struct sigaction oldact;
167 pid_t child;
168 int status;
169 bool ok = true;
170 int ret;
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");
180 t = tfork_create();
181 if (t == NULL) {
182 torture_fail(tctx, "tfork failed\n");
183 return false;
185 child = tfork_child_pid(t);
186 if (child == 0) {
187 sleep(1);
188 _exit(123);
191 child = fork();
192 if (child == -1) {
193 torture_fail(tctx, "fork failed\n");
194 return false;
196 if (child == 0) {
197 _exit(0);
200 status = tfork_status(&t, true);
201 if (status == -1) {
202 torture_fail(tctx, "tfork_status failed\n");
205 torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
206 "tfork failed\n");
207 torture_assert_goto(tctx, WEXITSTATUS(status) == 123, ok, done,
208 "tfork failed\n");
209 torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status));
211 done:
212 sigaction(SIGCHLD, &oldact, NULL);
214 return ok;
217 static bool test_tfork_process_hierarchy(struct torture_context *tctx)
219 struct tfork *t = NULL;
220 pid_t pid = getpid();
221 pid_t child;
222 pid_t pgid = getpgid(0);
223 pid_t sid = getsid(0);
224 char *procpath = NULL;
225 int status;
226 struct stat st;
227 int ret;
228 bool ok = true;
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);
235 if (ret != 0) {
236 if (errno == ENOENT) {
237 torture_skip(tctx, "/proc missing\n");
239 torture_fail(tctx, "stat failed\n");
242 t = tfork_create();
243 if (t == NULL) {
244 torture_fail(tctx, "tfork failed\n");
245 return false;
247 child = tfork_child_pid(t);
248 if (child == 0) {
249 char *cmd = NULL;
250 FILE *fp = NULL;
251 char line[64];
252 char *p;
253 pid_t ppid;
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);
265 pclose(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");
272 _exit(0);
274 child_fail:
275 _exit(1);
278 status = tfork_status(&t, true);
279 if (status == -1) {
280 torture_fail(tctx, "tfork_status failed\n");
283 torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
284 "tfork failed\n");
285 torture_assert_goto(tctx, WEXITSTATUS(status) == 0, ok, done,
286 "tfork failed\n");
287 torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status));
289 done:
290 return ok;
293 static bool test_tfork_pipe(struct torture_context *tctx)
295 struct tfork *t = NULL;
296 int status;
297 pid_t child;
298 int up[2];
299 int down[2];
300 char c;
301 int ret;
302 bool ok = true;
304 ret = pipe(&up[0]);
305 torture_assert(tctx, ret == 0, "pipe failed\n");
307 ret = pipe(&down[0]);
308 torture_assert(tctx, ret == 0, "pipe failed\n");
310 t = tfork_create();
311 if (t == NULL) {
312 torture_fail(tctx, "tfork failed\n");
313 return false;
315 child = tfork_child_pid(t);
316 if (child == 0) {
317 close(up[0]);
318 close(down[1]);
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");
327 _exit(0);
329 child_fail:
330 _exit(1);
333 close(up[1]);
334 close(down[0]);
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);
344 if (status == -1) {
345 torture_fail(tctx, "tfork_status failed\n");
348 torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
349 "tfork failed\n");
350 torture_assert_goto(tctx, WEXITSTATUS(status) == 0, ok, done,
351 "tfork failed\n");
352 done:
353 return ok;
356 static bool test_tfork_twice(struct torture_context *tctx)
358 struct tfork *t = NULL;
359 int status;
360 pid_t child;
361 pid_t pid;
362 int up[2];
363 int ret;
364 bool ok = true;
366 ret = pipe(&up[0]);
367 torture_assert(tctx, ret == 0, "pipe failed\n");
369 t = tfork_create();
370 if (t == NULL) {
371 torture_fail(tctx, "tfork failed\n");
372 return false;
374 child = tfork_child_pid(t);
375 if (child == 0) {
376 close(up[0]);
378 t = tfork_create();
379 if (t == NULL) {
380 torture_fail(tctx, "tfork failed\n");
381 return false;
383 child = tfork_child_pid(t);
384 if (child == 0) {
385 sleep(1);
386 pid = getpid();
387 ret = write(up[1], &pid, sizeof(pid_t));
388 torture_assert_goto(tctx, ret == sizeof(pid_t), ok, child_fail, "write failed\n");
390 _exit(0);
392 child_fail:
393 _exit(1);
396 _exit(0);
399 close(up[1]);
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,
408 "tfork failed\n");
409 torture_assert_goto(tctx, WEXITSTATUS(status) == 0, ok, done,
410 "tfork failed\n");
411 done:
412 return ok;
415 static void *tfork_thread(void *p)
417 struct tfork *t = NULL;
418 int status;
419 pid_t child;
420 uint64_t tid = (uint64_t)pthread_self();
421 uint64_t *result = NULL;
422 int up[2];
423 ssize_t nread;
424 int ret;
426 ret = pipe(up);
427 if (ret != 0) {
428 pthread_exit(NULL);
431 t = tfork_create();
432 if (t == NULL) {
433 pthread_exit(NULL);
435 child = tfork_child_pid(t);
436 if (child == 0) {
437 ssize_t nwritten;
439 close(up[0]);
440 tid++;
441 nwritten = sys_write(up[1], &tid, sizeof(uint64_t));
442 if (nwritten != sizeof(uint64_t)) {
443 _exit(1);
445 _exit(0);
447 close(up[1]);
449 result = malloc(sizeof(uint64_t));
450 if (result == NULL) {
451 pthread_exit(NULL);
454 nread = sys_read(up[0], result, sizeof(uint64_t));
455 if (nread != sizeof(uint64_t)) {
456 pthread_exit(NULL);
459 status = tfork_status(&t, true);
460 if (status == -1) {
461 pthread_exit(NULL);
464 pthread_exit(result);
467 static bool test_tfork_threads(struct torture_context *tctx)
469 int ret;
470 bool ok = true;
471 const int num_threads = 64;
472 pthread_t threads[num_threads];
473 sigset_t set;
474 int i;
476 #ifndef HAVE_PTHREAD
477 torture_skip(tctx, "no pthread support\n");
478 #endif
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. :)
485 sigemptyset(&set);
486 sigaddset(&set, SIGCHLD);
487 #ifdef HAVE_PTHREAD
488 ret = pthread_sigmask(SIG_UNBLOCK, &set, NULL);
489 #else
490 ret = sigprocmask(SIG_UNBLOCK, &set, NULL);
491 #endif
492 if (ret != 0) {
493 return false;
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++) {
503 void *p;
504 uint64_t *result;
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");
512 free(p);
515 done:
516 return ok;
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 };
524 bool ok = true;
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,
535 cmd, "foo", NULL);
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");
544 done:
545 TALLOC_FREE(ev);
547 return ok;
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)
556 bool ok = true;
558 struct tfork *t1 = NULL;
559 pid_t child1;
560 struct pollfd poll1[] = {
562 .fd = -1,
563 .events = POLLIN,
567 struct tfork *t2 = NULL;
568 pid_t child2;
569 struct pollfd poll2[] = {
571 .fd = -1,
572 .events = POLLIN,
577 t1 = tfork_create();
578 if (t1 == NULL) {
579 torture_fail(tctx, "tfork failed\n");
580 return false;
583 child1 = tfork_child_pid(t1);
584 if (child1 == 0) {
586 * Parent process will kill this with a SIGTERM
587 * so 10 seconds should be plenty
589 sleep(10);
590 exit(1);
592 poll1[0].fd = tfork_event_fd(t1);
594 t2 = tfork_create();
595 if (t2 == NULL) {
596 torture_fail(tctx, "tfork failed\n");
597 return false;
599 child2 = tfork_child_pid(t2);
600 if (child2 == 0) {
602 * Parent process will kill this with a SIGTERM
603 * so 10 seconds should be plenty
605 sleep(10);
606 exit(2);
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
614 poll(poll1, 1, 0);
615 ok = !(poll1[0].revents & POLLIN);
616 torture_assert_goto(tctx, ok, ok, done,
617 "tfork process 1 event fd readable\n");
618 poll(poll2, 1, 0);
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);
625 sleep(1);
628 * Have killed the first child, so expect it's event_fd to have gone
629 * readable.
632 poll(poll1, 1, 0);
633 ok = (poll1[0].revents & POLLIN);
634 torture_assert_goto(tctx, ok, ok, done,
635 "tfork process 1 event fd not readable\n");
636 poll(poll2, 1, 0);
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);
643 sleep(1);
645 * Have killed the children, so expect their event_fd's to have gone
646 * readable.
649 poll(poll1, 1, 0);
650 ok = (poll1[0].revents & POLLIN);
651 torture_assert_goto(tctx, ok, ok, done,
652 "tfork process 1 event fd not readable\n");
653 poll(poll2, 1, 0);
654 ok = (poll2[0].revents & POLLIN);
655 torture_assert_goto(tctx, ok, ok, done,
656 "tfork process 2 event fd not readable\n");
658 done:
659 free(t1);
660 free(t2);
662 return ok;
666 * Test to ensure that the status calls behave as expected after a process
667 * terminates.
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)
679 bool ok = true;
681 struct tfork *t1 = NULL;
682 pid_t child1;
684 struct tfork *t2 = NULL;
685 pid_t child2;
687 int status;
688 int fd;
689 int ev1_fd;
690 int ev2_fd;
693 t1 = tfork_create();
694 if (t1 == NULL) {
695 torture_fail(tctx, "tfork failed\n");
696 return false;
699 child1 = tfork_child_pid(t1);
700 if (child1 == 0) {
702 * Parent process will kill this with a SIGTERM
703 * so 10 seconds should be plenty
705 sleep(10);
706 exit(1);
708 ev1_fd = tfork_event_fd(t1);
710 t2 = tfork_create();
711 if (t2 == NULL) {
712 torture_fail(tctx, "tfork failed\n");
713 return false;
715 child2 = tfork_child_pid(t2);
716 if (child2 == 0) {
718 * Parent process will kill this with a SIGTERM
719 * so 10 seconds should be plenty
721 sleep(10);
722 exit(2);
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);
733 ok = status == -1;
734 torture_assert_goto(tctx, ok, ok, done,
735 "tfork status available for non terminated "
736 "process 1\n");
737 /* Is the event fd open? */
738 fd = dup(ev1_fd);
739 ok = fd != -1;
740 torture_assert_goto(tctx, ok, ok, done,
741 "tfork process 1 event fd is not open");
743 status = tfork_status(&t2, false);
744 ok = status == -1;
745 torture_assert_goto(tctx, ok, ok, done,
746 "tfork status available for non terminated "
747 "process 2\n");
748 /* Is the event fd open? */
749 fd = dup(ev2_fd);
750 ok = fd != -1;
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);
760 sleep(1);
761 status = tfork_status(&t1, false);
762 ok = status != -1;
763 torture_assert_goto(tctx, ok, ok, done,
764 "tfork status for child 1 not available after "
765 "termination\n");
766 /* Is the event fd open? */
767 fd = dup(ev2_fd);
768 ok = fd != -1;
769 torture_assert_goto(tctx, ok, ok, done,
770 "tfork process 1 event fd is not open");
772 status = tfork_status(&t2, false);
773 ok = status == -1;
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);
782 sleep(1);
783 status = tfork_status(&t2, false);
784 ok = status != -1;
785 torture_assert_goto(tctx, ok, ok, done,
786 "tfork status for child 2 not available after "
787 "termination\n");
789 /* Check that the event fd's are still open */
790 /* Is the event fd open? */
791 fd = dup(ev1_fd);
792 ok = fd != -1;
793 torture_assert_goto(tctx, ok, ok, done,
794 "tfork process 1 event fd is not open");
795 /* Is the event fd open? */
796 fd = dup(ev2_fd);
797 ok = fd != -1;
798 torture_assert_goto(tctx, ok, ok, done,
799 "tfork process 2 event fd is not open");
801 done:
802 return ok;
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,
811 "tfork_simple",
812 test_tfork_simple);
814 torture_suite_add_simple_test(suite,
815 "tfork_status",
816 test_tfork_status);
818 torture_suite_add_simple_test(suite,
819 "tfork_sigign",
820 test_tfork_sigign);
822 torture_suite_add_simple_test(suite,
823 "tfork_sighandler",
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,
831 "tfork_pipe",
832 test_tfork_pipe);
834 torture_suite_add_simple_test(suite,
835 "tfork_twice",
836 test_tfork_twice);
838 torture_suite_add_simple_test(suite,
839 "tfork_threads",
840 test_tfork_threads);
842 torture_suite_add_simple_test(suite,
843 "tfork_cmd_send",
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);
854 return suite;