The branch, master has been updated
       via  375d772 vfs_acl_xattr|tdb: ensure create mask is at least 0666 if 
ignore_system_acls is set
       via  fd98a7b cleanupdb: Fix a memory read error
       via  426e407 lib/util: add a test for samba_runcmd_send()
       via  4a43129 wafsamba: add source directory define SRCDIR to config.h
       via  292e46a lib/util: make use of tfork in samba_runcmd_send()
       via  3ce1060 lib/util: add a test for tfork()
       via  6b950ae lib/util: add tfork()
      from  fef69a1 smbldap: Bump version number

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 375d772d04338861d92e683ae3c6c9d7ecb846ad
Author: Ralph Boehme <[email protected]>
Date:   Mon Feb 6 12:47:41 2017 +0100

    vfs_acl_xattr|tdb: ensure create mask is at least 0666 if 
ignore_system_acls is set
    
    Bug: https://bugzilla.samba.org/show_bug.cgi?id=12562
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Uri Simchoni <[email protected]>
    
    Autobuild-User(master): Ralph Böhme <[email protected]>
    Autobuild-Date(master): Thu Apr 20 20:50:10 CEST 2017 on sn-devel-144

commit fd98a7b6a0053b62802e29fb729e219dc08eef6b
Author: Hanno Böck <[email protected]>
Date:   Wed Apr 19 14:00:21 2017 +0200

    cleanupdb: Fix a memory read error
    
    Bug: https://bugzilla.samba.org/show_bug.cgi?id=12748
    
    Signed-off-by: Hanno Böck <[email protected]>
    Reviewed-by: Volker Lendecke <[email protected]>
    Reviewed-by: Jeremy Allison <[email protected]>
    Reviewed-by: Ralph Boehme <[email protected]>

commit 426e407c533ae6980da672cf2d4e7b01b821a3d3
Author: Ralph Boehme <[email protected]>
Date:   Tue Apr 11 20:00:05 2017 +0200

    lib/util: add a test for samba_runcmd_send()
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Jeremy Allison <[email protected]>

commit 4a43129b7a7285024f0d2b76e893740f73adce2f
Author: Ralph Boehme <[email protected]>
Date:   Sat Apr 15 09:09:21 2017 +0200

    wafsamba: add source directory define SRCDIR to config.h
    
    This will be used in the next commit to prepare the path to a test
    script in a smbtorture test.
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Jeremy Allison <[email protected]>

commit 292e46ab12d8ec172c9d3b26330d8d6028a1d5a5
Author: Ralph Boehme <[email protected]>
Date:   Tue Apr 11 20:05:05 2017 +0200

    lib/util: make use of tfork in samba_runcmd_send()
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Jeremy Allison <[email protected]>

commit 3ce1060f781ff5b97582542861f83129b16a8bbf
Author: Ralph Boehme <[email protected]>
Date:   Tue Apr 11 17:32:01 2017 +0200

    lib/util: add a test for tfork()
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Jeremy Allison <[email protected]>

commit 6b950ae37e80eccea7ed0a831497f6c7923f6d03
Author: Stefan Metzmacher <[email protected]>
Date:   Thu Sep 23 18:10:02 2010 +0200

    lib/util: add tfork()
    
    triple-fork to avoid handling SIGCHLD in the parent.
    
    This function is a workaround for the problem of using fork() in
    library code. In that case the library should avoid setting a global
    signal handler for SIGCHLD, because the application may wants to use its
    own handler.
    
    status_fd can be used to wait for the child to exit and get its exit
    status.
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Jeremy Allison <[email protected]>

-----------------------------------------------------------------------

Summary of changes:
 buildtools/wafsamba/wscript         |   2 +
 lib/util/tests/tfork.c              | 138 +++++++++++++++
 lib/util/tfork.c                    | 326 ++++++++++++++++++++++++++++++++++++
 lib/util/tfork.h                    |  52 ++++++
 lib/util/util_runcmd.c              | 103 ++++++------
 lib/util/util_runcmd.h              |   3 +-
 lib/util/wscript_build              |   4 +-
 source3/lib/cleanupdb.c             |   2 +-
 source3/modules/vfs_acl_tdb.c       |  24 ++-
 source3/modules/vfs_acl_xattr.c     |  24 ++-
 source4/torture/local/local.c       |   1 +
 source4/torture/local/wscript_build |   1 +
 testprogs/blackbox/tfork.sh         |  15 ++
 13 files changed, 638 insertions(+), 57 deletions(-)
 create mode 100644 lib/util/tests/tfork.c
 create mode 100644 lib/util/tfork.c
 create mode 100644 lib/util/tfork.h
 create mode 100755 testprogs/blackbox/tfork.sh


