a6591a
From dc2074588d3e7b5a216cb8c0b82094157c3cf773 Mon Sep 17 00:00:00 2001
a6591a
From: David King <dking@redhat.com>
a6591a
Date: Mon, 25 Jun 2018 14:46:14 -0400
a6591a
Subject: [PATCH] daemon: use HOME as the working directory
a6591a
a6591a
Session buses started as part of a systemd --user session are launched
a6591a
with the current working directory being the home directory of the user.
a6591a
Applications which are launched via dbus activation inherit the working
a6591a
directory from the session bus dbus-daemon.
a6591a
a6591a
When dbus-launch is used to start dbus-daemon, as is commonly the case
a6591a
with a session manager such as gnome-session, this leads to applications
a6591a
having a default working directory of "/", which is undesirable (as an
a6591a
example, the default directory in a GTK+ save dialog becomes "/").
a6591a
a6591a
As an improvement, make dbus-launch use the value of the environment
a6591a
variable HOME, if it is set, as the current working directory.
a6591a
a6591a
Signed-off-by: David King <dking@redhat.com>
a6591a
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=106987
a6591a
Bug-RedHat: https://bugzilla.redhat.com/show_bug.cgi?id=1470310
a6591a
---
a6591a
 bus/bus.c                     |  9 +++++++++
a6591a
 dbus/dbus-sysdeps-util-unix.c |  8 +++++---
a6591a
 dbus/dbus-sysdeps-util-win.c  |  2 ++
a6591a
 dbus/dbus-sysdeps.h           |  1 +
a6591a
 doc/dbus-launch.1.xml.in      |  4 ++++
a6591a
 tools/dbus-launch.c           | 22 ++++++++++++++--------
a6591a
 6 files changed, 35 insertions(+), 11 deletions(-)
