示例#1
0
文件: dbus-spawn.c 项目: d-bus/dbus
/**
 * Spawns a new process.
 *
 * On Unix platforms, the child_setup function is passed the given
 * user_data and is run in the child after fork() but before calling exec().
 * This can be used to change uid, resource limits and so on.
 * On Windows, this functionality does not fit the multi-processing model
 * (Windows does the equivalent of fork() and exec() in a single API call),
 * and the child_setup function and its user_data are ignored.
 *
 * 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.
 *
 * @param sitter_p return location for babysitter or #NULL
 * @param log_name the name under which to log messages about this process being spawned
 * @param argv the executable and arguments
 * @param env the environment, or #NULL to copy the parent's
 * @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_with_babysitter (DBusBabysitter          **sitter_p,
                                   const char               *log_name,
                                   char             * const *argv,
                                   char                    **env,
                                   DBusSpawnFlags            flags,
                                   DBusSpawnChildSetupFunc   child_setup,
                                   void                     *user_data,
                                   DBusError                *error)
{
  DBusBabysitter *sitter;
  int child_err_report_pipe[2] = { -1, -1 };
  DBusSocket babysitter_pipe[2] = { DBUS_SOCKET_INIT, DBUS_SOCKET_INIT };
  pid_t pid;
#ifdef HAVE_SYSTEMD
  int fd_out = -1;
  int fd_err = -1;
#endif
  
  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
  _dbus_assert (argv[0] != NULL);

  if (sitter_p != NULL)
    *sitter_p = NULL;

  sitter = NULL;

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

  sitter->log_name = _dbus_strdup (log_name);
  if (sitter->log_name == NULL && log_name != NULL)
    {
      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
      goto cleanup_and_fail;
    }

  if (sitter->log_name == NULL)
    sitter->log_name = _dbus_strdup (argv[0]);

  if (sitter->log_name == 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;

  if (!_dbus_socketpair (&babysitter_pipe[0], &babysitter_pipe[1], TRUE, error))
    goto cleanup_and_fail;

  /* 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))
    {
      /* 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;
    }
      
  sitter->sitter_watch = _dbus_watch_new (babysitter_pipe[0].fd,
                                          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))
    {
      /* 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;
    }

  _DBUS_ASSERT_ERROR_IS_CLEAR (error);

#ifdef HAVE_SYSTEMD
  if (flags & DBUS_SPAWN_REDIRECT_OUTPUT)
    {
      /* This may fail, but it's not critical.
       * In particular, if we were compiled with journald support but are now
       * running on a non-systemd system, this is going to fail, so we
       * have to cope gracefully. */
      fd_out = sd_journal_stream_fd (sitter->log_name, LOG_INFO, FALSE);
      fd_err = sd_journal_stream_fd (sitter->log_name, LOG_WARNING, FALSE);
    }
#endif

  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].fd);
      
      /* Create the child that will exec () */
      grandchild_pid = fork ();
      
      if (grandchild_pid < 0)
	{
	  write_err_and_exit (babysitter_pipe[1].fd,
			      CHILD_FORK_FAILED);
          _dbus_assert_not_reached ("Got to code after write_err_and_exit()");
	}
      else if (grandchild_pid == 0)
      {
#ifdef __linux__
          int fd = -1;

#ifdef O_CLOEXEC
          fd = open ("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC);
#endif

          if (fd < 0)
            {
              fd = open ("/proc/self/oom_score_adj", O_WRONLY);
              _dbus_fd_set_close_on_exec (fd);
            }

          if (fd >= 0)
            {
              if (write (fd, "0", sizeof (char)) < 0)
                _dbus_warn ("writing oom_score_adj error: %s", strerror (errno));
              _dbus_close (fd, NULL);
            }
#endif
          /* Go back to ignoring SIGPIPE, since it's evil
           */
          signal (SIGPIPE, SIG_IGN);

          close_and_invalidate (&babysitter_pipe[1].fd);
#ifdef HAVE_SYSTEMD
	  /* log to systemd journal if possible */
	  if (fd_out >= 0)
            dup2 (fd_out, STDOUT_FILENO);
	  if (fd_err >= 0)
            dup2 (fd_err, STDERR_FILENO);
          close_and_invalidate (&fd_out);
          close_and_invalidate (&fd_err);
#endif
	  do_exec (child_err_report_pipe[WRITE_END],
		   argv,
		   env,
		   child_setup, user_data);
          _dbus_assert_not_reached ("Got to code after exec() - should have exited on error");
	}
      else
	{
          close_and_invalidate (&child_err_report_pipe[WRITE_END]);
#ifdef HAVE_SYSTEMD
          close_and_invalidate (&fd_out);
          close_and_invalidate (&fd_err);
#endif
          babysit (grandchild_pid, babysitter_pipe[1].fd);
          _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].fd);
#ifdef HAVE_SYSTEMD
      close_and_invalidate (&fd_out);
      close_and_invalidate (&fd_err);
#endif

      sitter->socket_to_babysitter = babysitter_pipe[0];
      babysitter_pipe[0].fd = -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_free_string_array (env);

      _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].fd);
  close_and_invalidate (&babysitter_pipe[1].fd);
#ifdef HAVE_SYSTEMD
  close_and_invalidate (&fd_out);
  close_and_invalidate (&fd_err);
