Example #1
0
/**
 * Creates a VMCI stream socket connected to the given cid and port.
 * The connection fd is returned, and is set up as nonblocking.
 *
 * This will set FD_CLOEXEC for the socket returned.
 *
 * @param path the path to UNIX domain socket
 * @param abstract #TRUE to use abstract namespace
 * @param error return location for error code
 * @returns connection file descriptor or -1 on error
 */
static int
_dbus_connect_vmci (const char     *cid_str,
                    const char     *port_str,
                    DBusError      *error)
{
  int fd;
  size_t path_len;
  struct sockaddr_vm addr;
  unsigned int af_vmci, cid, port;

  _DBUS_ASSERT_ERROR_IS_CLEAR (error);

  _dbus_verbose ("connecting to VMCI stream socket %s:%s\n", cid_str, port_str);
  cid = atoi(cid_str);
  port = atoi(port_str);
  if ((af_vmci = VMCISock_GetAFValue()) < 0) {
    assert(0);
  }
  if (_dbus_open_socket(&fd, af_vmci, SOCK_STREAM, 0, error) < 0) {
    return fd;
  }
  _DBUS_ZERO (addr);
  addr.svm_family = af_vmci;
  addr.svm_cid = cid;
  addr.svm_port = port;
  if (connect (fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
    dbus_set_error (error,
        _dbus_error_from_errno (errno),
        "Failed to connect to VMCI (cid, port): %s:%s: %s",
        cid_str, port_str, _dbus_strerror (errno));

    _dbus_close (fd, NULL);
    fd = -1;

    return -1;
  }
  if (!_dbus_set_fd_nonblocking (fd, error))
    {
      _DBUS_ASSERT_ERROR_IS_SET (error);

      _dbus_close (fd, NULL);
      fd = -1;

      return -1;
    }

  return fd;
}
Example #2
0
/**
 * close a pipe.
 *
 * @param pipe the pipe instance
 * @param error return location for an error
 * @returns -1 if error is set
 */
int
_dbus_pipe_close  (DBusPipe         *pipe,
                   DBusError        *error)
{
  if (!_dbus_close (pipe->fd, error))
    {
      return -1;
    }
  else
    {
      _dbus_pipe_invalidate (pipe);
      return 0;
    }
}
dbus_bool_t
_dbus_write_pid_file (const DBusString *filename,
                      unsigned long     pid,
		      DBusError        *error)
{
  const char *cfilename;
  int fd;
  FILE *f;

  cfilename = _dbus_string_get_const_data (filename);
  
  fd = open (cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644);
  
  if (fd < 0)
    {
      dbus_set_error (error, _dbus_error_from_errno (errno),
                      "Failed to open \"%s\": %s", cfilename,
                      _dbus_strerror (errno));
      return FALSE;
    }

  if ((f = fdopen (fd, "w")) == NULL)
    {
      dbus_set_error (error, _dbus_error_from_errno (errno),
                      "Failed to fdopen fd %d: %s", fd, _dbus_strerror (errno));
      _dbus_close (fd, NULL);
      return FALSE;
    }
  
  if (fprintf (f, "%lu\n", pid) < 0)
    {
      dbus_set_error (error, _dbus_error_from_errno (errno),
                      "Failed to write to \"%s\": %s", cfilename,
                      _dbus_strerror (errno));
      
      fclose (f);
      return FALSE;
    }

  if (fclose (f) == EOF)
    {
      dbus_set_error (error, _dbus_error_from_errno (errno),
                      "Failed to close \"%s\": %s", cfilename,
                      _dbus_strerror (errno));
      return FALSE;
    }
  
  return TRUE;
}
Example #4
0
/* Avoids a danger in re-entrant situations (calling close()
 * on a file descriptor twice, and another module has
 * re-opened it since the first close).
 *
 * This previously claimed to be relevant for threaded situations, but by
 * trivial inspection, it is not thread-safe. It doesn't actually
 * matter, since this module is only used in the -util variant of the
 * library, which is only used in single-threaded situations.
 */
static int
close_and_invalidate (int *fd)
{
  int ret;

  if (*fd < 0)
    return -1;
  else
    {
      ret = _dbus_close (*fd, NULL);
      *fd = -1;
    }

  return ret;
}
Example #5
0
static void
close_error_pipe_from_child (DBusBabysitter *sitter)
{
  _dbus_verbose ("Closing child error\n");

  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 (sitter->error_pipe_from_child, NULL);
      sitter->error_pipe_from_child = -1;
    }
}
Example #6
0
/**
 * 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;
}
Example #7
0
/**
 * Get a printable string describing the command used to execute
 * the process with pid.  This string should only be used for
 * informative purposes such as logging; it may not be trusted.
 * 
 * The command is guaranteed to be printable ASCII and no longer
 * than max_len.
 * 
 * @param pid Process id
 * @param str Append command to this string
 * @param max_len Maximum length of returned command
 * @param error return location for errors
 * @returns #FALSE on error
 */
dbus_bool_t 
_dbus_command_for_pid (unsigned long  pid,
                       DBusString    *str,
                       int            max_len,
                       DBusError     *error)
{
  /* This is all Linux-specific for now */
  DBusString path;
  DBusString cmdline;
  int fd;
  
  if (!_dbus_string_init (&path)) 
    {
      _DBUS_SET_OOM (error);
      return FALSE;
    }
  
  if (!_dbus_string_init (&cmdline))
    {
      _DBUS_SET_OOM (error);
      _dbus_string_free (&path);
      return FALSE;
    }
  
  if (!_dbus_string_append_printf (&path, "/proc/%ld/cmdline", pid))
    goto oom;
  
  fd = open (_dbus_string_get_const_data (&path), O_RDONLY);
  if (fd < 0) 
    {
      dbus_set_error (error,
                      _dbus_error_from_errno (errno),
                      "Failed to open \"%s\": %s",
                      _dbus_string_get_const_data (&path),
                      _dbus_strerror (errno));
      goto fail;
    }
  
  if (!_dbus_read (fd, &cmdline, max_len))
    {
      dbus_set_error (error,
                      _dbus_error_from_errno (errno),
                      "Failed to read from \"%s\": %s",
                      _dbus_string_get_const_data (&path),
                      _dbus_strerror (errno));      
      goto fail;
    }
  
  if (!_dbus_close (fd, error))
    goto fail;
  
  string_squash_nonprintable (&cmdline);  

  if (!_dbus_string_copy (&cmdline, 0, str, _dbus_string_get_length (str)))
    goto oom;

  _dbus_string_free (&cmdline);  
  _dbus_string_free (&path);
  return TRUE;
oom:
  _DBUS_SET_OOM (error);
fail:
  _dbus_string_free (&cmdline);
  _dbus_string_free (&path);
  return FALSE;
}