Merge "[lib-fix] Fixed return value of kdbus_write_msg Change-Id: Ia3a45a6a79b4078929...
[platform/upstream/dbus.git] / dbus / dbus-spawn.c
index 35ccba6..f8d5fd1 100644 (file)
  * 
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
+
+#include <config.h>
+
 #include "dbus-spawn.h"
 #include "dbus-sysdeps-unix.h"
 #include "dbus-internals.h"
@@ -36,6 +39,8 @@
 #include <errno.h>
 #endif
 
+extern char **environ;
+
 /**
  * @addtogroup DBusInternalsUtils
  * @{
@@ -72,8 +77,8 @@ read_ints (int        fd,
   
   while (TRUE)
     {
-      size_t chunk;
-      ssize_t to_read;
+      ssize_t chunk;
+      size_t to_read;
 
       to_read = sizeof (int) * n_ints_in_buf - bytes;
 
@@ -127,9 +132,9 @@ read_pid (int        fd,
   
   while (TRUE)
     {
-      size_t chunk;    
-      ssize_t to_read;
-      
+      ssize_t chunk;
+      size_t to_read;
+
       to_read = sizeof (pid_t) - bytes;
 
       if (to_read == 0)
@@ -200,6 +205,9 @@ struct DBusBabysitter
   DBusWatch *error_watch; /**< Error pipe watch */
   DBusWatch *sitter_watch; /**< Sitter pipe watch */
 
+  DBusBabysitterFinishedFunc finished_cb;
+  void *finished_data;
+
   int errnum; /**< Error number */
   int status; /**< Exit status code */
   unsigned int have_child_status : 1; /**< True if child status has been reaped */
@@ -252,6 +260,9 @@ _dbus_babysitter_ref (DBusBabysitter *sitter)
   return sitter;
 }
 
