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