2003-04-03 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Fri, 4 Apr 2003 01:55:28 +0000 (01:55 +0000)
committerHavoc Pennington <hp@redhat.com>
Fri, 4 Apr 2003 01:55:28 +0000 (01:55 +0000)
* dbus/dbus-spawn.c: Move dbus-spawn into a separate file
in preparation for modifying it, dbus-sysdeps is getting
a bit unmanageable.

ChangeLog
dbus/Makefile.am
dbus/dbus-spawn.c [new file with mode: 0644]
dbus/dbus-spawn.h [new file with mode: 0644]
dbus/dbus-sysdeps.c
dbus/dbus-sysdeps.h

index 363fc3c..d3ca8ed 100644 (file)
--- 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
index 2729380..2065e75 100644 (file)
@@ -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 (file)
index 0000000..27fb6e6
--- /dev/null
@@ -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 (file)
index 0000000..04348d7
--- /dev/null
@@ -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 */
index 17da1fb..7e635d9 100644 (file)
@@ -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);
  */
index 113db5e..5057f52 100644 (file)
@@ -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
  *