LCOV - code coverage report
Current view: top level - lib/util/tests - tfork.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 334 443 75.4 %
Date: 2024-04-21 15:09:00 Functions: 14 14 100.0 %

          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             : }

Generated by: LCOV version 1.14