Changeset truncated at 500 lines:

diff --git a/buildtools/wafsamba/wscript b/buildtools/wafsamba/wscript
index 4eef008..430d164 100644
--- a/buildtools/wafsamba/wscript
+++ b/buildtools/wafsamba/wscript
@@ -212,6 +212,8 @@ def configure(conf):
     conf.env.hlist = []
     conf.env.srcdir = conf.srcdir
 
+    conf.define('SRCDIR', conf.env['srcdir'])
+
     if Options.options.timestamp_dependencies:
         conf.ENABLE_TIMESTAMP_DEPENDENCIES()
 
diff --git a/lib/util/tests/tfork.c b/lib/util/tests/tfork.c
new file mode 100644
index 0000000..7a96928
--- /dev/null
+++ b/lib/util/tests/tfork.c
@@ -0,0 +1,138 @@
+/*
+ * Tests for tfork
+ *
+ * Copyright Ralph Boehme <[email protected]> 2017
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include <talloc.h>
+#include <tevent.h>
+#include "system/filesys.h"
+#include "libcli/util/ntstatus.h"
+#include "torture/torture.h"
+#include "lib/util/data_blob.h"
+#include "torture/local/proto.h"
+#include "lib/util/tfork.h"
+#include "lib/util/samba_util.h"
+#include "lib/util/sys_rw.h"
+
+static bool test_tfork_simple(struct torture_context *tctx)
+{
+       pid_t pid;
+       pid_t parent = getpid();
+       pid_t parent_arg;
+
+       pid = tfork(NULL, &parent_arg);
+       if (pid == 0) {
+               torture_comment(tctx, "my parent pid is %d\n", parent);
+               torture_assert(tctx, parent == parent_arg, "tfork failed\n");
+               _exit(0);
+       }
+       if (pid == -1) {
+               torture_fail(tctx, "tfork failed\n");
+               return false;
+       }
+
+       return true;
+}
+
+static bool test_tfork_status(struct torture_context *tctx)
+{
+       pid_t child;
+       int status;
+       ssize_t nread;
+       int status_fd = -1;
+       bool ok = true;
+
+       child = tfork(&status_fd, NULL);
+       if (child == 0) {
+               _exit(123);
+       }
+       if (child == -1) {
+               torture_fail(tctx, "tfork failed\n");
+               return false;
+       }
+
+       nread = sys_read(status_fd, &status, sizeof(status));
+       if (nread != sizeof(status)) {
+               torture_fail(tctx, "sys_read failed\n");
+       }
+
+       torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
+                           "tfork failed\n");
+       torture_assert_goto(tctx, WEXITSTATUS(status) == 123, ok, done,
+                           "tfork failed\n");
+
+       torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status));
+
+done:
+       if (status_fd != -1) {
+               close(status_fd);
+       }
+
+       return ok;
+}
+
+static bool test_tfork_cmd_send(struct torture_context *tctx)
+{
+       struct tevent_context *ev = NULL;
+       struct tevent_req *req = NULL;
+       const char *cmd[2] = { NULL, NULL };
+       bool ok = true;
+
+       ev = tevent_context_init(tctx);
+       torture_assert_goto(tctx, ev != NULL, ok, done,
+                           "tevent_context_init failed\n");
+
+       cmd[0] = talloc_asprintf(tctx, "%s/testprogs/blackbox/tfork.sh", 
SRCDIR);
+       torture_assert_goto(tctx, cmd[0] != NULL, ok, done,
+                           "talloc_asprintf failed\n");
+
+       req = samba_runcmd_send(tctx, ev, timeval_zero(), 1, 0,
+                               cmd, "foo", NULL);
+       torture_assert_goto(tctx, req != NULL, ok, done,
+                           "samba_runcmd_send failed\n");
+
+       ok = tevent_req_poll(req, ev);
+       torture_assert_goto(tctx, ok, ok, done, "tevent_req_poll failed\n");
+
+       torture_comment(tctx, "samba_runcmd_send test finished\n");
+
+done:
+       TALLOC_FREE(ev);
+
+       return ok;
+}
+
+struct torture_suite *torture_local_tfork(TALLOC_CTX *mem_ctx)
+{
+       struct torture_suite *suite =
+               torture_suite_create(mem_ctx, "tfork");
+
+       torture_suite_add_simple_test(suite,
+                                     "tfork_simple",
+                                     test_tfork_simple);
+
+       torture_suite_add_simple_test(suite,
+                                     "tfork_status",
+                                     test_tfork_status);
+
+       torture_suite_add_simple_test(suite,
+                                     "tfork_cmd_send",
+                                     test_tfork_cmd_send);
+
+       return suite;
+}
diff --git a/lib/util/tfork.c b/lib/util/tfork.c
new file mode 100644
index 0000000..27b6cc0
--- /dev/null
+++ b/lib/util/tfork.c
@@ -0,0 +1,326 @@
+/*
+   fork on steroids to avoid SIGCHLD and waitpid
+
+   Copyright (C) Stefan Metzmacher 2010
+   Copyright (C) Ralph Boehme 2017
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/wait.h"
+#include "system/filesys.h"
+#include "lib/util/samba_util.h"
+#include "lib/util/sys_rw.h"
+#include "lib/util/tfork.h"
+#include "lib/util/debug.h"
+
+struct tfork_state {
+       void (*old_sig_chld)(int);
+       int status_pipe[2];
+       pid_t *parent;
+
+       pid_t level0_pid;
+       int level0_status;
+
+       pid_t level1_pid;
+       int level1_errno;
+
+       pid_t level2_pid;
+       int level2_errno;
+
+       pid_t level3_pid;
+};
+
+/*
+ * TODO: We should make this global thread local
+ */
+static struct tfork_state *tfork_global;
+
+static void tfork_sig_chld(int signum)
+{
+       if (tfork_global->level1_pid > 0) {
+               int ret = waitpid(tfork_global->level1_pid,
+                             &tfork_global->level0_status,
+                             WNOHANG);
+               if (ret == tfork_global->level1_pid) {
+                       tfork_global->level1_pid = -1;
+                       return;
+               }
+       }
+
+       /*
+        * Not our child, forward to old handler
+        */
+
+       if (tfork_global->old_sig_chld == SIG_IGN) {
+               return;
+       }
+
+       if (tfork_global->old_sig_chld == SIG_DFL) {
+               return;
+       }
+
+       tfork_global->old_sig_chld(signum);
+}
+
+static pid_t level2_fork_and_wait(int child_ready_fd)
+{
+       int status;
+       ssize_t written;
+       pid_t pid;
+       int fd;
+       bool wait;
+
+       /*
+        * Child level 2.
+        *
+        * Do a final fork and if the tfork() caller passed a status_fd, wait
+        * for child3 and return its exit status via status_fd.
+        */
+
+       pid = fork();
+       if (pid == 0) {
+               /*
+                * Child level 3, this one finally returns from tfork() as child
+                * with pid 0.
+                *
+                * Cleanup all ressources we allocated before returning.
+                */
+               close(child_ready_fd);
+               close(tfork_global->status_pipe[1]);
+
+               if (tfork_global->parent != NULL) {
+                       /*
+                        * we're in the child and return the level0 parent pid
+                        */
+                       *tfork_global->parent = tfork_global->level0_pid;
+               }
+
+               anonymous_shared_free(tfork_global);
+               tfork_global = NULL;
+
+               return 0;
+       }
+
+       tfork_global->level3_pid = pid;
+       if (tfork_global->level3_pid == -1) {
+               tfork_global->level2_errno = errno;
+               _exit(0);
+       }
+
+       sys_write(child_ready_fd, &(char){0}, 1);
+
+       if (tfork_global->status_pipe[1] == -1) {
+               _exit(0);
+       }
+       wait = true;
+
+       /*
+        * We're going to stay around until child3 exits, so lets close all fds
+        * other then the pipe fd we may have inherited from the caller.
+        */
+       fd = dup2(tfork_global->status_pipe[1], 0);
+       if (fd == -1) {
+               status = errno;
+               kill(tfork_global->level3_pid, SIGKILL);
+               wait = false;
+       }
+       closefrom(1);
+
+       while (wait) {
+               int ret = waitpid(tfork_global->level3_pid, &status, 0);
+               if (ret == -1) {
+                       if (errno == EINTR) {
+                               continue;
+                       }
+                       status = errno;
+               }
+               break;
+       }
+
+       written = sys_write(fd, &status, sizeof(status));
+       if (written != sizeof(status)) {
+               abort();
+       }
+
+       _exit(0);
+}
+
+pid_t tfork(int *status_fd, pid_t *parent)
+{
+       int ret;
+       pid_t pid;
+       pid_t child;
+
+       tfork_global = (struct tfork_state *)
+               anonymous_shared_allocate(sizeof(struct tfork_state));
+       if (tfork_global == NULL) {
+               return -1;
+       }
+
+       tfork_global->parent = parent;
+       tfork_global->status_pipe[0] = -1;
+       tfork_global->status_pipe[1] = -1;
+
+       tfork_global->level0_pid = getpid();
+       tfork_global->level0_status = -1;
+       tfork_global->level1_pid = -1;
+       tfork_global->level1_errno = ECANCELED;
+       tfork_global->level2_pid = -1;
+       tfork_global->level2_errno = ECANCELED;
+       tfork_global->level3_pid = -1;
+
+       if (status_fd != NULL) {
+               ret = pipe(&tfork_global->status_pipe[0]);
+               if (ret != 0) {
+                       int saved_errno = errno;
+
+                       anonymous_shared_free(tfork_global);
+                       tfork_global = NULL;
+                       errno = saved_errno;
+                       return -1;
+               }
+
+               *status_fd = tfork_global->status_pipe[0];
+       }
+
+       /*
+        * We need to set our own signal handler to prevent any existing signal
+        * handler from reaping our child.
+        */
+       tfork_global->old_sig_chld = CatchSignal(SIGCHLD, tfork_sig_chld);
+
+       pid = fork();
+       if (pid == 0) {
+               int level2_pipe[2];
+               char c;
+               ssize_t nread;
+
+               /*
+                * Child level 1.
+                *
+                * Restore SIGCHLD handler
+                */
+               CatchSignal(SIGCHLD, SIG_DFL);
+
+               /*
+                * Close read end of the signal pipe, we don't need it anymore
+                * and don't want to leak it into childs.
+                */
+               if (tfork_global->status_pipe[0] != -1) {
+                       close(tfork_global->status_pipe[0]);
+                       tfork_global->status_pipe[0] = -1;
+               }
+
+               /*
+                * Create a pipe for waiting for the child level 2 to finish
+                * forking.
+                */
+               ret = pipe(&level2_pipe[0]);
+               if (ret != 0) {
+                       tfork_global->level1_errno = errno;
+                       _exit(0);
+               }
+
+               pid = fork();
+               if (pid == 0) {
+
+                       /*
+                        * Child level 2.
+                        */
+
+                       close(level2_pipe[0]);
+                       return level2_fork_and_wait(level2_pipe[1]);
+               }
+
+               tfork_global->level2_pid = pid;
+               if (tfork_global->level2_pid == -1) {
+                       tfork_global->level1_errno = errno;
+                       _exit(0);
+               }
+
+               close(level2_pipe[1]);
+               level2_pipe[1] = -1;
+
+               nread = sys_read(level2_pipe[0], &c, 1);
+               if (nread != 1) {
+                       abort();
+               }
+               _exit(0);
+       }
+
+       tfork_global->level1_pid = pid;
+       if (tfork_global->level1_pid == -1) {
+               int saved_errno = errno;
+
+               anonymous_shared_free(tfork_global);
+               tfork_global = NULL;
+               errno = saved_errno;
+               return -1;
+       }
+
+       /*
+        * By using the helper variable pid we avoid a TOCTOU with the signal
+        * handler that will set tfork_global->level1_pid to -1 (which would
+        * cause waitpid() to block waiting for another exitted child).
+        *
+        * We can't avoid the race waiting for pid twice (in the signal handler
+        * and then again here in the while loop), but we must avoid waiting for
+        * -1 and this does the trick.
+        */
+       pid = tfork_global->level1_pid;
+
+       while (tfork_global->level1_pid != -1) {
+               ret = waitpid(pid, &tfork_global->level0_status, 0);
+               if (ret == -1 && errno == EINTR) {
+                       continue;
+               }
+
+               break;
+       }
+
+       CatchSignal(SIGCHLD, tfork_global->old_sig_chld);
+
+       if (tfork_global->level0_status != 0) {
+               anonymous_shared_free(tfork_global);
+               tfork_global = NULL;
+               errno = ECHILD;
+               return -1;
+       }
+
+       if (tfork_global->level2_pid == -1) {
+               int saved_errno = tfork_global->level1_errno;
+
+               anonymous_shared_free(tfork_global);
+               tfork_global = NULL;
+               errno = saved_errno;
+               return -1;
+       }
+
+       if (tfork_global->level3_pid == -1) {
+               int saved_errno = tfork_global->level2_errno;
+
+               anonymous_shared_free(tfork_global);
+               tfork_global = NULL;
+               errno = saved_errno;
+               return -1;
+       }
+
+       child = tfork_global->level3_pid;
+       anonymous_shared_free(tfork_global);
+       tfork_global = NULL;
+
+       return child;
+}
diff --git a/lib/util/tfork.h b/lib/util/tfork.h
new file mode 100644
index 0000000..0c62fc3
--- /dev/null
+++ b/lib/util/tfork.h
@@ -0,0 +1,52 @@
+/*
+   fork on steroids to avoid SIGCHLD and waitpid
+
+   Copyright (C) Stefan Metzmacher 2010
+   Copyright (C) Ralph Boehme 2017


-- 
Samba Shared Repository

Reply via email to