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