--- /dev/null
+/*
+ double fork to avoid SIGCHLD and zombies when the child exists.
+
+ Copyright (C) Stefan Metzmacher 2010
+
+ 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" /* for getpid() via unistd.h */
+#include "../lib/util/util.h"
+
+struct double_fork_state {
+ void (*old_sig_chld)(int);
+ int level0_pid;
+ int level0_status;
+ int level1_pid;
+ int level1_errno;
+ int level2_pid;
+};
+
+/*
+ * TODO: We should make this global thread local?
+ */
+struct double_fork_state *double_fork_global;
+
+static void double_fork_sig_chld(int signum)
+{
+ int ret;
+
+ if (double_fork_global->level1_pid > 0) {
+ ret = waitpid(double_fork_global->level1_pid,
+ &double_fork_global->level0_status,
+ WNOHANG);
+ if (ret == double_fork_global->level1_pid) {
+ return;
+ }
+ }
+
+ if (double_fork_global->old_sig_chld == SIG_IGN) {
+ return;
+ }
+
+ if (double_fork_global->old_sig_chld == SIG_DFL) {
+ return;
+ }
+
+ double_fork_global->old_sig_chld(signum);
+}
+
+int double_fork(int *parent)
+{
+ int level1_pid;
+ int ret;
+ int child = -1;
+
+ double_fork_global = (struct double_fork_state *)
+ anonymous_shared_allocate(sizeof(struct double_fork_state));
+ if (double_fork_global == NULL) {
+ return -1;
+ }
+
+ double_fork_global->level0_pid = getpid();
+ double_fork_global->level0_status = -1;
+ double_fork_global->level1_pid = -1;
+ double_fork_global->level1_errno = ECANCELED;
+ double_fork_global->level2_pid = -1;
+
+ double_fork_global->old_sig_chld = signal(SIGCHLD, double_fork_sig_chld);
+
+ level1_pid = fork();
+ if (level1_pid == 0) {
+ int level2_pid;
+ signal(SIGCHLD, SIG_DFL);
+
+ level2_pid = fork();
+ if (level2_pid == 0) {
+ /*
+ * we're in the child and return the level0 parent pid
+ */
+ if (parent) {
+ *parent = double_fork_global->level0_pid;
+ }
+
+ anonymous_shared_free(double_fork_global);
+ double_fork_global = NULL;
+
+ return 0;
+ }
+ double_fork_global->level2_pid = level2_pid;
+ if (double_fork_global->level2_pid == -1) {
+ double_fork_global->level1_errno = errno;
+ }
+ _exit(0);
+ }
+ double_fork_global->level1_pid = level1_pid;
+ if (double_fork_global->level1_pid == -1) {
+ int saved_errno = errno;
+ anonymous_shared_free(double_fork_global);
+ double_fork_global = NULL;
+ errno = saved_errno;
+ return -1;
+ }
+ errno = 0;
+ while (true) {
+ ret = waitpid(double_fork_global->level1_pid,
+ &double_fork_global->level0_status, 0);
+ if (ret == -1 && errno == EINTR) {
+ continue;
+ }
+
+ break;
+ }
+
+ signal(SIGCHLD, double_fork_global->old_sig_chld);
+
+ if (double_fork_global->level0_status != 0) {
+ anonymous_shared_free(double_fork_global);
+ double_fork_global = NULL;
+ errno = ECHILD;
+ return -1;
+ }
+
+ if (double_fork_global->level2_pid == -1) {
+ int saved_errno = double_fork_global->level1_errno;
+ anonymous_shared_free(double_fork_global);
+ double_fork_global = NULL;
+ errno = saved_errno;
+ return -1;
+ }
+
+ child = double_fork_global->level2_pid;
+ anonymous_shared_free(double_fork_global);
+ double_fork_global = NULL;
+ return child;
+}
--- /dev/null
+/*
+ double fork to avoid SIGCHLD and zombies when the child exists.
+
+ Copyright (C) Stefan Metzmacher 2010
+
+ 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/>.
+*/
+
+#ifndef LIB_UTIL_DOUBLE_FORK_H
+#define LIB_UTIL_DOUBLE_FORK_H
+
+/**
+ * @brief do a fork() that avoids getting SIGCHLD and getting zombies
+ *
+ * This function is a workaround for the problem of using fork() in
+ * library code. In that case the library should avoid to set a global
+ * signal handler for SIGCHLD, because the application may wants to use its
+ * own handler!
+ *
+ * The child process will have handle SIGCHLD with SIG_DFL, so the
+ * child might need to setup its own handler.
+ *
+ * @param[out] parent The PID of the parent process, if 0 is returned
+ * otherwise the variable will not be touched at all.
+ * It is possible to pass NULL.
+ *
+ * @return Value > 0 in the parent, 0 in the child, -1 on error with errno set.
+ */
+int double_fork(int *parent);
+
+#endif /* LIB_UTIL_DOUBLE_FORK_H */
#!/usr/bin/env python
bld.SAMBA_LIBRARY('samba-util',
- source='xfile.c debug.c fault.c signal.c system.c time.c genrand.c dprintf.c util_str.c rfc1738.c substitute.c util_strlist.c util_file.c data_blob.c util.c blocking.c util_net.c fsusage.c ms_fnmatch.c idtree.c become_daemon.c rbtree.c talloc_stack.c smb_threads.c params.c parmlist.c util_id.c select.c',
+ source='''xfile.c debug.c fault.c signal.c system.c time.c genrand.c
+ dprintf.c util_str.c rfc1738.c substitute.c util_strlist.c util_file.c
+ data_blob.c util.c blocking.c util_net.c fsusage.c ms_fnmatch.c
+ idtree.c become_daemon.c rbtree.c talloc_stack.c smb_threads.c
+ params.c parmlist.c util_id.c select.c double_fork.c''',
public_deps='talloc LIBCRYPTO CHARSET execinfo uid_wrapper',
- public_headers='attr.h byteorder.h data_blob.h debug.h memory.h safe_string.h time.h talloc_stack.h xfile.h dlinklist.h util.h',
+ public_headers='''attr.h byteorder.h data_blob.h debug.h memory.h
+ safe_string.h time.h talloc_stack.h xfile.h dlinklist.h
+ util.h double_fork.h''',
header_path= [ ('dlinklist.h util.h', '.'), ('*', 'util') ],
local_include=False,
vnum='0.0.1',