+static void close_socket_to_babysitter  (DBusBabysitter *sitter);
+static void close_error_pipe_from_child (DBusBabysitter *sitter);
+
 /**
  * Decrement the reference count on the babysitter object.
  * When the reference count of the babysitter object reaches
@@ -268,25 +279,17 @@ _dbus_babysitter_unref (DBusBabysitter *sitter)
   
   sitter->refcount -= 1;
   if (sitter->refcount == 0)
-    {      
-      if (sitter->socket_to_babysitter >= 0)
-        {
-          /* If we haven't forked other babysitters
-           * since this babysitter and socket were
-           * created then this close will cause the
-           * babysitter to wake up from poll with
-           * a hangup and then the babysitter will
-           * quit itself.
-           */
-          _dbus_close_socket (sitter->socket_to_babysitter, NULL);
-          sitter->socket_to_babysitter = -1;
-        }
+    {
+      /* If we haven't forked other babysitters
+       * since this babysitter and socket were
+       * created then this close will cause the
+       * babysitter to wake up from poll with
+       * a hangup and then the babysitter will
+       * quit itself.
+       */
+      close_socket_to_babysitter (sitter);
 
-      if (sitter->error_pipe_from_child >= 0)
-        {
-          _dbus_close_socket (sitter->error_pipe_from_child, NULL);
-          sitter->error_pipe_from_child = -1;
-        }
+      close_error_pipe_from_child (sitter);
 
       if (sitter->sitter_pid > 0)
         {
@@ -336,21 +339,7 @@ _dbus_babysitter_unref (DBusBabysitter *sitter)
 
           sitter->sitter_pid = -1;
         }
-      
-      if (sitter->error_watch)
-        {
-          _dbus_watch_invalidate (sitter->error_watch);
-          _dbus_watch_unref (sitter->error_watch);
-          sitter->error_watch = NULL;
-        }
 
-      if (sitter->sitter_watch)
-        {
-          _dbus_watch_invalidate (sitter->sitter_watch);
-          _dbus_watch_unref (sitter->sitter_watch);
-          sitter->sitter_watch = NULL;
-        }
-      
       if (sitter->watches)
         _dbus_watch_list_free (sitter->watches);
 
@@ -471,16 +460,42 @@ static void
 close_socket_to_babysitter (DBusBabysitter *sitter)
 {
   _dbus_verbose ("Closing babysitter\n");
-  _dbus_close_socket (sitter->socket_to_babysitter, NULL);
-  sitter->socket_to_babysitter = -1;
+
+  if (sitter->sitter_watch != NULL)
+    {
+      _dbus_assert (sitter->watches != NULL);
+      _dbus_watch_list_remove_watch (sitter->watches,  sitter->sitter_watch);
+      _dbus_watch_invalidate (sitter->sitter_watch);
+      _dbus_watch_unref (sitter->sitter_watch);
+      sitter->sitter_watch = NULL;
+    }
+
+  if (sitter->socket_to_babysitter >= 0)
+    {
+      _dbus_close_socket (sitter->socket_to_babysitter, NULL);
+      sitter->socket_to_babysitter = -1;
+    }
 }
 
 static void
 close_error_pipe_from_child (DBusBabysitter *sitter)
 {
   _dbus_verbose ("Closing child error\n");
-  _dbus_close_socket (sitter->error_pipe_from_child, NULL);
-  sitter->error_pipe_from_child = -1;
+
+  if (sitter->error_watch != NULL)
+    {
+      _dbus_assert (sitter->watches != NULL);
+      _dbus_watch_list_remove_watch (sitter->watches,  sitter->error_watch);
+      _dbus_watch_invalidate (sitter->error_watch);
+      _dbus_watch_unref (sitter->error_watch);
+      sitter->error_watch = NULL;
+    }
+
+  if (sitter->error_pipe_from_child >= 0)
+    {
+      _dbus_close_socket (sitter->error_pipe_from_child, NULL);
+      sitter->error_pipe_from_child = -1;
+    }
 }
 
 static void
@@ -747,7 +762,7 @@ handle_watch (DBusWatch       *watch,
               unsigned int     condition,
               void            *data)
 {
-  DBusBabysitter *sitter = data;
+  DBusBabysitter *sitter = _dbus_babysitter_ref (data);
   int revents;
   int fd;
   
@@ -769,7 +784,20 @@ handle_watch (DBusWatch       *watch,
   while (LIVE_CHILDREN (sitter) &&
          babysitter_iteration (sitter, FALSE))
     ;
-  
+
+  /* fd.o #32992: if the handle_* methods closed their sockets, they previously
+   * didn't always remove the watches. Check that we don't regress. */
+  _dbus_assert (sitter->socket_to_babysitter != -1 || sitter->sitter_watch == NULL);
+  _dbus_assert (sitter->error_pipe_from_child != -1 || sitter->error_watch == NULL);
+
+  if (_dbus_babysitter_get_child_exited (sitter) &&
+      sitter->finished_cb != NULL)
+    {
+      sitter->finished_cb (sitter, sitter->finished_data);
+      sitter->finished_cb = NULL;
+    }
+
+  _dbus_babysitter_unref (sitter);
   return TRUE;
 }
 
@@ -803,9 +831,25 @@ static dbus_bool_t
 make_pipe (int         p[2],
            DBusError  *error)
 {
+  int retval;
+
+#ifdef HAVE_PIPE2
+  dbus_bool_t cloexec_done;
+
+  retval = pipe2 (p, O_CLOEXEC);
+  cloexec_done = retval >= 0;
+
+  /* Check if kernel seems to be too old to know pipe2(). We assume
+     that if pipe2 is available, O_CLOEXEC is too.  */
+  if (retval < 0 && errno == ENOSYS)
+#endif
+    {
+      retval = pipe(p);
+    }
+
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-  
-  if (pipe (p) < 0)
+
+  if (retval < 0)
     {
       dbus_set_error (error,
                      DBUS_ERROR_SPAWN_FAILED,
@@ -814,6 +858,14 @@ make_pipe (int         p[2],
       return FALSE;
     }
 
+#ifdef HAVE_PIPE2
+  if (!cloexec_done)
+#endif
+    {
+      _dbus_fd_set_close_on_exec (p[0]);
+      _dbus_fd_set_close_on_exec (p[1]);
+    }
+
   return TRUE;
 }
 
@@ -884,7 +936,7 @@ do_exec (int                       child_err_report_fd,
         DBusSpawnChildSetupFunc   child_setup,
         void                     *user_data)
 {
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
   int i, max_open;
 #endif
 
@@ -895,7 +947,7 @@ do_exec (int                       child_err_report_fd,
   if (child_setup)
     (* child_setup) (user_data);
 
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
   max_open = sysconf (_SC_OPEN_MAX);
   
   for (i = 3; i < max_open; i++)
@@ -914,8 +966,6 @@ do_exec (int                       child_err_report_fd,
 
   if (envp == NULL)
     {
-      extern char **environ;
-
       _dbus_assert (environ != NULL);
 
       envp = environ;
@@ -1052,7 +1102,10 @@ babysit (pid_t grandchild_pid,
       else if (pfds[1].revents & _DBUS_POLLIN)
         {
           char b;
-          read (sigchld_pipe[READ_END], &b, 1);
+          if (read (sigchld_pipe[READ_END], &b, 1) == -1)
+            {
+              /* ignore */
+            }
           /* do waitpid check */
           check_babysit_events (grandchild_pid, parent_pipe, 0);
         }
@@ -1117,15 +1170,9 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter          **sitter_p,
   if (!make_pipe (child_err_report_pipe, error))
     goto cleanup_and_fail;
 
-  _dbus_fd_set_close_on_exec (child_err_report_pipe[READ_END]);
-  _dbus_fd_set_close_on_exec (child_err_report_pipe[WRITE_END]);
-
   if (!_dbus_full_duplex_pipe (&babysitter_pipe[0], &babysitter_pipe[1], TRUE, error))
     goto cleanup_and_fail;
 
-  _dbus_fd_set_close_on_exec (babysitter_pipe[0]);
-  _dbus_fd_set_close_on_exec (babysitter_pipe[1]);
-
   /* Setting up the babysitter is only useful in the parent,
    * but we don't want to run out of memory and fail
    * after we've already forked, since then we'd leak
@@ -1142,6 +1189,12 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter          **sitter_p,
         
   if (!_dbus_watch_list_add_watch (sitter->watches,  sitter->error_watch))
     {
+      /* we need to free it early so the destructor won't try to remove it
+       * without it having been added, which DBusLoop doesn't allow */
+      _dbus_watch_invalidate (sitter->error_watch);
+      _dbus_watch_unref (sitter->error_watch);
+      sitter->error_watch = NULL;
+
       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
       goto cleanup_and_fail;
     }
@@ -1157,6 +1210,12 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter          **sitter_p,
       
   if (!_dbus_watch_list_add_watch (sitter->watches,  sitter->sitter_watch))
     {
+      /* we need to free it early so the destructor won't try to remove it
+       * without it having been added, which DBusLoop doesn't allow */
+      _dbus_watch_invalidate (sitter->sitter_watch);
+      _dbus_watch_unref (sitter->sitter_watch);
+      sitter->sitter_watch = NULL;
+
       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
       goto cleanup_and_fail;
     }
@@ -1197,7 +1256,11 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter          **sitter_p,
           _dbus_assert_not_reached ("Got to code after write_err_and_exit()");
        }
       else if (grandchild_pid == 0)
-       {
+      {
+          /* Go back to ignoring SIGPIPE, since it's evil
+           */
+          signal (SIGPIPE, SIG_IGN);
+
          do_exec (child_err_report_pipe[WRITE_END],
                   argv,
                   env,
@@ -1251,9 +1314,42 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter          **sitter_p,
   return FALSE;
 }
 
+void
+_dbus_babysitter_set_result_function  (DBusBabysitter             *sitter,
+                                       DBusBabysitterFinishedFunc  finished,
+                                       void                       *user_data)
+{
+  sitter->finished_cb = finished;
+  sitter->finished_data = user_data;
+}
+
 /** @} */
 
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+
+static char *
+get_test_exec (const char *exe,
+               DBusString *scratch_space)
+{
+  const char *dbus_test_exec;
+
+  dbus_test_exec = _dbus_getenv ("DBUS_TEST_EXEC");
+
+  if (dbus_test_exec == NULL)
+    dbus_test_exec = DBUS_TEST_EXEC;
+
+  if (!_dbus_string_init (scratch_space))
+    return NULL;
+
+  if (!_dbus_string_append_printf (scratch_space, "%s/%s%s",
+                                   dbus_test_exec, exe, DBUS_EXEEXT))
+    {
+      _dbus_string_free (scratch_space);
+      return NULL;
+    }
+
+  return _dbus_string_get_data (scratch_space);
+}
 
 static void
 _dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter)
@@ -1309,10 +1405,18 @@ check_spawn_segfault (void *data)
   char *argv[4] = { NULL, NULL, NULL, NULL };
   DBusBabysitter *sitter = NULL;
   DBusError error = DBUS_ERROR_INIT;
+  DBusString argv0;
 
   /*** Test launching segfault binary */
-  
-  argv[0] = TEST_SEGFAULT_BINARY;
+
+  argv[0] = get_test_exec ("test-segfault", &argv0);
+
+  if (argv[0] == NULL)
+    {
+      /* OOM was simulated, never mind */
+      return TRUE;
+    }
+
   if (_dbus_spawn_async_with_babysitter (&sitter, argv,
                                          NULL, NULL, NULL,
                                          &error))
@@ -1321,6 +1425,8 @@ check_spawn_segfault (void *data)
       _dbus_babysitter_set_child_exit_error (sitter, &error);
     }
 
+  _dbus_string_free (&argv0);
+
   if (sitter)
     _dbus_babysitter_unref (sitter);
 
@@ -1350,10 +1456,18 @@ check_spawn_exit (void *data)
   char *argv[4] = { NULL, NULL, NULL, NULL };
   DBusBabysitter *sitter = NULL;
   DBusError error = DBUS_ERROR_INIT;
+  DBusString argv0;
 
   /*** Test launching exit failure binary */
-  
-  argv[0] = TEST_EXIT_BINARY;
+
+  argv[0] = get_test_exec ("test-exit", &argv0);
+
+  if (argv[0] == NULL)
+    {
+      /* OOM was simulated, never mind */
+      return TRUE;
+    }
+
   if (_dbus_spawn_async_with_babysitter (&sitter, argv,
                                          NULL, NULL, NULL,
                                          &error))
@@ -1362,6 +1476,8 @@ check_spawn_exit (void *data)
       _dbus_babysitter_set_child_exit_error (sitter, &error);
     }
 
+  _dbus_string_free (&argv0);
+
   if (sitter)
     _dbus_babysitter_unref (sitter);
 
@@ -1391,10 +1507,18 @@ check_spawn_and_kill (void *data)
   char *argv[4] = { NULL, NULL, NULL, NULL };
   DBusBabysitter *sitter = NULL;
   DBusError error = DBUS_ERROR_INIT;
+  DBusString argv0;
 
   /*** Test launching sleeping binary then killing it */
 
-  argv[0] = TEST_SLEEP_FOREVER_BINARY;
+  argv[0] = get_test_exec ("test-sleep-forever", &argv0);
+
+  if (argv[0] == NULL)
+    {
+      /* OOM was simulated, never mind */
+      return TRUE;
+    }
+
   if (_dbus_spawn_async_with_babysitter (&sitter, argv,
                                          NULL, NULL, NULL,
                                          &error))
@@ -1406,6 +1530,8 @@ check_spawn_and_kill (void *data)
       _dbus_babysitter_set_child_exit_error (sitter, &error);
     }
 
+  _dbus_string_free (&argv0);
+
   if (sitter)
     _dbus_babysitter_unref (sitter);