#endif

  if (sitter != NULL)
    _dbus_babysitter_unref (sitter);
  
  return FALSE;
}
示例#2
0
int
main (int argc, char **argv)
{
  const char *prev_arg;
  const char *shname;
  const char *runprog = NULL;
  int remaining_args = 0;
  int exit_with_session;
  int exit_with_x11 = FALSE;
  int binary_syntax = FALSE;
  int c_shell_syntax = FALSE;
  int bourne_shell_syntax = FALSE;
  int auto_shell_syntax = FALSE;
  int autolaunch = FALSE;
  int requires_arg = FALSE;
  int close_stderr = FALSE;
  int i;
  int ret;
  int bus_pid_to_launcher_pipe[2];
  int bus_pid_to_babysitter_pipe[2];
  int bus_address_to_launcher_pipe[2];
  char *config_file;
  dbus_bool_t user_bus_supported = FALSE;
  DBusString user_bus;
  const char *error_str;

  exit_with_session = FALSE;
  config_file = NULL;

  /* Ensure that the first three fds are open, to ensure that when we
   * create other file descriptors (for example for epoll, inotify or
   * a socket), they never get assigned as fd 0, 1 or 2. If they were,
   * which could happen if our caller had (incorrectly) closed those
   * standard fds, then we'd start dbus-daemon with those fds closed,
   * which is unexpected and could cause it to misbehave. */
  if (!_dbus_ensure_standard_fds (0, &error_str))
    {
      fprintf (stderr,
               "dbus-launch: fatal error setting up standard fds: %s: %s\n",
               error_str, _dbus_strerror (errno));
      return 1;
    }

  prev_arg = NULL;
  i = 1;
  while (i < argc)
    {
      const char *arg = argv[i];
 
      if (strcmp (arg, "--help") == 0 ||
          strcmp (arg, "-h") == 0 ||
          strcmp (arg, "-?") == 0)
        usage (0);
      else if (strcmp (arg, "--auto-syntax") == 0)
        auto_shell_syntax = TRUE;
      else if (strcmp (arg, "-c") == 0 ||
               strcmp (arg, "--csh-syntax") == 0)
        c_shell_syntax = TRUE;
      else if (strcmp (arg, "-s") == 0 ||
               strcmp (arg, "--sh-syntax") == 0)
        bourne_shell_syntax = TRUE;
      else if (strcmp (arg, "--binary-syntax") == 0)
        binary_syntax = TRUE;
      else if (strcmp (arg, "--version") == 0)
        version ();
      else if (strcmp (arg, "--exit-with-session") == 0)
        exit_with_session = TRUE;
      else if (strcmp (arg, "--exit-with-x11") == 0)
        exit_with_x11 = TRUE;
      else if (strcmp (arg, "--close-stderr") == 0)
        close_stderr = TRUE;
      else if (strstr (arg, "--autolaunch=") == arg)
        {
          const char *s;

          if (autolaunch)
            {
              fprintf (stderr, "--autolaunch given twice\n");
              exit (1);
            }
          
          autolaunch = TRUE;

          s = strchr (arg, '=');
          ++s;

          save_machine_uuid (s);
        }
      else if (prev_arg &&
               strcmp (prev_arg, "--autolaunch") == 0)
        {
          if (autolaunch)
            {
              fprintf (stderr, "--autolaunch given twice\n");
              exit (1);
            }
          
          autolaunch = TRUE;

          save_machine_uuid (arg);
	  requires_arg = FALSE;
        }
      else if (strcmp (arg, "--autolaunch") == 0)
	requires_arg = TRUE;
      else if (strstr (arg, "--config-file=") == arg)
        {
          const char *file;

          if (config_file != NULL)
            {
              fprintf (stderr, "--config-file given twice\n");
              exit (1);
            }
          
          file = strchr (arg, '=');
          ++file;

          config_file = xstrdup (file);
        }
      else if (prev_arg &&
               strcmp (prev_arg, "--config-file") == 0)
        {
          if (config_file != NULL)
            {
              fprintf (stderr, "--config-file given twice\n");
              exit (1);
            }

          config_file = xstrdup (arg);
	  requires_arg = FALSE;
        }
      else if (strcmp (arg, "--config-file") == 0)
	requires_arg = TRUE;
      else if (arg[0] == '-')
        {
          if (strcmp (arg, "--") != 0)
            {
              fprintf (stderr, "Option `%s' is unknown.\n", arg);
              exit (1);
            }
          else
            {
              runprog = argv[i+1];
              remaining_args = i+2;
              break;
            }
        }
      else
	{
	  runprog = arg;
	  remaining_args = i+1;
	  break;
	}
      
      prev_arg = arg;
      
      ++i;
    }
  if (requires_arg)
    {
      fprintf (stderr, "Option `%s' requires an argument.\n", prev_arg);
      exit (1);
    }

  if (auto_shell_syntax)
    {
      if ((shname = getenv ("SHELL")) != NULL)
       {
         if (!strncmp (shname + strlen (shname) - 3, "csh", 3))
           c_shell_syntax = TRUE;
         else
           bourne_shell_syntax = TRUE;
       }
      else
       bourne_shell_syntax = TRUE;
    }  

  if (exit_with_session)
    verbose ("--exit-with-session enabled\n");

  if (exit_with_x11)
    verbose ("--exit-with-x11 enabled\n");

  if (autolaunch)
    {      
#ifndef DBUS_BUILD_X11
      fprintf (stderr, "Autolaunch requested, but X11 support not compiled in.\n"
	       "Cannot continue.\n");
      exit (1);
#else /* DBUS_BUILD_X11 */
      fprintf (stderr, "X11 autolaunch support disabled at compile time.\n");
      exit (1);
#endif /* DBUS_BUILD_X11 */
    }
  else if (exit_with_x11)
    {
#ifndef DBUS_BUILD_X11
      fprintf (stderr, "Session lifetime based on X11 requested, but X11 support not compiled in.\n");
      exit (1);
#else /* DBUS_BUILD_X11 */
      if (!read_machine_uuid_if_needed())
        {
          fprintf (stderr, "Session lifetime based on X11 requested, but machine UUID unavailable.\n");
          exit (1);
        }

      if (!x11_init ())
        {
          fprintf (stderr, "Session lifetime based on X11 requested, but X11 initialization failed.\n");
          exit (1);
        }
#endif /* DBUS_BUILD_X11 */
    }
#ifdef DBUS_BUILD_X11
  else if (read_machine_uuid_if_needed())
    {
      x11_init();
    }
#endif /* DBUS_BUILD_X11 */


  if (pipe (bus_pid_to_launcher_pipe) < 0 ||
      pipe (bus_address_to_launcher_pipe) < 0 ||
      pipe (bus_pid_to_babysitter_pipe) < 0)
    {
      fprintf (stderr,
               "Failed to create pipe: %s\n",
               strerror (errno));
      exit (1);
    }

  ret = fork ();
  if (ret < 0)
    {
      fprintf (stderr, "Failed to fork: %s\n",
               strerror (errno));
      exit (1);
    }

  if (ret == 0)
    {
      /* Child */
#define MAX_FD_LEN 64
      char write_pid_fd_as_string[MAX_FD_LEN];
      char write_address_fd_as_string[MAX_FD_LEN];

#ifdef DBUS_BUILD_X11
      xdisplay = NULL;
#endif

      if (close_stderr)
	do_close_stderr ();

      verbose ("=== Babysitter's intermediate parent created\n");

      /* Fork once more to create babysitter */
      
      ret = fork ();
      if (ret < 0)
        {
          fprintf (stderr, "Failed to fork: %s\n",
                   strerror (errno));
          exit (1);
        }
      
      if (ret > 0)
        {
          /* In babysitter */
          verbose ("=== Babysitter's intermediate parent continues\n");
          
          close (bus_pid_to_launcher_pipe[READ_END]);
	  close (bus_pid_to_launcher_pipe[WRITE_END]);
          close (bus_address_to_launcher_pipe[READ_END]);
          close (bus_address_to_launcher_pipe[WRITE_END]);
          close (bus_pid_to_babysitter_pipe[WRITE_END]);

          /* babysit() will fork *again*
           * and will also reap the pre-forked bus
           * daemon
           */
          babysit (exit_with_session || exit_with_x11, ret,
                   bus_pid_to_babysitter_pipe[READ_END]);
          exit (0);
        }

      verbose ("=== Bus exec process created\n");
      
      /* Now we are the bus process (well, almost;
       * dbus-daemon itself forks again)
       */
      close (bus_pid_to_launcher_pipe[READ_END]);
      close (bus_address_to_launcher_pipe[READ_END]);
      close (bus_pid_to_babysitter_pipe[READ_END]);
      close (bus_pid_to_babysitter_pipe[WRITE_END]);

      /* If we have a user bus and want to use it, do so instead of
       * exec'ing a new dbus-daemon. */
      if (autolaunch && user_bus_supported)
        {
          do_write (bus_pid_to_launcher_pipe[WRITE_END], "0\n", 2);
          close (bus_pid_to_launcher_pipe[WRITE_END]);

          do_write (bus_address_to_launcher_pipe[WRITE_END],
                    _dbus_string_get_const_data (&user_bus),
                    _dbus_string_get_length (&user_bus));
          do_write (bus_address_to_launcher_pipe[WRITE_END], "\n", 1);
          close (bus_address_to_launcher_pipe[WRITE_END]);

          exit (0);
        }

      sprintf (write_pid_fd_as_string,
               "%d", bus_pid_to_launcher_pipe[WRITE_END]);

      sprintf (write_address_fd_as_string,
               "%d", bus_address_to_launcher_pipe[WRITE_END]);

      verbose ("Calling exec()\n");
 
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
      {
        /* exec from testdir */
        const char *test_daemon = getenv ("DBUS_TEST_DAEMON");

        if (test_daemon != NULL)
          {
            if (config_file == NULL && getenv ("DBUS_TEST_DATA") != NULL)
              {
                config_file = concat2 (getenv ("DBUS_TEST_DATA"),
                    "/valid-config-files/session.conf");

                if (config_file == NULL)
                  {
                    fprintf (stderr, "Out of memory\n");
                    exit (1);
                  }
              }

            execl (test_daemon,
                   test_daemon,
                   "--fork",
                   "--print-pid", write_pid_fd_as_string,
                   "--print-address", write_address_fd_as_string,
                   config_file ? "--config-file" : "--session",
                   config_file, /* has to be last in this varargs list */
                   NULL);

            fprintf (stderr,
                     "Failed to execute test message bus daemon %s: %s.\n",
                     test_daemon, strerror (errno));
            exit (1);
          }
      }
 #endif /* DBUS_ENABLE_EMBEDDED_TESTS */

      execl (DBUS_DAEMONDIR"/dbus-daemon",
             DBUS_DAEMONDIR"/dbus-daemon",
             "--fork",
             "--print-pid", write_pid_fd_as_string,
             "--print-address", write_address_fd_as_string,
             config_file ? "--config-file" : "--session",
             config_file, /* has to be last in this varargs list */
             NULL);

      fprintf (stderr,
               "Failed to execute message bus daemon %s: %s.  Will try again without full path.\n",
               DBUS_DAEMONDIR"/dbus-daemon", strerror (errno));
      
      /*
       * If it failed, try running without full PATH.  Note this is needed
       * because the build process builds the run-with-tmp-session-bus.conf
       * file and the dbus-daemon will not be in the install location during
       * build time.
       */
      execlp ("dbus-daemon",
              "dbus-daemon",
              "--fork",
              "--print-pid", write_pid_fd_as_string,
              "--print-address", write_address_fd_as_string,
              config_file ? "--config-file" : "--session",
              config_file, /* has to be last in this varargs list */
              NULL);

      fprintf (stderr,
               "Failed to execute message bus daemon: %s\n",
               strerror (errno));
      exit (1);
    }
  else
    {
      /* Parent */
#define MAX_PID_LEN 64
      pid_t bus_pid;  
      char bus_address[MAX_ADDR_LEN];
      char buf[MAX_PID_LEN];
      char *end;
      long wid = 0;
      long val;

      verbose ("=== Parent dbus-launch continues\n");
      
      close (bus_pid_to_launcher_pipe[WRITE_END]);
      close (bus_address_to_launcher_pipe[WRITE_END]);
      close (bus_pid_to_babysitter_pipe[READ_END]);

      verbose ("Waiting for babysitter's intermediate parent\n");
      
      /* Immediately reap parent of babysitter
       * (which was created just for us to reap)
       */
      if (do_waitpid (ret) < 0)
        {
          fprintf (stderr, "Failed to waitpid() for babysitter intermediate process: %s\n",
                   strerror (errno));
          exit (1);
        }

      verbose ("Reading address from bus\n");
      
      /* Read the pipe data, print, and exit */
      switch (read_line (bus_address_to_launcher_pipe[READ_END],
                         bus_address, MAX_ADDR_LEN))
        {
        case READ_STATUS_OK:
          break;
        case READ_STATUS_EOF:
          fprintf (stderr, "EOF in dbus-launch reading address from bus daemon\n");
          exit (1);
          break;
        case READ_STATUS_ERROR:
          fprintf (stderr, "Error in dbus-launch reading address from bus daemon: %s\n",
                   strerror (errno));
          exit (1);
          break;
        }
        
      close (bus_address_to_launcher_pipe[READ_END]);

      verbose ("Reading PID from daemon\n");
      /* Now read data */
      switch (read_line (bus_pid_to_launcher_pipe[READ_END], buf, MAX_PID_LEN))
	{
	case READ_STATUS_OK:
	  break;
	case READ_STATUS_EOF:
	  fprintf (stderr, "EOF reading PID from bus daemon\n");
	  exit (1);
	  break;
	case READ_STATUS_ERROR:
	  fprintf (stderr, "Error reading PID from bus daemon: %s\n",
		   strerror (errno));
	  exit (1);
	  break;
	}

      end = NULL;
      val = strtol (buf, &end, 0);
      if (buf == end || end == NULL)
	{
	  fprintf (stderr, "Failed to parse bus PID \"%s\": %s\n",
		   buf, strerror (errno));
	  exit (1);
	}

      bus_pid = val;

      /* Have to initialize bus_pid_to_kill ASAP, so that the
         X error callback can kill it if an error happens. */
      bus_pid_to_kill = bus_pid;

      close (bus_pid_to_launcher_pipe[READ_END]);

#ifdef DBUS_ENABLE_X11_AUTOLAUNCH
      if (xdisplay != NULL)
        {
          int ret2;

          verbose("Saving x11 address\n");
          ret2 = x11_save_address (bus_address, bus_pid, &wid);
          /* Only get an existing dbus session when autolaunching */
          if (autolaunch)
            {
              if (ret2 == 0)
                {
                  char *address = NULL;
                  /* another window got added. Return its address */
                  if (x11_get_address (&address, &bus_pid, &wid)
                       && address != NULL)
                    {
                      verbose ("dbus-daemon is already running. Returning existing parameters.\n");
                      /* Kill the old bus */
                      kill_bus();
                      pass_info (runprog, address, bus_pid, wid,
                         c_shell_syntax, bourne_shell_syntax, binary_syntax,
                         argc, argv, remaining_args);
                    }
                  }
              if (ret2 < 0)
                {
                  fprintf (stderr, "Error saving bus information.\n");
                  bus_pid_to_kill = bus_pid;
                  kill_bus_and_exit (1);
                }
            }
        }
#endif

      /* Forward the pid to the babysitter */
      write_pid (bus_pid_to_babysitter_pipe[WRITE_END], bus_pid);
      close (bus_pid_to_babysitter_pipe[WRITE_END]);

       pass_info (runprog, bus_address, bus_pid, wid, c_shell_syntax,
              bourne_shell_syntax, binary_syntax, argc, argv, remaining_args);
    }

  return 0;
}
示例#3
0
文件: dbus-spawn.c 项目: mirsal/dbus
/**
 * 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.
 *
 * @param sitter_p return location for babysitter or #NULL
 * @param argv the executable and arguments
 * @param env the environment (not used on unix yet)
 * @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_with_babysitter (DBusBabysitter          **sitter_p,
                                   char                    **argv,
                                   char                    **env,
                                   DBusSpawnChildSetupFunc   child_setup,
                                   void                     *user_data,
                                   DBusError                *error)
{
    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);

    if (sitter_p != NULL)
        *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;

    if (!_dbus_full_duplex_pipe (&babysitter_pipe[0], &babysitter_pipe[1], TRUE, error))
        goto cleanup_and_fail;

    /* 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))
    {
        /* 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;
    }

    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))
    {
        /* 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;
    }

    _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,
                     env,
                     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_free_string_array (env);

        _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;
}
示例#4
0
int
main (int argc, char **argv)
{
  const char *prev_arg;
  const char *shname;
  const char *runprog = NULL;
  int remaining_args = 0;
  int exit_with_session;
  int c_shell_syntax = FALSE;
  int bourne_shell_syntax = FALSE;
  int auto_shell_syntax = FALSE;
  int i;  
  int ret;
  int bus_pid_to_launcher_pipe[2];
  int bus_pid_to_babysitter_pipe[2];
  int bus_address_to_launcher_pipe[2];
  char *config_file;
  
  exit_with_session = FALSE;
  config_file = NULL;
  
  prev_arg = NULL;
  i = 1;
  while (i < argc)
    {
      const char *arg = argv[i];
      
      if (strcmp (arg, "--help") == 0 ||
          strcmp (arg, "-h") == 0 ||
          strcmp (arg, "-?") == 0)
        usage (0);
      else if (strcmp (arg, "--auto-syntax") == 0)
        auto_shell_syntax = TRUE;
      else if (strcmp (arg, "-c") == 0 ||
	       strcmp (arg, "--csh-syntax") == 0)
        c_shell_syntax = TRUE;
      else if (strcmp (arg, "-s") == 0 ||
	       strcmp (arg, "--sh-syntax") == 0)
        bourne_shell_syntax = TRUE;
      else if (strcmp (arg, "--version") == 0)
        version ();
      else if (strcmp (arg, "--exit-with-session") == 0)
        exit_with_session = TRUE;
      else if (strstr (arg, "--config-file=") == arg)
        {
          const char *file;

          if (config_file != NULL)
            {
              fprintf (stderr, "--config-file given twice\n");
              exit (1);
            }
          
          file = strchr (arg, '=');
          ++file;

          config_file = xstrdup (file);
        }
      else if (prev_arg &&
               strcmp (prev_arg, "--config-file") == 0)
        {
          if (config_file != NULL)
            {
              fprintf (stderr, "--config-file given twice\n");
              exit (1);
            }

          config_file = xstrdup (arg);
        }
      else if (strcmp (arg, "--config-file") == 0)
        ; /* wait for next arg */
      else
	{
	  runprog = arg;
	  remaining_args = i+1;
	  break;
	}
      
      prev_arg = arg;
      
      ++i;
    }

  if (exit_with_session)
    verbose ("--exit-with-session enabled\n");

  if (auto_shell_syntax)
    {
      if ((shname = getenv ("SHELL")) != NULL)
       {
         if (!strncmp (shname + strlen (shname) - 3, "csh", 3))
           c_shell_syntax = TRUE;
         else
           bourne_shell_syntax = TRUE;
       }
      else
       bourne_shell_syntax = TRUE;
    }  

  if (pipe (bus_pid_to_launcher_pipe) < 0 ||
      pipe (bus_address_to_launcher_pipe) < 0)
    {
      fprintf (stderr,
               "Failed to create pipe: %s\n",
               strerror (errno));
      exit (1);
    }

  bus_pid_to_babysitter_pipe[READ_END] = -1;
  bus_pid_to_babysitter_pipe[WRITE_END] = -1;
  
  ret = fork ();
  if (ret < 0)
    {
      fprintf (stderr, "Failed to fork: %s\n",
               strerror (errno));
      exit (1);
    }

  if (ret == 0)
    {
      /* Child */
#define MAX_FD_LEN 64
      char write_pid_fd_as_string[MAX_FD_LEN];
      char write_address_fd_as_string[MAX_FD_LEN];

      verbose ("=== Babysitter's intermediate parent created\n");
      
      /* Fork once more to create babysitter */
      
      if (pipe (bus_pid_to_babysitter_pipe) < 0)
        {
          fprintf (stderr,
                   "Failed to create pipe: %s\n",
                   strerror (errno));
          exit (1);              
        }
      
      ret = fork ();
      if (ret < 0)
        {
          fprintf (stderr, "Failed to fork: %s\n",
                   strerror (errno));
          exit (1);
        }
      
      if (ret > 0)
        {
          /* In babysitter */
          verbose ("=== Babysitter's intermediate parent continues\n");
          
          close (bus_pid_to_launcher_pipe[READ_END]);
          close (bus_address_to_launcher_pipe[READ_END]);
          close (bus_address_to_launcher_pipe[WRITE_END]);
          close (bus_pid_to_babysitter_pipe[WRITE_END]);
          
          /* babysit() will fork *again*
           * and will also reap the pre-forked bus
           * daemon
           */
          babysit (exit_with_session, ret,
                   bus_pid_to_babysitter_pipe[READ_END],
                   bus_pid_to_launcher_pipe[WRITE_END]);
          exit (0);
        }

      verbose ("=== Bus exec process created\n");
      
      /* Now we are the bus process (well, almost;
       * dbus-daemon itself forks again)
       */
      close (bus_pid_to_launcher_pipe[READ_END]);
      close (bus_address_to_launcher_pipe[READ_END]);
      close (bus_pid_to_babysitter_pipe[READ_END]);
      close (bus_pid_to_launcher_pipe[WRITE_END]);

      sprintf (write_pid_fd_as_string,
               "%d", bus_pid_to_babysitter_pipe[WRITE_END]);

      sprintf (write_address_fd_as_string,
               "%d", bus_address_to_launcher_pipe[WRITE_END]);

      verbose ("Calling exec()\n");
      
      execlp ("dbus-daemon",
              "dbus-daemon",
              "--fork",
              "--print-pid", write_pid_fd_as_string,
              "--print-address", write_address_fd_as_string,
              config_file ? "--config-file" : "--session",
              config_file, /* has to be last in this varargs list */
              NULL);

      fprintf (stderr,
               "Failed to execute message bus daemon: %s\n",
               strerror (errno));
      exit (1);
    }
  else
    {
      /* Parent */
#define MAX_ADDR_LEN 512
      pid_t bus_pid;  
      char bus_address[MAX_ADDR_LEN];

      verbose ("=== Parent dbus-launch continues\n");
      
      close (bus_pid_to_launcher_pipe[WRITE_END]);
      close (bus_address_to_launcher_pipe[WRITE_END]);

      verbose ("Waiting for babysitter's intermediate parent\n");
      
      /* Immediately reap parent of babysitter
       * (which was created just for us to reap)
       */
      if (do_waitpid (ret) < 0)
        {
          fprintf (stderr, "Failed to waitpid() for babysitter intermediate process: %s\n",
                   strerror (errno));
          exit (1);
        }

      verbose ("Reading address from bus\n");
      
      /* Read the pipe data, print, and exit */
      switch (read_line (bus_address_to_launcher_pipe[READ_END],
                         bus_address, MAX_ADDR_LEN))
        {
        case READ_STATUS_OK:
          break;
        case READ_STATUS_EOF:
          fprintf (stderr, "EOF in dbus-launch reading address from bus daemon\n");
          exit (1);
          break;
        case READ_STATUS_ERROR:
          fprintf (stderr, "Error in dbus-launch reading address from bus daemon: %s\n",
                   strerror (errno));
          exit (1);
          break;
        }
        
      close (bus_address_to_launcher_pipe[READ_END]);

      verbose ("Reading PID from babysitter\n");
      
      switch (read_pid (bus_pid_to_launcher_pipe[READ_END], &bus_pid))
        {
        case READ_STATUS_OK:
          break;
        case READ_STATUS_EOF:
          fprintf (stderr, "EOF in dbus-launch reading address from bus daemon\n");
          exit (1);
          break;
        case READ_STATUS_ERROR:
          fprintf (stderr, "Error in dbus-launch reading address from bus daemon: %s\n",
                   strerror (errno));
          exit (1);
          break;
        }

      close (bus_pid_to_launcher_pipe[READ_END]);
      
      if (runprog)
	{
	  char *envvar;
	  char **args;

	  envvar = malloc (strlen ("DBUS_SESSION_BUS_ADDRESS=") + strlen (bus_address) + 1);
	  args = malloc (sizeof (char *) * ((argc-remaining_args)+2));

	  if (envvar == NULL || args == NULL)
	    goto oom;

	  args[0] = xstrdup (runprog);
	  if (!args[0])
	    goto oom;
	  for (i = 1; i <= (argc-remaining_args); i++)
	    {
	      size_t len = strlen (argv[remaining_args+i-1])+1;
	      args[i] = malloc (len);
	      if (!args[i])
		goto oom;
	      strncpy (args[i], argv[remaining_args+i-1], len);
	    }
	  args[i] = NULL;

	  strcpy (envvar, "DBUS_SESSION_BUS_ADDRESS=");
	  strcat (envvar, bus_address);
	  putenv (envvar);

	  execvp (runprog, args);
	  fprintf (stderr, "Couldn't exec %s: %s\n", runprog, strerror (errno));
	  exit (1);
	}
      else
	{
	  if (c_shell_syntax)
	    printf ("setenv DBUS_SESSION_BUS_ADDRESS '%s'\n", bus_address);	
	  else
	    {
	      printf ("DBUS_SESSION_BUS_ADDRESS='%s'\n", bus_address);
	      if (bourne_shell_syntax)
		printf ("export DBUS_SESSION_BUS_ADDRESS\n");
	    }
	  if (c_shell_syntax)
	    printf ("set DBUS_SESSION_BUS_PID=%ld\n", (long) bus_pid);
	  else
	    printf ("DBUS_SESSION_BUS_PID=%ld\n", (long) bus_pid);
	}
	  
      verbose ("dbus-launch exiting\n");

      fflush (stdout);
      fflush (stderr);
      close (1);
      close (2);
      
      exit (0);
    } 
  
  return 0;
 oom:
  fprintf (stderr, "Out of memory!");
  exit (1);
}
示例#5
0
int
main (int argc, char **argv)
{
  const char *prev_arg;
  const char *shname;
  const char *runprog = NULL;
  int remaining_args = 0;
  int exit_with_session;
  int binary_syntax = FALSE;
  int c_shell_syntax = FALSE;
  int bourne_shell_syntax = FALSE;
  int auto_shell_syntax = FALSE;
  int autolaunch = FALSE;
  int requires_arg = FALSE;
  int close_stderr = FALSE;
  int i;  
  int ret;
  int bus_pid_to_launcher_pipe[2];
  int bus_pid_to_babysitter_pipe[2];
  int bus_address_to_launcher_pipe[2];
  char *config_file;
  
  exit_with_session = FALSE;
  config_file = NULL;
  
  prev_arg = NULL;
  i = 1;
  while (i < argc)
    {
      const char *arg = argv[i];
 
      if (strcmp (arg, "--help") == 0 ||
          strcmp (arg, "-h") == 0 ||
          strcmp (arg, "-?") == 0)
        usage (0);
      else if (strcmp (arg, "--auto-syntax") == 0)
        auto_shell_syntax = TRUE;
      else if (strcmp (arg, "-c") == 0 ||
               strcmp (arg, "--csh-syntax") == 0)
        c_shell_syntax = TRUE;
      else if (strcmp (arg, "-s") == 0 ||
               strcmp (arg, "--sh-syntax") == 0)
        bourne_shell_syntax = TRUE;
      else if (strcmp (arg, "--binary-syntax") == 0)
        binary_syntax = TRUE;
      else if (strcmp (arg, "--version") == 0)
        version ();
      else if (strcmp (arg, "--exit-with-session") == 0)
        exit_with_session = TRUE;
      else if (strcmp (arg, "--close-stderr") == 0)
        close_stderr = TRUE;
      else if (strstr (arg, "--autolaunch=") == arg)
        {
          const char *s;

          if (autolaunch)
            {
              fprintf (stderr, "--autolaunch given twice\n");
              exit (1);
            }
          
          autolaunch = TRUE;

          s = strchr (arg, '=');
          ++s;

          save_machine_uuid (s);
        }
      else if (prev_arg &&
               strcmp (prev_arg, "--autolaunch") == 0)
        {
          if (autolaunch)
            {
              fprintf (stderr, "--autolaunch given twice\n");
              exit (1);
            }
          
          autolaunch = TRUE;

          save_machine_uuid (arg);
	  requires_arg = FALSE;
        }
      else if (strcmp (arg, "--autolaunch") == 0)
	requires_arg = TRUE;
      else if (strstr (arg, "--config-file=") == arg)
        {
          const char *file;

          if (config_file != NULL)
            {
              fprintf (stderr, "--config-file given twice\n");
              exit (1);
            }
          
          file = strchr (arg, '=');
          ++file;

          config_file = xstrdup (file);
        }
      else if (prev_arg &&
               strcmp (prev_arg, "--config-file") == 0)
        {
          if (config_file != NULL)
            {
              fprintf (stderr, "--config-file given twice\n");
              exit (1);
            }

          config_file = xstrdup (arg);
	  requires_arg = FALSE;
        }
      else if (strcmp (arg, "--config-file") == 0)
	requires_arg = TRUE;
      else if (arg[0] == '-')
        {
          if (strcmp (arg, "--") != 0)
            {
              fprintf (stderr, "Option `%s' is unknown.\n", arg);
              exit (1);
            }
          else
            {
              runprog = argv[i+1];
              remaining_args = i+2;
              break;
            }
        }
      else
	{
	  runprog = arg;
	  remaining_args = i+1;
	  break;
	}
      
      prev_arg = arg;
      
      ++i;
    }
  if (requires_arg)
    {
      fprintf (stderr, "Option `%s' requires an argument.\n", prev_arg);
      exit (1);
    }

  if (auto_shell_syntax)
    {
      if ((shname = getenv ("SHELL")) != NULL)
       {
         if (!strncmp (shname + strlen (shname) - 3, "csh", 3))
           c_shell_syntax = TRUE;
         else
           bourne_shell_syntax = TRUE;
       }
      else
       bourne_shell_syntax = TRUE;
    }  

  if (exit_with_session)
    verbose ("--exit-with-session enabled\n");

  if (autolaunch)
    {      
#ifndef DBUS_BUILD_X11
      fprintf (stderr, "Autolaunch requested, but X11 support not compiled in.\n"
	       "Cannot continue.\n");
      exit (1);
#else
      char *address;
      pid_t pid;
      long wid;
      
      if (get_machine_uuid () == NULL)
        {
          fprintf (stderr, "Machine UUID not provided as arg to --autolaunch\n");
          exit (1);
        }

      /* FIXME right now autolaunch always does print_variables(), but it should really
       * exec the child program instead if a child program was specified. For now
       * we just exit if this conflict arises.
       */
      if (runprog)
        {
          fprintf (stderr, "Currently --autolaunch does not support running a program\n");
          exit (1);
        }
      
      verbose ("Autolaunch enabled (using X11).\n");
      if (!exit_with_session)
	{
	  verbose ("--exit-with-session automatically enabled\n");
	  exit_with_session = TRUE;
	}

      if (!x11_init ())
	{
	  fprintf (stderr, "Autolaunch error: X11 initialization failed.\n");
	  exit (1);
	}

      if (!x11_get_address (&address, &pid, &wid))
	{
	  fprintf (stderr, "Autolaunch error: X11 communication error.\n");
	  exit (1);
	}

      if (address != NULL)
	{          
	  verbose ("dbus-daemon is already running. Returning existing parameters.\n");
	  print_variables (address, pid, wid, c_shell_syntax,
			   bourne_shell_syntax, binary_syntax);
	  exit (0);
	}
#endif
    }

  if (pipe (bus_pid_to_launcher_pipe) < 0 ||
      pipe (bus_address_to_launcher_pipe) < 0 ||
      pipe (bus_pid_to_babysitter_pipe) < 0)
    {
      fprintf (stderr,
               "Failed to create pipe: %s\n",
               strerror (errno));
      exit (1);
    }

  ret = fork ();
  if (ret < 0)
    {
      fprintf (stderr, "Failed to fork: %s\n",
               strerror (errno));
      exit (1);
    }

  if (ret == 0)
    {
      /* Child */
#define MAX_FD_LEN 64
      char write_pid_fd_as_string[MAX_FD_LEN];
      char write_address_fd_as_string[MAX_FD_LEN];

      if (close_stderr)
	do_close_stderr ();

      verbose ("=== Babysitter's intermediate parent created\n");

      /* Fork once more to create babysitter */
      
      ret = fork ();
      if (ret < 0)
        {
          fprintf (stderr, "Failed to fork: %s\n",
                   strerror (errno));
          exit (1);
        }
      
      if (ret > 0)
        {
          /* In babysitter */
          verbose ("=== Babysitter's intermediate parent continues\n");
          
          close (bus_pid_to_launcher_pipe[READ_END]);
	  close (bus_pid_to_launcher_pipe[WRITE_END]);
          close (bus_address_to_launcher_pipe[READ_END]);
          close (bus_address_to_launcher_pipe[WRITE_END]);
          close (bus_pid_to_babysitter_pipe[WRITE_END]);
          
          /* babysit() will fork *again*
           * and will also reap the pre-forked bus
           * daemon
           */
          babysit (exit_with_session, ret,
                   bus_pid_to_babysitter_pipe[READ_END]);
          exit (0);
        }

      verbose ("=== Bus exec process created\n");
      
      /* Now we are the bus process (well, almost;
       * dbus-daemon itself forks again)
       */
      close (bus_pid_to_launcher_pipe[READ_END]);
      close (bus_address_to_launcher_pipe[READ_END]);
      close (bus_pid_to_babysitter_pipe[READ_END]);
      close (bus_pid_to_babysitter_pipe[WRITE_END]);

      sprintf (write_pid_fd_as_string,
               "%d", bus_pid_to_launcher_pipe[WRITE_END]);

      sprintf (write_address_fd_as_string,
               "%d", bus_address_to_launcher_pipe[WRITE_END]);

      verbose ("Calling exec()\n");
 
#ifdef DBUS_BUILD_TESTS 
      /* exec from testdir */
      if (getenv("DBUS_USE_TEST_BINARY") != NULL)
        {
          execl (TEST_BUS_BINARY,
                 TEST_BUS_BINARY,
                 "--fork",
                 "--print-pid", write_pid_fd_as_string,
                 "--print-address", write_address_fd_as_string,
                 config_file ? "--config-file" : "--session",
                 config_file, /* has to be last in this varargs list */
                 NULL); 

          fprintf (stderr,
                   "Failed to execute test message bus daemon %s: %s. Will try again with the system path.\n",
                   TEST_BUS_BINARY, strerror (errno));
        }
 #endif /* DBUS_BUILD_TESTS */

      execl (DBUS_DAEMONDIR"/dbus-daemon",
             DBUS_DAEMONDIR"/dbus-daemon",
             "--fork",
             "--print-pid", write_pid_fd_as_string,
             "--print-address", write_address_fd_as_string,
             config_file ? "--config-file" : "--session",
             config_file, /* has to be last in this varargs list */
             NULL);

      fprintf (stderr,
               "Failed to execute message bus daemon %s: %s.  Will try again without full path.\n",
               DBUS_DAEMONDIR"/dbus-daemon", strerror (errno));
      
      /*
       * If it failed, try running without full PATH.  Note this is needed
       * because the build process builds the run-with-tmp-session-bus.conf
       * file and the dbus-daemon will not be in the install location during
       * build time.
       */
      execlp ("dbus-daemon",
              "dbus-daemon",
              "--fork",
              "--print-pid", write_pid_fd_as_string,
              "--print-address", write_address_fd_as_string,
              config_file ? "--config-file" : "--session",
              config_file, /* has to be last in this varargs list */
              NULL);

      fprintf (stderr,
               "Failed to execute message bus daemon: %s\n",
               strerror (errno));
      exit (1);
    }
  else
    {
      /* Parent */
#define MAX_PID_LEN 64
      pid_t bus_pid;  
      char bus_address[MAX_ADDR_LEN];
      char buf[MAX_PID_LEN];
      char *end;
      long wid = 0;
      long val;
      int ret2;

      verbose ("=== Parent dbus-launch continues\n");
      
      close (bus_pid_to_launcher_pipe[WRITE_END]);
      close (bus_address_to_launcher_pipe[WRITE_END]);
      close (bus_pid_to_babysitter_pipe[READ_END]);

      verbose ("Waiting for babysitter's intermediate parent\n");
      
      /* Immediately reap parent of babysitter
       * (which was created just for us to reap)
       */
      if (do_waitpid (ret) < 0)
        {
          fprintf (stderr, "Failed to waitpid() for babysitter intermediate process: %s\n",
                   strerror (errno));
          exit (1);
        }

      verbose ("Reading address from bus\n");
      
      /* Read the pipe data, print, and exit */
      switch (read_line (bus_address_to_launcher_pipe[READ_END],
                         bus_address, MAX_ADDR_LEN))
        {
        case READ_STATUS_OK:
          break;
        case READ_STATUS_EOF:
          fprintf (stderr, "EOF in dbus-launch reading address from bus daemon\n");
          exit (1);
          break;
        case READ_STATUS_ERROR:
          fprintf (stderr, "Error in dbus-launch reading address from bus daemon: %s\n",
                   strerror (errno));
          exit (1);
          break;
        }
        
      close (bus_address_to_launcher_pipe[READ_END]);

      verbose ("Reading PID from daemon\n");
      /* Now read data */
      switch (read_line (bus_pid_to_launcher_pipe[READ_END], buf, MAX_PID_LEN))
	{
	case READ_STATUS_OK:
	  break;
	case READ_STATUS_EOF:
	  fprintf (stderr, "EOF reading PID from bus daemon\n");
	  exit (1);
	  break;
	case READ_STATUS_ERROR:
	  fprintf (stderr, "Error reading PID from bus daemon: %s\n",
		   strerror (errno));
	  exit (1);
	  break;
	}

      end = NULL;
      val = strtol (buf, &end, 0);
      if (buf == end || end == NULL)
	{
	  fprintf (stderr, "Failed to parse bus PID \"%s\": %s\n",
		   buf, strerror (errno));
	  exit (1);
	}

      bus_pid = val;

      close (bus_pid_to_launcher_pipe[READ_END]);

#ifdef DBUS_BUILD_X11
      /* FIXME the runprog == NULL is broken - we need to launch the runprog with the existing bus,
       * instead of just doing print_variables() if there's an existing bus.
       */
      if (xdisplay != NULL && runprog == NULL)
        {
          ret2 = x11_save_address (bus_address, bus_pid, &wid);
          if (ret2 == 0)
            {
              /* another window got added. Return its address */
              char *address;
              pid_t pid;
              long wid;
              
              if (x11_get_address (&address, &pid, &wid) && address != NULL)
                {
                  verbose ("dbus-daemon is already running. Returning existing parameters.\n");
                  print_variables (address, pid, wid, c_shell_syntax,
                                   bourne_shell_syntax, binary_syntax);
                  free (address);
                  
                  bus_pid_to_kill = bus_pid;
                  kill_bus_and_exit (0);
                }
              
              /* if failed, fall through */
            }
          if (ret2 <= 0)
            {
              fprintf (stderr, "Error saving bus information.\n");
              bus_pid_to_kill = bus_pid;
              kill_bus_and_exit (1);
            }
        }
#endif

      /* Forward the pid to the babysitter */
      write_pid (bus_pid_to_babysitter_pipe[WRITE_END], bus_pid);
      close (bus_pid_to_babysitter_pipe[WRITE_END]);

      if (runprog)
	{
	  char *envvar;
	  char **args;

	  envvar = malloc (strlen ("DBUS_SESSION_BUS_ADDRESS=") + strlen (bus_address) + 1);
	  args = malloc (sizeof (char *) * ((argc-remaining_args)+2));

	  if (envvar == NULL || args == NULL)
	    goto oom;

	  args[0] = xstrdup (runprog);
	  if (!args[0])
	    goto oom;
	  for (i = 1; i <= (argc-remaining_args); i++)
	    {
	      size_t len = strlen (argv[remaining_args+i-1])+1;
	      args[i] = malloc (len);
	      if (!args[i])
		goto oom;
	      strncpy (args[i], argv[remaining_args+i-1], len);
	    }
	  args[i] = NULL;

	  strcpy (envvar, "DBUS_SESSION_BUS_ADDRESS=");
	  strcat (envvar, bus_address);
	  putenv (envvar);

	  execvp (runprog, args);
	  fprintf (stderr, "Couldn't exec %s: %s\n", runprog, strerror (errno));
	  exit (1);
	}
      else
	{
	  print_variables (bus_address, bus_pid, wid, c_shell_syntax,
			   bourne_shell_syntax, binary_syntax);
	}
	  
      verbose ("dbus-launch exiting\n");

      fflush (stdout);
      fflush (stderr);
      close (1);
      close (2);
      
      exit (0);
    } 
  
  return 0;
 oom:
  fprintf (stderr, "Out of memory!");
  exit (1);
}
示例#6
0
EXPORT_C
#endif
dbus_bool_t
_dbus_spawn_async_with_babysitter (DBusBabysitter          **sitter_p,
                                   char                    **argv,
                                   DBusSpawnChildSetupFunc   child_setup,
                                   void                     *user_data,
                                   DBusError                *error)
{

#ifndef __SYMBIAN32__
  DBusBabysitter *sitter;
  int child_err_report_pipe[2] = { -1, -1 };
  int babysitter_pipe[2] = { -1, -1 };
  pid_t pid;
  
  #ifndef __SYMBIAN32__
  _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]);
  _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
   * 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
       */
#ifndef __SYMBIAN32__       
      signal (SIGPIPE, SIG_DFL);
#endif      

      /* 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);
  #endif
  return FALSE;
  
  
#else


/*
FILE* ChildProcessStream;
 ChildProcessStream =popen(argv[0], "r");
	if (ChildProcessStream == NULL)
	{
	 dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
                      "Failed to execute service process");
     	 return FALSE;
      
	}
*/
 //static pid_t Childpid; //have to make it static for thread fuction to access it
 pid_t childPid;
 int retVal; 
 
 //pthread_t thread;

 retVal = posix_spawn(&childPid, argv[0], NULL,NULL, NULL, NULL);
  	if (retVal!=0)
	{
	 dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
                      "Failed to execute service process");
     	 return FALSE;
      
	}

/*
 pthread_create(&thread, NULL, (void*)&thread_fun, &Childpid);
//(void) waitpid(Childpid, NULL, 0);
//printf("\r\n*** Child process finished ***\r\n");

*/

if(!retVal)
	return TRUE;
else
	return FALSE;

#endif  
}