Logo Search packages:      
Sourcecode: dbus version File versions

dbus_bool_t _dbus_spawn_async_with_babysitter ( DBusBabysitter **  sitter_p,
char **  argv,
DBusSpawnChildSetupFunc  child_setup,
void *  user_data,
DBusError error 
)

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().

Also creates a "babysitter" which tracks the status of the child process, advising the parent if the child exits. If the spawn fails, no babysitter is created. If sitter_p is NULL, no babysitter is kept.

Parameters:
sitter_p return location for babysitter or NULL
argv the executable and arguments
child_setup function to call in child pre-exec()
user_data user data for setup function
error error object to be filled in if function fails
Returns:
TRUE on success, FALSE if error is filled in

Definition at line 1007 of file dbus-spawn.c.

References _dbus_assert_not_reached, _dbus_babysitter_unref(), _dbus_fd_set_close_on_exec(), _dbus_full_duplex_pipe(), _dbus_strdup(), _dbus_strerror(), _dbus_watch_list_add_watch(), _dbus_watch_new(), dbus_set_error(), DBusBabysitter::error_pipe_from_child, DBusBabysitter::error_watch, DBusBabysitter::executable, FALSE, NULL, READ_END, DBusBabysitter::sitter_pid, DBusBabysitter::sitter_watch, DBusBabysitter::socket_to_babysitter, TRUE, DBusBabysitter::watches, and WRITE_END.

{
  DBusBabysitter *sitter;
  int child_err_report_pipe[2] = { -1, -1 };
  int babysitter_pipe[2] = { -1, -1 };
  pid_t pid;
  
  _DBUS_ASSERT_ERROR_IS_CLEAR (error);

  *sitter_p = NULL;
  sitter = NULL;

  sitter = _dbus_babysitter_new ();
  if (sitter == NULL)
    {
      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
      return FALSE;
    }

  sitter->executable = _dbus_strdup (argv[0]);
  if (sitter->executable == NULL)
    {
      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
      goto cleanup_and_fail;
    }
  
  if (!make_pipe (child_err_report_pipe, error))
    goto cleanup_and_fail;

  _dbus_fd_set_close_on_exec (child_err_report_pipe[READ_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
   * child processes everywhere.
   */
  sitter->error_watch = _dbus_watch_new (child_err_report_pipe[READ_END],
                                         DBUS_WATCH_READABLE,
                                         TRUE, handle_watch, sitter, NULL);
  if (sitter->error_watch == NULL)
    {
      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
      goto cleanup_and_fail;
    }
        
  if (!_dbus_watch_list_add_watch (sitter->watches,  sitter->error_watch))
    {
      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
      goto cleanup_and_fail;
    }
      
  sitter->sitter_watch = _dbus_watch_new (babysitter_pipe[0],
                                          DBUS_WATCH_READABLE,
                                          TRUE, handle_watch, sitter, NULL);
  if (sitter->sitter_watch == NULL)
    {
      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
      goto cleanup_and_fail;
    }
      
  if (!_dbus_watch_list_add_watch (sitter->watches,  sitter->sitter_watch))
    {
      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
      goto cleanup_and_fail;
    }

  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
  
  pid = fork ();
  
  if (pid < 0)
    {
      dbus_set_error (error,
                  DBUS_ERROR_SPAWN_FORK_FAILED,
                  "Failed to fork (%s)",
                  _dbus_strerror (errno));
      goto cleanup_and_fail;
    }
  else if (pid == 0)
    {
      /* Immediate child, this is the babysitter process. */
      int grandchild_pid;
      
      /* 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. */
      close_and_invalidate (&child_err_report_pipe[READ_END]);
      close_and_invalidate (&babysitter_pipe[0]);
      
      /* Create the child that will exec () */
      grandchild_pid = fork ();
      
      if (grandchild_pid < 0)
      {
        write_err_and_exit (babysitter_pipe[1],
                        CHILD_FORK_FAILED);
          _dbus_assert_not_reached ("Got to code after write_err_and_exit()");
      }
      else if (grandchild_pid == 0)
      {
        do_exec (child_err_report_pipe[WRITE_END],
               argv,
               child_setup, user_data);
          _dbus_assert_not_reached ("Got to code after exec() - should have exited on error");
      }
      else
      {
          babysit (grandchild_pid, babysitter_pipe[1]);
          _dbus_assert_not_reached ("Got to code after babysit()");
      }
    }
  else
    {      
      /* Close the uncared-about ends of the pipes */
      close_and_invalidate (&child_err_report_pipe[WRITE_END]);
      close_and_invalidate (&babysitter_pipe[1]);

      sitter->socket_to_babysitter = babysitter_pipe[0];
      babysitter_pipe[0] = -1;
      
      sitter->error_pipe_from_child = child_err_report_pipe[READ_END];
      child_err_report_pipe[READ_END] = -1;

      sitter->sitter_pid = pid;

      if (sitter_p != NULL)
        *sitter_p = sitter;
      else
        _dbus_babysitter_unref (sitter);

      _DBUS_ASSERT_ERROR_IS_CLEAR (error);
      
      return TRUE;
    }

 cleanup_and_fail:

  _DBUS_ASSERT_ERROR_IS_SET (error);
  
  close_and_invalidate (&child_err_report_pipe[READ_END]);
  close_and_invalidate (&child_err_report_pipe[WRITE_END]);
  close_and_invalidate (&babysitter_pipe[0]);
  close_and_invalidate (&babysitter_pipe[1]);

  if (sitter != NULL)
    _dbus_babysitter_unref (sitter);
  
  return FALSE;
}


Generated by  Doxygen 1.6.0   Back to index