summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--dbus/Makefile.am2
-rw-r--r--dbus/dbus-spawn.c339
-rw-r--r--dbus/dbus-spawn.h35
-rw-r--r--dbus/dbus-sysdeps.c301
-rw-r--r--dbus/dbus-sysdeps.h3
6 files changed, 385 insertions, 301 deletions
diff --git a/ChangeLog b/ChangeLog
index 363fc3ce..d3ca8ede 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2003-04-03 Havoc Pennington <hp@pobox.com>
+
+ * dbus/dbus-spawn.c: Move dbus-spawn into a separate file
+ in preparation for modifying it, dbus-sysdeps is getting
+ a bit unmanageable.
+
2003-04-03 Havoc Pennington <hp@redhat.com>
* bus/loop.h, bus/loop.c: make the mainloop an object so we can
diff --git a/dbus/Makefile.am b/dbus/Makefile.am
index 27293807..2065e75e 100644
--- a/dbus/Makefile.am
+++ b/dbus/Makefile.am
@@ -84,6 +84,8 @@ UTIL_SOURCES= \
dbus-mempool.h \
dbus-message-builder.c \
dbus-message-builder.h \
+ dbus-spawn.c \
+ dbus-spawn.h \
dbus-string.c \
dbus-string.h \
dbus-string-private.h \
diff --git a/dbus/dbus-spawn.c b/dbus/dbus-spawn.c
new file mode 100644
index 00000000..27fb6e6c
--- /dev/null
+++ b/dbus/dbus-spawn.c
@@ -0,0 +1,339 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-spawn.c Wrapper around fork/exec
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "dbus-spawn.h"
+#include "dbus-sysdeps.h"
+#include "dbus-internals.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+/**
+ * @addtogroup DBusInternalsUtils
+ * @{
+ */
+
+/* Avoids a danger in threaded situations (calling close()
+ * on a file descriptor twice, and another thread has
+ * re-opened it since the first close)
+ */
+static int
+close_and_invalidate (int *fd)
+{
+ int ret;
+
+ if (*fd < 0)
+ return -1;
+ else
+ {
+ ret = close (*fd);
+ *fd = -1;
+ }
+
+ return ret;
+}
+
+static dbus_bool_t
+make_pipe (int p[2],
+ DBusError *error)
+{
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (pipe (p) < 0)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_SPAWN_FAILED,
+ "Failed to create pipe for communicating with child process (%s)",
+ _dbus_errno_to_string (errno));
+ return FALSE;
+ }
+ else
+ {
+ _dbus_fd_set_close_on_exec (p[0]);
+ _dbus_fd_set_close_on_exec (p[1]);
+ return TRUE;
+ }
+}
+
+enum
+{
+ CHILD_CHDIR_FAILED,
+ CHILD_EXEC_FAILED,
+ CHILD_DUP2_FAILED,
+ CHILD_FORK_FAILED
+};
+
+static void
+write_err_and_exit (int fd, int msg)
+{
+ int en = errno;
+
+ write (fd, &msg, sizeof(msg));
+ write (fd, &en, sizeof(en));
+
+ _exit (1);
+}
+
+static dbus_bool_t
+read_ints (int fd,
+ int *buf,
+ int n_ints_in_buf,
+ int *n_ints_read,
+ DBusError *error)
+{
+ size_t bytes = 0;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ while (TRUE)
+ {
+ size_t chunk;
+
+ if (bytes >= sizeof(int)*2)
+ break; /* give up, who knows what happened, should not be
+ * possible.
+ */
+
+ again:
+ chunk = read (fd,
+ ((char*)buf) + bytes,
+ sizeof(int) * n_ints_in_buf - bytes);
+ if (chunk < 0 && errno == EINTR)
+ goto again;
+
+ if (chunk < 0)
+ {
+ /* Some weird shit happened, bail out */
+
+ dbus_set_error (error,
+ DBUS_ERROR_SPAWN_FAILED,
+ "Failed to read from child pipe (%s)",
+ _dbus_errno_to_string (errno));
+
+ return FALSE;
+ }
+ else if (chunk == 0)
+ break; /* EOF */
+ else /* chunk > 0 */
+ bytes += chunk;
+ }
+
+ *n_ints_read = (int)(bytes / sizeof(int));
+
+ return TRUE;
+}
+
+static void
+do_exec (int child_err_report_fd,
+ char **argv,
+ DBusSpawnChildSetupFunc child_setup,
+ void *user_data)
+{
+#ifdef DBUS_BUILD_TESTS
+ int i, max_open;
+#endif
+
+ if (child_setup)
+ (* child_setup) (user_data);
+
+#ifdef DBUS_BUILD_TESTS
+ max_open = sysconf (_SC_OPEN_MAX);
+
+ for (i = 3; i < max_open; i++)
+ {
+ int retval;
+
+ retval = fcntl (i, F_GETFD);
+
+ if (retval != -1 && !(retval & FD_CLOEXEC))
+ _dbus_warn ("Fd %d did not have the close-on-exec flag set!\n", i);
+ }
+#endif
+
+ execv (argv[0], argv);
+
+ /* Exec failed */
+ write_err_and_exit (child_err_report_fd,
+ CHILD_EXEC_FAILED);
+
+}
+
+/**
+ * Spawns a new process. The executable name and argv[0]
+ * are the same, both are provided in argv[0]. The child_setup
+ * function is passed the given user_data and is run in the child
+ * just before calling exec().
+ *
+ * @todo this code should be reviewed/double-checked as it's fairly
+ * complex and no one has reviewed it yet.
+ *
+ * @param argv the executable and arguments
+ * @param child_setup function to call in child pre-exec()
+ * @param user_data user data for setup function
+ * @param error error object to be filled in if function fails
+ * @returns #TRUE on success, #FALSE if error is filled in
+ */
+dbus_bool_t
+_dbus_spawn_async (char **argv,
+ DBusSpawnChildSetupFunc child_setup,
+ void *user_data,
+ DBusError *error)
+{
+ int pid = -1, grandchild_pid;
+ int child_err_report_pipe[2] = { -1, -1 };
+ int status;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!make_pipe (child_err_report_pipe, error))
+ return FALSE;
+
+ pid = fork ();
+
+ if (pid < 0)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_SPAWN_FORK_FAILED,
+ "Failed to fork (%s)",
+ _dbus_errno_to_string (errno));
+ return FALSE;
+ }
+ else if (pid == 0)
+ {
+ /* Immediate child. */
+
+ /* Be sure we crash if the parent exits
+ * and we write to the err_report_pipe
+ */
+ signal (SIGPIPE, SIG_DFL);
+
+ /* Close the parent's end of the pipes;
+ * not needed in the close_descriptors case,
+ * though
+ */
+ close_and_invalidate (&child_err_report_pipe[0]);
+
+ /* We need to fork an intermediate child that launches the
+ * final child. The purpose of the intermediate child
+ * is to exit, so we can waitpid() it immediately.
+ * Then the grandchild will not become a zombie.
+ */
+ grandchild_pid = fork ();
+
+ if (grandchild_pid < 0)
+ {
+ write_err_and_exit (child_err_report_pipe[1],
+ CHILD_FORK_FAILED);
+ }
+ else if (grandchild_pid == 0)
+ {
+ do_exec (child_err_report_pipe[1],
+ argv,
+ child_setup, user_data);
+ }
+ else
+ {
+ _exit (0);
+ }
+ }
+ else
+ {
+ /* Parent */
+
+ int buf[2];
+ int n_ints = 0;
+
+ /* Close the uncared-about ends of the pipes */
+ close_and_invalidate (&child_err_report_pipe[1]);
+
+ wait_again:
+ if (waitpid (pid, &status, 0) < 0)
+ {
+ if (errno == EINTR)
+ goto wait_again;
+ else if (errno == ECHILD)
+ ; /* do nothing, child already reaped */
+ else
+ _dbus_warn ("waitpid() should not fail in "
+ "'_dbus_spawn_async'");
+ }
+
+ if (!read_ints (child_err_report_pipe[0],
+ buf, 2, &n_ints,
+ error))
+ goto cleanup_and_fail;
+
+ if (n_ints >= 2)
+ {
+ /* Error from the child. */
+ switch (buf[0])
+ {
+ default:
+ dbus_set_error (error,
+ DBUS_ERROR_SPAWN_FAILED,
+ "Unknown error executing child process \"%s\"",
+ argv[0]);
+ break;
+ }
+
+ goto cleanup_and_fail;
+ }
+
+
+ /* Success against all odds! return the information */
+ close_and_invalidate (&child_err_report_pipe[0]);
+
+ return TRUE;
+ }
+
+ cleanup_and_fail:
+
+ /* There was an error from the Child, reap the child to avoid it being
+ a zombie.
+ */
+ if (pid > 0)
+ {
+ wait_failed:
+ if (waitpid (pid, NULL, 0) < 0)
+ {
+ if (errno == EINTR)
+ goto wait_failed;
+ else if (errno == ECHILD)
+ ; /* do nothing, child already reaped */
+ else
+ _dbus_warn ("waitpid() should not fail in "
+ "'_dbus_spawn_async'");
+ }
+ }
+
+ close_and_invalidate (&child_err_report_pipe[0]);
+ close_and_invalidate (&child_err_report_pipe[1]);
+
+ return FALSE;
+}
+
+
+/** @} */
diff --git a/dbus/dbus-spawn.h b/dbus/dbus-spawn.h
new file mode 100644
index 00000000..04348d75
--- /dev/null
+++ b/dbus/dbus-spawn.h
@@ -0,0 +1,35 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-spawn.h Wrapper around fork/exec
+ *
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef DBUS_SPAWN_H
+#define DBUS_SPAWN_H
+
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-errors.h>
+
+DBUS_BEGIN_DECLS;
+
+DBUS_END_DECLS;
+
+#endif /* DBUS_SPAWN_H */
diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c
index 17da1fbe..7e635d9d 100644
--- a/dbus/dbus-sysdeps.c
+++ b/dbus/dbus-sysdeps.c
@@ -2,6 +2,7 @@
/* dbus-sysdeps.c Wrappers around system/libc features (internal to D-BUS implementation)
*
* Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
*
* Licensed under the Academic Free License version 1.2
*
@@ -32,7 +33,6 @@
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
-#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <dirent.h>
@@ -2596,305 +2596,6 @@ _dbus_strerror (int error_number)
return msg;
}
-/* Avoids a danger in threaded situations (calling close()
- * on a file descriptor twice, and another thread has
- * re-opened it since the first close)
- */
-static int
-close_and_invalidate (int *fd)
-{
- int ret;
-
- if (*fd < 0)
- return -1;
- else
- {
- ret = close (*fd);
- *fd = -1;
- }
-
- return ret;
-}
-
-static dbus_bool_t
-make_pipe (int p[2],
- DBusError *error)
-{
- _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-
- if (pipe (p) < 0)
- {
- dbus_set_error (error,
- DBUS_ERROR_SPAWN_FAILED,
- "Failed to create pipe for communicating with child process (%s)",
- _dbus_errno_to_string (errno));
- return FALSE;
- }
- else
- {
- _dbus_fd_set_close_on_exec (p[0]);
- _dbus_fd_set_close_on_exec (p[1]);
- return TRUE;
- }
-}
-
-enum
-{
- CHILD_CHDIR_FAILED,
- CHILD_EXEC_FAILED,
- CHILD_DUP2_FAILED,
- CHILD_FORK_FAILED
-};
-
-static void
-write_err_and_exit (int fd, int msg)
-{
- int en = errno;
-
- write (fd, &msg, sizeof(msg));
- write (fd, &en, sizeof(en));
-
- _exit (1);
-}
-
-static dbus_bool_t
-read_ints (int fd,
- int *buf,
- int n_ints_in_buf,
- int *n_ints_read,
- DBusError *error)
-{
- size_t bytes = 0;
-
- _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-
- while (TRUE)
- {
- size_t chunk;
-
- if (bytes >= sizeof(int)*2)
- break; /* give up, who knows what happened, should not be
- * possible.
- */
-
- again:
- chunk = read (fd,
- ((char*)buf) + bytes,
- sizeof(int) * n_ints_in_buf - bytes);
- if (chunk < 0 && errno == EINTR)
- goto again;
-
- if (chunk < 0)
- {
- /* Some weird shit happened, bail out */
-
- dbus_set_error (error,
- DBUS_ERROR_SPAWN_FAILED,
- "Failed to read from child pipe (%s)",
- _dbus_errno_to_string (errno));
-
- return FALSE;
- }
- else if (chunk == 0)
- break; /* EOF */
- else /* chunk > 0 */
- bytes += chunk;
- }
-
- *n_ints_read = (int)(bytes / sizeof(int));
-
- return TRUE;
-}
-
-static void
-do_exec (int child_err_report_fd,
- char **argv,
- DBusSpawnChildSetupFunc child_setup,
- void *user_data)
-{
-#ifdef DBUS_BUILD_TESTS
- int i, max_open;
-#endif
-
- if (child_setup)
- (* child_setup) (user_data);
-
-#ifdef DBUS_BUILD_TESTS
- max_open = sysconf (_SC_OPEN_MAX);
-
- for (i = 3; i < max_open; i++)
- {
- int retval;
-
- retval = fcntl (i, F_GETFD);
-
- if (retval != -1 && !(retval & FD_CLOEXEC))
- _dbus_warn ("Fd %d did not have the close-on-exec flag set!\n", i);
- }
-#endif
-
- execv (argv[0], argv);
-
- /* Exec failed */
- write_err_and_exit (child_err_report_fd,
- CHILD_EXEC_FAILED);
-
-}
-
-/**
- * Spawns a new process. The executable name and argv[0]
- * are the same, both are provided in argv[0]. The child_setup
- * function is passed the given user_data and is run in the child
- * just before calling exec().
- *
- * @todo this code should be reviewed/double-checked as it's fairly
- * complex and no one has reviewed it yet.
- *
- * @param argv the executable and arguments
- * @param child_setup function to call in child pre-exec()
- * @param user_data user data for setup function
- * @param error error object to be filled in if function fails
- * @returns #TRUE on success, #FALSE if error is filled in
- */
-dbus_bool_t
-_dbus_spawn_async (char **argv,
- DBusSpawnChildSetupFunc child_setup,
- void *user_data,
- DBusError *error)
-{
- int pid = -1, grandchild_pid;
- int child_err_report_pipe[2] = { -1, -1 };
- int status;
-
- _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-
- if (!make_pipe (child_err_report_pipe, error))
- return FALSE;
-
- pid = fork ();
-
- if (pid < 0)
- {
- dbus_set_error (error,
- DBUS_ERROR_SPAWN_FORK_FAILED,
- "Failed to fork (%s)",
- _dbus_errno_to_string (errno));
- return FALSE;
- }
- else if (pid == 0)
- {
- /* Immediate child. */
-
- /* Be sure we crash if the parent exits
- * and we write to the err_report_pipe
- */
- signal (SIGPIPE, SIG_DFL);
-
- /* Close the parent's end of the pipes;
- * not needed in the close_descriptors case,
- * though
- */
- close_and_invalidate (&child_err_report_pipe[0]);
-
- /* We need to fork an intermediate child that launches the
- * final child. The purpose of the intermediate child
- * is to exit, so we can waitpid() it immediately.
- * Then the grandchild will not become a zombie.
- */
- grandchild_pid = fork ();
-
- if (grandchild_pid < 0)
- {
- write_err_and_exit (child_err_report_pipe[1],
- CHILD_FORK_FAILED);
- }
- else if (grandchild_pid == 0)
- {
- do_exec (child_err_report_pipe[1],
- argv,
- child_setup, user_data);
- }
- else
- {
- _exit (0);
- }
- }
- else
- {
- /* Parent */
-
- int buf[2];
- int n_ints = 0;
-
- /* Close the uncared-about ends of the pipes */
- close_and_invalidate (&child_err_report_pipe[1]);
-
- wait_again:
- if (waitpid (pid, &status, 0) < 0)
- {
- if (errno == EINTR)
- goto wait_again;
- else if (errno == ECHILD)
- ; /* do nothing, child already reaped */
- else
- _dbus_warn ("waitpid() should not fail in "
- "'_dbus_spawn_async'");
- }
-
- if (!read_ints (child_err_report_pipe[0],
- buf, 2, &n_ints,
- error))
- goto cleanup_and_fail;
-
- if (n_ints >= 2)
- {
- /* Error from the child. */
- switch (buf[0])
- {
- default:
- dbus_set_error (error,
- DBUS_ERROR_SPAWN_FAILED,
- "Unknown error executing child process \"%s\"",
- argv[0]);
- break;
- }
-
- goto cleanup_and_fail;
- }
-
-
- /* Success against all odds! return the information */
- close_and_invalidate (&child_err_report_pipe[0]);
-
- return TRUE;
- }
-
- cleanup_and_fail:
-
- /* There was an error from the Child, reap the child to avoid it being
- a zombie.
- */
- if (pid > 0)
- {
- wait_failed:
- if (waitpid (pid, NULL, 0) < 0)
- {
- if (errno == EINTR)
- goto wait_failed;
- else if (errno == ECHILD)
- ; /* do nothing, child already reaped */
- else
- _dbus_warn ("waitpid() should not fail in "
- "'_dbus_spawn_async'");
- }
- }
-
- close_and_invalidate (&child_err_report_pipe[0]);
- close_and_invalidate (&child_err_report_pipe[1]);
-
- return FALSE;
-}
-
/**
* signal (SIGPIPE, SIG_IGN);
*/
diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h
index 113db5eb..5057f521 100644
--- a/dbus/dbus-sysdeps.h
+++ b/dbus/dbus-sysdeps.h
@@ -1,7 +1,8 @@
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-sysdeps.h Wrappers around system/libc features (internal to D-BUS implementation)
*
- * Copyright (C) 2002 Red Hat, Inc.
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
*
* Licensed under the Academic Free License version 1.2
*