a6591a
a6591a
diff --git a/bus/bus.c b/bus/bus.c
a6591a
index f788e677..da2b2c1f 100644
a6591a
--- a/bus/bus.c
a6591a
+++ b/bus/bus.c
a6591a
@@ -870,63 +870,72 @@ bus_context_new (const DBusString *config_file,
a6591a
 
a6591a
   context->matchmaker = bus_matchmaker_new ();
a6591a
   if (context->matchmaker == NULL)
a6591a
     {
a6591a
       BUS_SET_OOM (error);
a6591a
       goto failed;
a6591a
     }
a6591a
 
a6591a
   /* check user before we fork */
a6591a
   if (context->user != NULL)
a6591a
     {
a6591a
       if (!_dbus_verify_daemon_user (context->user))
a6591a
         {
a6591a
           dbus_set_error (error, DBUS_ERROR_FAILED,
a6591a
                           "Could not get UID and GID for username \"%s\"",
a6591a
                           context->user);
a6591a
           goto failed;
a6591a
         }
a6591a
     }
a6591a
 
a6591a
   /* Now become a daemon if appropriate and write out pid file in any case */
a6591a
   {
a6591a
     DBusString u;
a6591a
 
a6591a
     if (context->pidfile)
a6591a
       _dbus_string_init_const (&u, context->pidfile);
a6591a
 
a6591a
     if (((flags & BUS_CONTEXT_FLAG_FORK_NEVER) == 0 && context->fork) ||
a6591a
         (flags & BUS_CONTEXT_FLAG_FORK_ALWAYS))
a6591a
       {
a6591a
+        const char *working_dir = NULL;
a6591a
+
a6591a
         _dbus_verbose ("Forking and becoming daemon\n");
a6591a
 
a6591a
+        if (context->type != NULL && strcmp (context->type, "session") == 0)
a6591a
+          working_dir = _dbus_getenv ("HOME");
a6591a
+
a6591a
+        if (working_dir == NULL)
a6591a
+          working_dir = "/";
a6591a
+
a6591a
         if (!_dbus_become_daemon (context->pidfile ? &u : NULL,
a6591a
+                                  working_dir,
a6591a
                                   print_pid_pipe,
a6591a
                                   error,
a6591a
                                   context->keep_umask))
a6591a
           {
a6591a
             _DBUS_ASSERT_ERROR_IS_SET (error);
a6591a
             goto failed;
a6591a
           }
a6591a
       }
a6591a
     else
a6591a
       {
a6591a
         _dbus_verbose ("Fork not requested\n");
a6591a
 
a6591a
         /* Need to write PID file and to PID pipe for ourselves,
a6591a
          * not for the child process. This is a no-op if the pidfile
a6591a
          * is NULL and print_pid_pipe is NULL.
a6591a
          */
a6591a
         if (!_dbus_write_pid_to_file_and_pipe (context->pidfile ? &u : NULL,
a6591a
                                                print_pid_pipe,
a6591a
                                                _dbus_getpid (),
a6591a
                                                error))
a6591a
           {
a6591a
             _DBUS_ASSERT_ERROR_IS_SET (error);
a6591a
             goto failed;
a6591a
           }
a6591a
       }
a6591a
   }
a6591a
 
a6591a
   if (print_pid_pipe && _dbus_pipe_is_valid (print_pid_pipe) &&
a6591a
       !_dbus_pipe_is_stdout_or_stderr (print_pid_pipe))
a6591a
     _dbus_pipe_close (print_pid_pipe, NULL);
a6591a
diff --git a/dbus/dbus-sysdeps-util-unix.c b/dbus/dbus-sysdeps-util-unix.c
a6591a
index 9b724cc9..30bb1441 100644
a6591a
--- a/dbus/dbus-sysdeps-util-unix.c
a6591a
+++ b/dbus/dbus-sysdeps-util-unix.c
a6591a
@@ -49,82 +49,84 @@
a6591a
 #include <sys/socket.h>
a6591a
 #include <dirent.h>
a6591a
 #include <sys/un.h>
a6591a
 
a6591a
 #ifdef HAVE_SYSLOG_H
a6591a
 #include <syslog.h>
a6591a
 #endif
a6591a
 
a6591a
 #ifdef HAVE_SYS_SYSLIMITS_H
a6591a
 #include <sys/syslimits.h>
a6591a
 #endif
a6591a
 
a6591a
 #ifdef HAVE_SYSTEMD
a6591a
 #include <systemd/sd-daemon.h>
a6591a
 #endif
a6591a
 
a6591a
 #ifndef O_BINARY
a6591a
 #define O_BINARY 0
a6591a
 #endif
a6591a
 
a6591a
 /**
a6591a
  * @addtogroup DBusInternalsUtils
a6591a
  * @{
a6591a
  */
a6591a
 
a6591a
 
a6591a
 /**
a6591a
  * Does the chdir, fork, setsid, etc. to become a daemon process.
a6591a
  *
a6591a
  * @param pidfile #NULL, or pidfile to create
a6591a
+ * @param working_dir directory to chdir to
a6591a
  * @param print_pid_pipe pipe to print daemon's pid to, or -1 for none
a6591a
  * @param error return location for errors
a6591a
  * @param keep_umask #TRUE to keep the original umask
a6591a
  * @returns #FALSE on failure
a6591a
  */
a6591a
 dbus_bool_t
a6591a
 _dbus_become_daemon (const DBusString *pidfile,
a6591a
+                     const char       *working_dir,
a6591a
                      DBusPipe         *print_pid_pipe,
a6591a
                      DBusError        *error,
a6591a
                      dbus_bool_t       keep_umask)
a6591a
 {
a6591a
   const char *s;
a6591a
   pid_t child_pid;
a6591a
   int dev_null_fd;
a6591a
 
a6591a
   _dbus_verbose ("Becoming a daemon...\n");
a6591a
 
a6591a
-  _dbus_verbose ("chdir to /\n");
a6591a
-  if (chdir ("/") < 0)
a6591a
+  _dbus_verbose ("chdir to %s\n", working_dir);
a6591a
+  if (chdir (working_dir) < 0)
a6591a
     {
a6591a
       dbus_set_error (error, DBUS_ERROR_FAILED,
a6591a
-                      "Could not chdir() to root directory");
a6591a
+                      "Could not chdir() to working directory (%s)", working_dir);
a6591a
       return FALSE;
a6591a
     }
a6591a
 
a6591a
   _dbus_verbose ("forking...\n");
a6591a
   switch ((child_pid = fork ()))
a6591a
     {
a6591a
     case -1:
a6591a
       _dbus_verbose ("fork failed\n");
a6591a
       dbus_set_error (error, _dbus_error_from_errno (errno),
a6591a
                       "Failed to fork daemon: %s", _dbus_strerror (errno));
a6591a
       return FALSE;
a6591a
       break;
a6591a
 
a6591a
     case 0:
a6591a
       _dbus_verbose ("in child, closing std file descriptors\n");
a6591a
 
a6591a
       /* silently ignore failures here, if someone
a6591a
        * doesn't have /dev/null we may as well try
a6591a
        * to continue anyhow
a6591a
        */
a6591a
       
a6591a
       dev_null_fd = open ("/dev/null", O_RDWR);
a6591a
       if (dev_null_fd >= 0)
a6591a
         {
a6591a
           dup2 (dev_null_fd, 0);
a6591a
           dup2 (dev_null_fd, 1);
a6591a
           
a6591a
           s = _dbus_getenv ("DBUS_DEBUG_OUTPUT");
a6591a
           if (s == NULL || *s == '\0')
a6591a
             dup2 (dev_null_fd, 2);
a6591a
diff --git a/dbus/dbus-sysdeps-util-win.c b/dbus/dbus-sysdeps-util-win.c
a6591a
index 3b754dbf..bfc1cb90 100644
a6591a
--- a/dbus/dbus-sysdeps-util-win.c
a6591a
+++ b/dbus/dbus-sysdeps-util-win.c
a6591a
@@ -27,67 +27,69 @@
a6591a
 #define STRSAFE_NO_DEPRECATE
a6591a
 
a6591a
 #include "dbus-sysdeps.h"
a6591a
 #include "dbus-internals.h"
a6591a
 #include "dbus-protocol.h"
a6591a
 #include "dbus-string.h"
a6591a
 #include "dbus-sysdeps.h"
a6591a
 #include "dbus-sysdeps-win.h"
a6591a
 #include "dbus-sockets-win.h"
a6591a
 #include "dbus-memory.h"
a6591a
 #include "dbus-pipe.h"
a6591a
 
a6591a
 #include <stdio.h>
a6591a
 #include <stdlib.h>
a6591a
 #if HAVE_ERRNO_H
a6591a
 #include <errno.h>
a6591a
 #endif
a6591a
 #include <winsock2.h>   // WSA error codes
a6591a
 
a6591a
 #ifndef DBUS_WINCE
a6591a
 #include <io.h>
a6591a
 #include <lm.h>
a6591a
 #include <sys/stat.h>
a6591a
 #endif
a6591a
 
a6591a
 
a6591a
 /**
a6591a
  * Does the chdir, fork, setsid, etc. to become a daemon process.
a6591a
  *
a6591a
  * @param pidfile #NULL, or pidfile to create
a6591a
+ * @param working_dir directory to chdir to
a6591a
  * @param print_pid_pipe file descriptor to print daemon's pid to, or -1 for none
a6591a
  * @param error return location for errors
a6591a
  * @param keep_umask #TRUE to keep the original umask
a6591a
  * @returns #FALSE on failure
a6591a
  */
a6591a
 dbus_bool_t
a6591a
 _dbus_become_daemon (const DBusString *pidfile,
a6591a
+                     const char       *working_dir,
a6591a
                      DBusPipe         *print_pid_pipe,
a6591a
                      DBusError        *error,
a6591a
                      dbus_bool_t       keep_umask)
a6591a
 {
a6591a
   dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
a6591a
                   "Cannot daemonize on Windows");
a6591a
   return FALSE;
a6591a
 }
a6591a
 
a6591a
 /**
a6591a
  * Creates a file containing the process ID.
a6591a
  *
a6591a
  * @param filename the filename to write to
a6591a
  * @param pid our process ID
a6591a
  * @param error return location for errors
a6591a
  * @returns #FALSE on failure
a6591a
  */
a6591a
 static dbus_bool_t
a6591a
 _dbus_write_pid_file (const DBusString *filename,
a6591a
                       unsigned long     pid,
a6591a
                       DBusError        *error)
a6591a
 {
a6591a
   const char *cfilename;
a6591a
   HANDLE hnd;
a6591a
   char pidstr[20];
a6591a
   int total;
a6591a
   int bytes_to_write;
a6591a
 
a6591a
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
a6591a
 
a6591a
diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h
a6591a
index 0ee45c97..e569b545 100644
a6591a
--- a/dbus/dbus-sysdeps.h
a6591a
+++ b/dbus/dbus-sysdeps.h
a6591a
@@ -498,60 +498,61 @@ int _dbus_printf_string_upper_bound (const char *format,
a6591a
                                      va_list args);
a6591a
 
a6591a
 
a6591a
 /**
a6591a
  * Portable struct with stat() results
a6591a
  */
a6591a
 typedef struct
a6591a
 {
a6591a
   unsigned long mode;  /**< File mode */
a6591a
   unsigned long nlink; /**< Number of hard links */
a6591a
   dbus_uid_t    uid;   /**< User owning file */
a6591a
   dbus_gid_t    gid;   /**< Group owning file */
a6591a
   unsigned long size;  /**< Size of file */
a6591a
   unsigned long atime; /**< Access time */
a6591a
   unsigned long mtime; /**< Modify time */
a6591a
   unsigned long ctime; /**< Creation time */
a6591a
 } DBusStat;
a6591a
 
a6591a
 dbus_bool_t _dbus_stat             (const DBusString *filename,
a6591a
                                     DBusStat         *statbuf,
a6591a
                                     DBusError        *error);
a6591a
 DBUS_PRIVATE_EXPORT
a6591a
 dbus_bool_t _dbus_socketpair (DBusSocket       *fd1,
a6591a
                               DBusSocket       *fd2,
a6591a
                               dbus_bool_t       blocking,
a6591a
                               DBusError        *error);
a6591a
 
a6591a
 void        _dbus_print_backtrace  (void);
a6591a
 
a6591a
 dbus_bool_t _dbus_become_daemon   (const DBusString *pidfile,
a6591a
+                                   const char       *working_dir,
a6591a
                                    DBusPipe         *print_pid_pipe,
a6591a
                                    DBusError        *error,
a6591a
                                    dbus_bool_t       keep_umask);
a6591a
 
a6591a
 dbus_bool_t _dbus_verify_daemon_user    (const char *user);
a6591a
 dbus_bool_t _dbus_change_to_daemon_user (const char *user,
a6591a
                                          DBusError  *error);
a6591a
 
a6591a
 dbus_bool_t _dbus_write_pid_to_file_and_pipe (const DBusString *pidfile,
a6591a
                                               DBusPipe         *print_pid_pipe,
a6591a
                                               dbus_pid_t        pid_to_write,
a6591a
                                               DBusError        *error);
a6591a
 
a6591a
 dbus_bool_t _dbus_command_for_pid (unsigned long  pid,
a6591a
                                    DBusString    *str,
a6591a
                                    int            max_len,
a6591a
                                    DBusError     *error);
a6591a
 
a6591a
 /** A UNIX signal handler */
a6591a
 typedef void (* DBusSignalHandler) (int sig);
a6591a
 
a6591a
 void _dbus_set_signal_handler (int               sig,
a6591a
                                DBusSignalHandler handler);
a6591a
 
a6591a
 dbus_bool_t _dbus_user_at_console (const char *username,
a6591a
                                    DBusError  *error);
a6591a
 
a6591a
 void _dbus_init_system_log (dbus_bool_t is_daemon);
a6591a
 
a6591a
 typedef enum {
a6591a
diff --git a/doc/dbus-launch.1.xml.in b/doc/dbus-launch.1.xml.in
a6591a
index 5135d9ca..606c65be 100644
a6591a
--- a/doc/dbus-launch.1.xml.in
a6591a
+++ b/doc/dbus-launch.1.xml.in
a6591a
@@ -23,60 +23,64 @@
a6591a
   <command>dbus-launch</command>
a6591a
     <arg choice='opt'>--version </arg>
a6591a
     <arg choice='opt'>--help </arg>
a6591a
     <arg choice='opt'>--sh-syntax </arg>
a6591a
     <arg choice='opt'>--csh-syntax </arg>
a6591a
     <arg choice='opt'>--auto-syntax </arg>
a6591a
     <arg choice='opt'>--binary-syntax </arg>
a6591a
     <arg choice='opt'>--close-stderr </arg>
a6591a
     <arg choice='opt'>--exit-with-session </arg>
a6591a
     <arg choice='opt'>--autolaunch=<replaceable>MACHINEID</replaceable></arg>
a6591a
     <arg choice='opt'>--config-file=<replaceable>FILENAME</replaceable></arg>
a6591a
     <arg choice='opt'><replaceable>PROGRAM</replaceable></arg>
a6591a
     <arg choice='opt' rep='repeat'><replaceable>ARGS</replaceable></arg>
a6591a
     <sbr/>
a6591a
 </cmdsynopsis>
a6591a
 </refsynopsisdiv>
a6591a
 
a6591a
 
a6591a
 <refsect1 id='description'><title>DESCRIPTION</title>
a6591a
 <para>The <command>dbus-launch</command> command is used to start a session bus
a6591a
 instance of <emphasis remap='I'>dbus-daemon</emphasis> from a shell script.
a6591a
 It would normally be called from a user's login
a6591a
 scripts. Unlike the daemon itself, <command>dbus-launch</command> exits, so
a6591a
 backticks or the $() construct can be used to read information from
a6591a
 <command>dbus-launch</command>.</para>
a6591a
 
a6591a
 <para>With no arguments, <command>dbus-launch</command> will launch a session bus
a6591a
 instance and print the address and PID of that instance to standard
a6591a
 output.</para>
a6591a
 
a6591a
+<para>If the environment variable HOME is set, it is used as the current
a6591a
+working directory. Otherwise, the root directory (<filename>/</filename>) is
a6591a
+used.</para>
a6591a
+
a6591a
 <para>You may specify a program to be run; in this case, <command>dbus-launch</command>
a6591a
 will launch a session bus instance, set the appropriate environment
a6591a
 variables so the specified program can find the bus, and then execute the
a6591a
 specified program, with the specified arguments.  See below for
a6591a
 examples.</para>
a6591a
 
a6591a
 <para>If you launch a program, <command>dbus-launch</command> will not print the
a6591a
 information about the new bus to standard output.</para>
a6591a
 
a6591a
 <para>When <command>dbus-launch</command> prints bus information to standard output, by
a6591a
 default it is in a simple key-value pairs format. However, you may
a6591a
 request several alternate syntaxes using the --sh-syntax, --csh-syntax,
a6591a
 --binary-syntax, or
a6591a
 --auto-syntax options. Several of these cause <command>dbus-launch</command> to emit shell code
a6591a
 to set up the environment.</para>
a6591a
 
a6591a
 <para>With the --auto-syntax option, <command>dbus-launch</command> looks at the value
a6591a
 of the SHELL environment variable to determine which shell syntax
a6591a
 should be used.  If SHELL ends in "csh", then csh-compatible code is
a6591a
 emitted; otherwise Bourne shell code is emitted.  Instead of passing
a6591a
 --auto-syntax, you may explicitly specify a particular one by using
a6591a
 --sh-syntax for Bourne syntax, or --csh-syntax for csh syntax.
a6591a
 In scripts, it's more robust to avoid --auto-syntax and you hopefully
a6591a
 know which shell your script is written in.</para>
a6591a
 
a6591a
 
a6591a
 <para>See <ulink url='http://www.freedesktop.org/software/dbus/'>http://www.freedesktop.org/software/dbus/</ulink> for more information
a6591a
 about D-Bus. See also the man page for <emphasis remap='I'>dbus-daemon</emphasis>.</para>
a6591a
 
a6591a
 </refsect1>
a6591a
diff --git a/tools/dbus-launch.c b/tools/dbus-launch.c
a6591a
index 80e4a241..a956684c 100644
a6591a
--- a/tools/dbus-launch.c
a6591a
+++ b/tools/dbus-launch.c
a6591a
@@ -592,71 +592,77 @@ kill_bus_when_session_ends (void)
a6591a
                   /* This shouldn't happen I don't think; to avoid
a6591a
                    * spinning on the fd forever we exit.
a6591a
                    */
a6591a
                   fprintf (stderr, "dbus-launch: error reading from stdin: %s\n",
a6591a
                            strerror (errno));
a6591a
                   kill_bus_and_exit (0);
a6591a
                 }
a6591a
             }
a6591a
           else if (FD_ISSET (tty_fd, &err_set))
a6591a
             {
a6591a
               verbose ("TTY has error condition\n");
a6591a
               
a6591a
               kill_bus_and_exit (0);
a6591a
             }
a6591a
         }
a6591a
     }
