daemon fix and development
[platform/upstream/dbus.git] / dbus / dbus-spawn.c
index c3be1b7..f8d5fd1 100644 (file)
@@ -205,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 */
@@ -257,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
@@ -273,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)
         {
@@ -341,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);
 
@@ -476,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
@@ -752,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;
   
@@ -774,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;
 }
 
@@ -913,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
 
@@ -924,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++)
@@ -1079,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);
         }
@@ -1163,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;
     }
@@ -1178,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;
     }
@@ -1218,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,
@@ -1272,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)
@@ -1330,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))
@@ -1342,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);
 
@@ -1371,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))
@@ -1383,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);
 
@@ -1412,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))
@@ -1427,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);