Line data Source code
1 : /*
2 : * Tests for tfork
3 : *
4 : * Copyright Ralph Boehme <slow@samba.org> 2017
5 : *
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.
10 : *
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.
15 : *
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/>.
18 : */
19 :
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
36 :
37 1 : static bool test_tfork_simple(struct torture_context *tctx)
38 : {
39 1 : pid_t parent = getpid();
40 1 : struct tfork *t = NULL;
41 1 : pid_t child;
42 1 : int ret;
43 :
44 1 : t = tfork_create();
45 1 : if (t == NULL) {
46 0 : torture_fail(tctx, "tfork failed\n");
47 : return false;
48 : }
49 1 : child = tfork_child_pid(t);
50 1 : if (child == 0) {
51 0 : torture_comment(tctx, "my parent pid is %d\n", parent);
52 0 : torture_assert(tctx, getpid() != parent, "tfork failed\n");
53 0 : _exit(0);
54 : }
55 :
56 1 : ret = tfork_destroy(&t);
57 1 : torture_assert(tctx, ret == 0, "tfork_destroy failed\n");
58 :
59 0 : return true;
60 : }
61 :
62 1 : static bool test_tfork_status(struct torture_context *tctx)
63 : {
64 1 : struct tfork *t = NULL;
65 1 : int status;
66 1 : pid_t child;
67 1 : bool ok = true;
68 :
69 1 : t = tfork_create();
70 1 : if (t == NULL) {
71 0 : torture_fail(tctx, "tfork failed\n");
72 : return false;
73 : }
74 1 : child = tfork_child_pid(t);
75 1 : if (child == 0) {
76 0 : _exit(123);
77 : }
78 :
79 1 : status = tfork_status(&t, true);
80 1 : if (status == -1) {
81 0 : torture_fail(tctx, "tfork_status failed\n");
82 : }
83 :
84 1 : torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
85 : "tfork failed\n");
86 1 : torture_assert_goto(tctx, WEXITSTATUS(status) == 123, ok, done,
87 : "tfork failed\n");
88 :
89 1 : torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status));
90 :
91 0 : done:
92 0 : return ok;
93 : }
94 :
95 1 : static bool test_tfork_sigign(struct torture_context *tctx)
96 : {
97 1 : struct tfork *t = NULL;
98 1 : struct sigaction act;
99 1 : pid_t child;
100 1 : int status;
101 1 : bool ok = true;
102 1 : int ret;
103 :
104 1 : act = (struct sigaction) {
105 : .sa_flags = SA_NOCLDWAIT,
106 : .sa_handler = SIG_IGN,
107 : };
108 :
109 1 : ret = sigaction(SIGCHLD, &act, NULL);
110 1 : torture_assert_goto(tctx, ret == 0, ok, done, "sigaction failed\n");
111 :
112 1 : t = tfork_create();
113 1 : if (t == NULL) {
114 0 : torture_fail(tctx, "tfork failed\n");
115 : return false;
116 : }
117 1 : child = tfork_child_pid(t);
118 1 : if (child == 0) {
119 0 : sleep(1);
120 0 : _exit(123);
121 : }
122 :
123 1 : child = fork();
124 1 : if (child == -1) {
125 0 : torture_fail(tctx, "fork failed\n");
126 : return false;
127 : }
128 1 : if (child == 0) {
129 0 : _exit(0);
130 : }
131 :
132 1 : status = tfork_status(&t, true);
133 1 : if (status == -1) {
134 0 : torture_fail(tctx, "tfork_status failed\n");
135 : }
136 :
137 1 : torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
138 : "tfork failed\n");
139 1 : torture_assert_goto(tctx, WEXITSTATUS(status) == 123, ok, done,
140 : "tfork failed\n");
141 1 : torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status));
142 :
143 0 : done:
144 0 : return ok;
145 : }
146 :
147 1 : static void sigchld_handler1(int signum, siginfo_t *si, void *u)
148 : {
149 1 : pid_t pid;
150 1 : int status;
151 :
152 1 : if (signum != SIGCHLD) {
153 0 : abort();
154 : }
155 :
156 1 : pid = waitpid(si->si_pid, &status, 0);
157 1 : if (pid != si->si_pid) {
158 0 : abort();
159 : }
160 1 : }
161 :
162 1 : static bool test_tfork_sighandler(struct torture_context *tctx)
163 : {
164 1 : struct tfork *t = NULL;
165 1 : struct sigaction act;
166 1 : struct sigaction oldact;
167 1 : pid_t child;
168 1 : int status;
169 1 : bool ok = true;
170 1 : int ret;
171 :
172 1 : act = (struct sigaction) {
173 : .sa_flags = SA_SIGINFO,
174 : .sa_sigaction = sigchld_handler1,
175 : };
176 :
177 1 : ret = sigaction(SIGCHLD, &act, &oldact);
178 1 : torture_assert_goto(tctx, ret == 0, ok, done, "sigaction failed\n");
179 :
180 1 : t = tfork_create();
181 1 : if (t == NULL) {
182 0 : torture_fail(tctx, "tfork failed\n");
183 : return false;
184 : }
185 1 : child = tfork_child_pid(t);
186 1 : if (child == 0) {
187 0 : sleep(1);
188 0 : _exit(123);
189 : }
190 :
191 1 : child = fork();
192 1 : if (child == -1) {
193 0 : torture_fail(tctx, "fork failed\n");
194 : return false;
195 : }
196 1 : if (child == 0) {
197 0 : _exit(0);
198 : }
199 :
200 1 : status = tfork_status(&t, true);
201 1 : if (status == -1) {
202 0 : torture_fail(tctx, "tfork_status failed\n");
203 : }
204 :
205 1 : torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
206 : "tfork failed\n");
207 1 : torture_assert_goto(tctx, WEXITSTATUS(status) == 123, ok, done,
208 : "tfork failed\n");
209 1 : torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status));
210 :
211 1 : done:
212 1 : sigaction(SIGCHLD, &oldact, NULL);
213 :
214 1 : return ok;
215 : }
216 :
217 1 : static bool test_tfork_process_hierarchy(struct torture_context *tctx)
218 : {
219 1 : struct tfork *t = NULL;
220 1 : pid_t pid = getpid();
221 1 : pid_t child;
222 1 : pid_t pgid = getpgid(0);
223 1 : pid_t sid = getsid(0);
224 1 : char *procpath = NULL;
225 1 : int status;
226 1 : struct stat st;
227 1 : int ret;
228 1 : bool ok = true;
229 :
230 1 : procpath = talloc_asprintf(tctx, "/proc/%d/status", getpid());
231 1 : torture_assert_not_null(tctx, procpath, "talloc_asprintf failed\n");
232 :
233 1 : ret = stat(procpath, &st);
234 1 : TALLOC_FREE(procpath);
235 1 : if (ret != 0) {
236 0 : if (errno == ENOENT) {
237 0 : torture_skip(tctx, "/proc missing\n");
238 : }
239 0 : torture_fail(tctx, "stat failed\n");
240 : }
241 :
242 1 : t = tfork_create();
243 1 : if (t == NULL) {
244 0 : torture_fail(tctx, "tfork failed\n");
245 : return false;
246 : }
247 1 : child = tfork_child_pid(t);
248 1 : if (child == 0) {
249 0 : char *cmd = NULL;
250 0 : FILE *fp = NULL;
251 0 : char line[64];
252 0 : char *p;
253 0 : pid_t ppid;
254 :
255 0 : torture_assert_goto(tctx, pgid == getpgid(0), ok, child_fail, "tfork failed\n");
256 0 : torture_assert_goto(tctx, sid == getsid(0), ok, child_fail, "tfork failed\n");
257 :
258 0 : cmd = talloc_asprintf(tctx, "cat /proc/%d/status | awk '/^PPid:/ {print $2}'", getppid());
259 0 : torture_assert_goto(tctx, cmd != NULL, ok, child_fail, "talloc_asprintf failed\n");
260 :
261 0 : fp = popen(cmd, "r");
262 0 : torture_assert_goto(tctx, fp != NULL, ok, child_fail, "popen failed\n");
263 :
264 0 : p = fgets(line, sizeof(line) - 1, fp);
265 0 : pclose(fp);
266 0 : torture_assert_goto(tctx, p != NULL, ok, child_fail, "popen failed\n");
267 :
268 0 : ret = sscanf(line, "%d", &ppid);
269 0 : torture_assert_goto(tctx, ret == 1, ok, child_fail, "sscanf failed\n");
270 0 : torture_assert_goto(tctx, ppid == pid, ok, child_fail, "process hierarchy not rooted at caller\n");
271 :
272 0 : _exit(0);
273 :
274 0 : child_fail:
275 0 : _exit(1);
276 : }
277 :
278 1 : status = tfork_status(&t, true);
279 1 : if (status == -1) {
280 0 : torture_fail(tctx, "tfork_status failed\n");
281 : }
282 :
283 1 : torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
284 : "tfork failed\n");
285 1 : torture_assert_goto(tctx, WEXITSTATUS(status) == 0, ok, done,
286 : "tfork failed\n");
287 1 : torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status));
288 :
289 0 : done:
290 0 : return ok;
291 : }
292 :
293 1 : static bool test_tfork_pipe(struct torture_context *tctx)
294 : {
295 1 : struct tfork *t = NULL;
296 1 : int status;
297 1 : pid_t child;
298 1 : int up[2];
299 1 : int down[2];
300 1 : char c;
301 1 : int ret;
302 1 : bool ok = true;
303 :
304 1 : ret = pipe(&up[0]);
305 1 : torture_assert(tctx, ret == 0, "pipe failed\n");
306 :
307 1 : ret = pipe(&down[0]);
308 1 : torture_assert(tctx, ret == 0, "pipe failed\n");
309 :
310 1 : t = tfork_create();
311 1 : if (t == NULL) {
312 0 : torture_fail(tctx, "tfork failed\n");
313 : return false;
314 : }
315 1 : child = tfork_child_pid(t);
316 1 : if (child == 0) {
317 0 : close(up[0]);
318 0 : close(down[1]);
319 :
320 0 : ret = read(down[0], &c, 1);
321 0 : torture_assert_goto(tctx, ret == 1, ok, child_fail, "read failed\n");
322 0 : torture_assert_goto(tctx, c == 1, ok, child_fail, "read failed\n");
323 :
324 0 : ret = write(up[1], &(char){2}, 1);
325 0 : torture_assert_goto(tctx, ret == 1, ok, child_fail, "write failed\n");
326 :
327 0 : _exit(0);
328 :
329 0 : child_fail:
330 0 : _exit(1);
331 : }
332 :
333 1 : close(up[1]);
334 1 : close(down[0]);
335 :
336 1 : ret = write(down[1], &(char){1}, 1);
337 1 : torture_assert(tctx, ret == 1, "read failed\n");
338 :
339 1 : ret = read(up[0], &c, 1);
340 1 : torture_assert(tctx, ret == 1, "read failed\n");
341 1 : torture_assert(tctx, c == 2, "read failed\n");
342 :
343 1 : status = tfork_status(&t, true);
344 1 : if (status == -1) {
345 0 : torture_fail(tctx, "tfork_status failed\n");
346 : }
347 :
348 1 : torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
349 : "tfork failed\n");
350 1 : torture_assert_goto(tctx, WEXITSTATUS(status) == 0, ok, done,
351 : "tfork failed\n");
352 1 : done:
353 0 : return ok;
354 : }
355 :
356 1 : static bool test_tfork_twice(struct torture_context *tctx)
357 : {
358 1 : struct tfork *t = NULL;
359 1 : int status;
360 1 : pid_t child;
361 1 : pid_t pid;
362 1 : int up[2];
363 1 : int ret;
364 1 : bool ok = true;
365 :
366 1 : ret = pipe(&up[0]);
367 1 : torture_assert(tctx, ret == 0, "pipe failed\n");
368 :
369 1 : t = tfork_create();
370 1 : if (t == NULL) {
371 0 : torture_fail(tctx, "tfork failed\n");
372 : return false;
373 : }
374 1 : child = tfork_child_pid(t);
375 1 : if (child == 0) {
376 0 : close(up[0]);
377 :
378 0 : t = tfork_create();
379 0 : if (t == NULL) {
380 0 : torture_fail(tctx, "tfork failed\n");
381 : return false;
382 : }
383 0 : child = tfork_child_pid(t);
384 0 : if (child == 0) {
385 0 : sleep(1);
386 0 : pid = getpid();
387 0 : ret = write(up[1], &pid, sizeof(pid_t));
388 0 : torture_assert_goto(tctx, ret == sizeof(pid_t), ok, child_fail, "write failed\n");
389 :
390 0 : _exit(0);
391 :
392 0 : child_fail:
393 0 : _exit(1);
394 : }
395 :
396 0 : _exit(0);
397 : }
398 :
399 1 : close(up[1]);
400 :
401 1 : ret = read(up[0], &pid, sizeof(pid_t));
402 1 : torture_assert(tctx, ret == sizeof(pid_t), "read failed\n");
403 :
404 1 : status = tfork_status(&t, true);
405 1 : torture_assert_goto(tctx, status != -1, ok, done, "tfork_status failed\n");
406 :
407 1 : torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
408 : "tfork failed\n");
409 1 : torture_assert_goto(tctx, WEXITSTATUS(status) == 0, ok, done,
410 : "tfork failed\n");
411 1 : done:
412 0 : return ok;
413 : }
414 :
415 64 : static void *tfork_thread(void *p)
416 : {
417 64 : struct tfork *t = NULL;
418 64 : int status;
419 64 : pid_t child;
420 64 : uint64_t tid = (uint64_t)pthread_self();
421 64 : uint64_t *result = NULL;
422 64 : int up[2];
423 64 : ssize_t nread;
424 64 : int ret;
425 :
426 64 : ret = pipe(up);
427 64 : if (ret != 0) {
428 0 : pthread_exit(NULL);
429 : }
430 :
431 64 : t = tfork_create();
432 64 : if (t == NULL) {
433 0 : pthread_exit(NULL);
434 : }
435 64 : child = tfork_child_pid(t);
436 64 : if (child == 0) {
437 0 : ssize_t nwritten;
438 :
439 0 : close(up[0]);
440 0 : tid++;
441 0 : nwritten = sys_write(up[1], &tid, sizeof(uint64_t));
442 0 : if (nwritten != sizeof(uint64_t)) {
443 0 : _exit(1);
444 : }
445 0 : _exit(0);
446 : }
447 64 : close(up[1]);
448 :
449 64 : result = malloc(sizeof(uint64_t));
450 64 : if (result == NULL) {
451 0 : pthread_exit(NULL);
452 : }
453 :
454 64 : nread = sys_read(up[0], result, sizeof(uint64_t));
455 64 : if (nread != sizeof(uint64_t)) {
456 0 : pthread_exit(NULL);
457 : }
458 :
459 64 : status = tfork_status(&t, true);
460 64 : if (status == -1) {
461 0 : pthread_exit(NULL);
462 : }
463 :
464 64 : pthread_exit(result);
465 : }
466 :
467 1 : static bool test_tfork_threads(struct torture_context *tctx)
468 1 : {
469 1 : int ret;
470 1 : bool ok = true;
471 1 : const int num_threads = 64;
472 1 : pthread_t threads[num_threads];
473 1 : sigset_t set;
474 1 : int i;
475 :
476 : #ifndef HAVE_PTHREAD
477 : torture_skip(tctx, "no pthread support\n");
478 : #endif
479 :
480 : /*
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. :)
484 : */
485 1 : sigemptyset(&set);
486 1 : sigaddset(&set, SIGCHLD);
487 : #ifdef HAVE_PTHREAD
488 1 : ret = pthread_sigmask(SIG_UNBLOCK, &set, NULL);
489 : #else
490 : ret = sigprocmask(SIG_UNBLOCK, &set, NULL);
491 : #endif
492 1 : if (ret != 0) {
493 0 : return false;
494 : }
495 :
496 65 : for (i = 0; i < num_threads; i++) {
497 64 : ret = pthread_create(&threads[i], NULL, tfork_thread, NULL);
498 64 : torture_assert_goto(tctx, ret == 0, ok, done,
499 : "pthread_create failed\n");
500 : }
501 :
502 65 : for (i = 0; i < num_threads; i++) {
503 64 : void *p;
504 64 : uint64_t *result;
505 :
506 64 : ret = pthread_join(threads[i], &p);
507 64 : torture_assert_goto(tctx, ret == 0, ok, done,
508 : "pthread_join failed\n");
509 64 : result = (uint64_t *)p;
510 64 : torture_assert_goto(tctx, *result == (uint64_t)threads[i] + 1,
511 : ok, done, "thread failed\n");
512 64 : free(p);
513 : }
514 :
515 1 : done:
516 0 : return ok;
517 : }
518 :
519 1 : static bool test_tfork_cmd_send(struct torture_context *tctx)
520 : {
521 1 : struct tevent_context *ev = NULL;
522 1 : struct tevent_req *req = NULL;
523 1 : const char *cmd[2] = { NULL, NULL };
524 1 : bool ok = true;
525 :
526 1 : ev = tevent_context_init(tctx);
527 1 : torture_assert_goto(tctx, ev != NULL, ok, done,
528 : "tevent_context_init failed\n");
529 :
530 1 : cmd[0] = talloc_asprintf(tctx, "%s/testprogs/blackbox/tfork.sh", SRCDIR);
531 1 : torture_assert_goto(tctx, cmd[0] != NULL, ok, done,
532 : "talloc_asprintf failed\n");
533 :
534 1 : req = samba_runcmd_send(tctx, ev, timeval_zero(), 0, 0,
535 : cmd, "foo", NULL);
536 1 : torture_assert_goto(tctx, req != NULL, ok, done,
537 : "samba_runcmd_send failed\n");
538 :
539 1 : ok = tevent_req_poll(req, ev);
540 1 : torture_assert_goto(tctx, ok, ok, done, "tevent_req_poll failed\n");
541 :
542 1 : torture_comment(tctx, "samba_runcmd_send test finished\n");
543 :
544 1 : done:
545 1 : TALLOC_FREE(ev);
546 :
547 1 : return ok;
548 : }
549 :
550 : /*
551 : * Test to ensure that the event_fd becomes readable after
552 : * a tfork_process terminates.
553 : */
554 1 : static bool test_tfork_event_file_handle(struct torture_context *tctx)
555 : {
556 1 : bool ok = true;
557 :
558 1 : struct tfork *t1 = NULL;
559 1 : pid_t child1;
560 1 : struct pollfd poll1[] = {
561 : {
562 : .fd = -1,
563 : .events = POLLIN,
564 : },
565 : };
566 :
567 1 : struct tfork *t2 = NULL;
568 1 : pid_t child2;
569 1 : struct pollfd poll2[] = {
570 : {
571 : .fd = -1,
572 : .events = POLLIN,
573 : },
574 : };
575 :
576 :
577 1 : t1 = tfork_create();
578 1 : if (t1 == NULL) {
579 0 : torture_fail(tctx, "tfork failed\n");
580 : return false;
581 : }
582 :
583 1 : child1 = tfork_child_pid(t1);
584 1 : if (child1 == 0) {
585 : /*
586 : * Parent process will kill this with a SIGTERM
587 : * so 10 seconds should be plenty
588 : */
589 0 : sleep(10);
590 0 : exit(1);
591 : }
592 1 : poll1[0].fd = tfork_event_fd(t1);
593 :
594 1 : t2 = tfork_create();
595 1 : if (t2 == NULL) {
596 0 : torture_fail(tctx, "tfork failed\n");
597 : return false;
598 : }
599 1 : child2 = tfork_child_pid(t2);
600 1 : if (child2 == 0) {
601 : /*
602 : * Parent process will kill this with a SIGTERM
603 : * so 10 seconds should be plenty
604 : */
605 0 : sleep(10);
606 0 : exit(2);
607 : }
608 1 : poll2[0].fd = tfork_event_fd(t2);
609 :
610 : /*
611 : * Have forked two process and are in the master process
612 : * Expect that both event_fds are unreadable
613 : */
614 1 : poll(poll1, 1, 0);
615 1 : ok = !(poll1[0].revents & POLLIN);
616 1 : torture_assert_goto(tctx, ok, ok, done,
617 : "tfork process 1 event fd readable\n");
618 1 : poll(poll2, 1, 0);
619 1 : ok = !(poll2[0].revents & POLLIN);
620 1 : torture_assert_goto(tctx, ok, ok, done,
621 : "tfork process 1 event fd readable\n");
622 :
623 : /* Kill the first child process */
624 1 : kill(child1, SIGKILL);
625 1 : sleep(1);
626 :
627 : /*
628 : * Have killed the first child, so expect it's event_fd to have gone
629 : * readable.
630 : *
631 : */
632 1 : poll(poll1, 1, 0);
633 1 : ok = (poll1[0].revents & POLLIN);
634 1 : torture_assert_goto(tctx, ok, ok, done,
635 : "tfork process 1 event fd not readable\n");
636 1 : poll(poll2, 1, 0);
637 1 : ok = !(poll2[0].revents & POLLIN);
638 1 : torture_assert_goto(tctx, ok, ok, done,
639 : "tfork process 2 event fd readable\n");
640 :
641 : /* Kill the secind child process */
642 1 : kill(child2, SIGKILL);
643 1 : sleep(1);
644 : /*
645 : * Have killed the children, so expect their event_fd's to have gone
646 : * readable.
647 : *
648 : */
649 1 : poll(poll1, 1, 0);
650 1 : ok = (poll1[0].revents & POLLIN);
651 1 : torture_assert_goto(tctx, ok, ok, done,
652 : "tfork process 1 event fd not readable\n");
653 1 : poll(poll2, 1, 0);
654 1 : ok = (poll2[0].revents & POLLIN);
655 1 : torture_assert_goto(tctx, ok, ok, done,
656 : "tfork process 2 event fd not readable\n");
657 :
658 1 : done:
659 1 : free(t1);
660 1 : free(t2);
661 :
662 1 : return ok;
663 : }
664 :
665 : /*
666 : * Test to ensure that the status calls behave as expected after a process
667 : * terminates.
668 : *
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.
672 : *
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.
675 : *
676 : */
677 1 : static bool test_tfork_status_handle(struct torture_context *tctx)
678 : {
679 1 : bool ok = true;
680 :
681 1 : struct tfork *t1 = NULL;
682 1 : pid_t child1;
683 :
684 1 : struct tfork *t2 = NULL;
685 1 : pid_t child2;
686 :
687 1 : int status;
688 1 : int fd;
689 1 : int ev1_fd;
690 1 : int ev2_fd;
691 :
692 :
693 1 : t1 = tfork_create();
694 1 : if (t1 == NULL) {
695 0 : torture_fail(tctx, "tfork failed\n");
696 : return false;
697 : }
698 :
699 1 : child1 = tfork_child_pid(t1);
700 1 : if (child1 == 0) {
701 : /*
702 : * Parent process will kill this with a SIGTERM
703 : * so 10 seconds should be plenty
704 : */
705 0 : sleep(10);
706 0 : exit(1);
707 : }
708 1 : ev1_fd = tfork_event_fd(t1);
709 :
710 1 : t2 = tfork_create();
711 1 : if (t2 == NULL) {
712 0 : torture_fail(tctx, "tfork failed\n");
713 : return false;
714 : }
715 1 : child2 = tfork_child_pid(t2);
716 1 : if (child2 == 0) {
717 : /*
718 : * Parent process will kill this with a SIGTERM
719 : * so 10 seconds should be plenty
720 : */
721 0 : sleep(10);
722 0 : exit(2);
723 : }
724 1 : ev2_fd = tfork_event_fd(t2);
725 :
726 : /*
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.
731 : */
732 1 : status = tfork_status(&t1, false);
733 1 : ok = status == -1;
734 1 : 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 1 : fd = dup(ev1_fd);
739 1 : ok = fd != -1;
740 1 : torture_assert_goto(tctx, ok, ok, done,
741 : "tfork process 1 event fd is not open");
742 :
743 1 : status = tfork_status(&t2, false);
744 1 : ok = status == -1;
745 1 : 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 1 : fd = dup(ev2_fd);
750 1 : ok = fd != -1;
751 1 : torture_assert_goto(tctx, ok, ok, done,
752 : "tfork process 2 event fd is not open");
753 :
754 : /*
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.
758 : */
759 1 : kill(child1, SIGTERM);
760 1 : sleep(1);
761 1 : status = tfork_status(&t1, false);
762 1 : ok = status != -1;
763 1 : 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 1 : fd = dup(ev2_fd);
768 1 : ok = fd != -1;
769 1 : torture_assert_goto(tctx, ok, ok, done,
770 : "tfork process 1 event fd is not open");
771 :
772 1 : status = tfork_status(&t2, false);
773 1 : ok = status == -1;
774 1 : torture_assert_goto(tctx, ok, ok, done,
775 : "tfork status available for child 2 after "
776 : "termination of child 1\n");
777 :
778 : /*
779 : * Kill the second process, it's status should be readable
780 : */
781 1 : kill(child2, SIGTERM);
782 1 : sleep(1);
783 1 : status = tfork_status(&t2, false);
784 1 : ok = status != -1;
785 1 : torture_assert_goto(tctx, ok, ok, done,
786 : "tfork status for child 2 not available after "
787 : "termination\n");
788 :
789 : /* Check that the event fd's are still open */
790 : /* Is the event fd open? */
791 1 : fd = dup(ev1_fd);
792 1 : ok = fd != -1;
793 1 : torture_assert_goto(tctx, ok, ok, done,
794 : "tfork process 1 event fd is not open");
795 : /* Is the event fd open? */
796 1 : fd = dup(ev2_fd);
797 1 : ok = fd != -1;
798 1 : torture_assert_goto(tctx, ok, ok, done,
799 : "tfork process 2 event fd is not open");
800 :
801 1 : done:
802 0 : return ok;
803 : }
804 :
805 2354 : struct torture_suite *torture_local_tfork(TALLOC_CTX *mem_ctx)
806 : {
807 125 : struct torture_suite *suite =
808 2354 : torture_suite_create(mem_ctx, "tfork");
809 :
810 2354 : torture_suite_add_simple_test(suite,
811 : "tfork_simple",
812 : test_tfork_simple);
813 :
814 2354 : torture_suite_add_simple_test(suite,
815 : "tfork_status",
816 : test_tfork_status);
817 :
818 2354 : torture_suite_add_simple_test(suite,
819 : "tfork_sigign",
820 : test_tfork_sigign);
821 :
822 2354 : torture_suite_add_simple_test(suite,
823 : "tfork_sighandler",
824 : test_tfork_sighandler);
825 :
826 2354 : torture_suite_add_simple_test(suite,
827 : "tfork_process_hierarchy",
828 : test_tfork_process_hierarchy);
829 :
830 2354 : torture_suite_add_simple_test(suite,
831 : "tfork_pipe",
832 : test_tfork_pipe);
833 :
834 2354 : torture_suite_add_simple_test(suite,
835 : "tfork_twice",
836 : test_tfork_twice);
837 :
838 2354 : torture_suite_add_simple_test(suite,
839 : "tfork_threads",
840 : test_tfork_threads);
841 :
842 2354 : torture_suite_add_simple_test(suite,
843 : "tfork_cmd_send",
844 : test_tfork_cmd_send);
845 :
846 2354 : torture_suite_add_simple_test(suite,
847 : "tfork_event_file_handle",
848 : test_tfork_event_file_handle);
849 :
850 2354 : torture_suite_add_simple_test(suite,
851 : "tfork_status_handle",
852 : test_tfork_status_handle);
853 :
854 2354 : return suite;
855 : }
|