a6591a
 }
a6591a
 
a6591a
 static void
a6591a
 babysit (int   exit_with_session,
a6591a
          pid_t child_pid,
a6591a
          int   read_bus_pid_fd)  /* read pid from here */
a6591a
 {
a6591a
   int ret;
a6591a
   int dev_null_fd;
a6591a
   const char *s;
a6591a
 
a6591a
   verbose ("babysitting, exit_with_session = %d, child_pid = %ld, read_bus_pid_fd = %d\n",
a6591a
            exit_with_session, (long) child_pid, read_bus_pid_fd);
a6591a
   
a6591a
-  /* We chdir ("/") since we are persistent and daemon-like, and fork
a6591a
-   * again so dbus-launch can reap the parent.  However, we don't
a6591a
-   * setsid() or close fd 0 because the idea is to remain attached
a6591a
-   * to the tty and the X server in order to kill the message bus
a6591a
-   * when the session ends.
a6591a
+  /* We chdir () since we are persistent and daemon-like, either to $HOME
a6591a
+   * to match the behaviour of a session bus started by systemd --user, or
a6591a
+   * otherwise "/". We fork again so dbus-launch can reap the parent.
a6591a
+   * However, we don't setsid() or close fd 0 because the idea is to
a6591a
+   * remain attached to the tty and the X server in order to kill the
a6591a
+   * message bus when the session ends.
a6591a
    */
a6591a
 
a6591a
-  if (chdir ("/") < 0)
a6591a
+  s = getenv ("HOME");
a6591a
+
a6591a
+  if (s == NULL || *s == '\0')
a6591a
+    s = "/";
a6591a
+
a6591a
+  if (chdir (s) < 0)
a6591a
     {
a6591a
-      fprintf (stderr, "Could not change to root directory: %s\n",
a6591a
-               strerror (errno));
a6591a
+      fprintf (stderr, "Could not change to working directory \"%s\": %s\n",
a6591a
+               s, strerror (errno));
a6591a
       exit (1);
a6591a
     }
a6591a
 
a6591a
   /* Close stdout/stderr so we don't block an "eval" or otherwise
a6591a
    * lock up. stdout is still chaining through to dbus-launch
a6591a
    * and in turn to the parent shell.
a6591a
    */
a6591a
   dev_null_fd = open ("/dev/null", O_RDWR);
a6591a
   if (dev_null_fd >= 0)
a6591a
     {
a6591a
       if (!exit_with_session)
a6591a
         dup2 (dev_null_fd, 0);
a6591a
       dup2 (dev_null_fd, 1);
a6591a
       s = getenv ("DBUS_DEBUG_OUTPUT");
a6591a
       if (s == NULL || *s == '\0')
a6591a
         dup2 (dev_null_fd, 2);
a6591a
       close (dev_null_fd);
a6591a
     }
a6591a
   else
a6591a
     {
a6591a
       fprintf (stderr, "Failed to open /dev/null: %s\n",
a6591a
                strerror (errno));
a6591a
       /* continue, why not */
a6591a
     }
a6591a
   
a6591a
   ret = fork ();
a6591a
 
a6591a
   if (ret < 0)
a6591a
     {
a6591a
       fprintf (stderr, "fork() failed in babysitter: %s\n",
a6591a
-- 
a6591a
2.17.1
a6591a