2003-05-04 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Sun, 4 May 2003 08:54:24 +0000 (08:54 +0000)
committerHavoc Pennington <hp@redhat.com>
Sun, 4 May 2003 08:54:24 +0000 (08:54 +0000)
* tools/dbus-launch.c: implement

* bus/main.c (main), bus/bus.c (bus_context_new):
implement --print-pid and --fork

ChangeLog
bus/bus.c
bus/bus.h
bus/dbus-daemon-1.1.in
bus/main.c
bus/test.c
configure.in
tools/Makefile.am
tools/dbus-launch.1
tools/dbus-launch.c

index ac82795..a3b0ce6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2003-05-04  Havoc Pennington  <hp@pobox.com>
+
+       * tools/dbus-launch.c: implement
+
+       * bus/main.c (main), bus/bus.c (bus_context_new): 
+       implement --print-pid and --fork
+
 2003-05-03  Havoc Pennington  <hp@redhat.com>
 
        * dbus/dbus-address.c (dbus_parse_address): fix bug when a key in
index cabc0b1..85d737d 100644 (file)
--- a/bus/bus.c
+++ b/bus/bus.c
@@ -280,7 +280,9 @@ setup_server (BusContext *context,
 
 BusContext*
 bus_context_new (const DBusString *config_file,
+                 dbus_bool_t       force_fork,
                  int               print_addr_fd,
+                 int               print_pid_fd,
                  DBusError        *error)
 {
   BusContext *context;
@@ -540,7 +542,7 @@ bus_context_new (const DBusString *config_file,
   _dbus_assert (context->policy != NULL);
   
   /* Now become a daemon if appropriate */
-  if (bus_config_parser_get_fork (parser))
+  if (force_fork || bus_config_parser_get_fork (parser))
     {
       DBusString u;
 
@@ -567,6 +569,42 @@ bus_context_new (const DBusString *config_file,
   /* keep around the pid filename so we can delete it later */
   context->pidfile = _dbus_strdup (pidfile);
 
+  /* Write PID if requested */
+  if (print_pid_fd >= 0)
+    {
+      DBusString pid;
+      int bytes;
+
+      if (!_dbus_string_init (&pid))
+        {
+          BUS_SET_OOM (error);
+          goto failed;
+        }
+      
+      if (!_dbus_string_append_int (&pid, _dbus_getpid ()) ||
+          !_dbus_string_append (&pid, "\n"))
+        {
+          _dbus_string_free (&pid);
+          BUS_SET_OOM (error);
+          goto failed;
+        }
+
+      bytes = _dbus_string_get_length (&pid);
+      if (_dbus_write (print_pid_fd, &pid, 0, bytes) != bytes)
+        {
+          dbus_set_error (error, DBUS_ERROR_FAILED,
+                          "Printing message bus PID: %s\n",
+                          _dbus_strerror (errno));
+          _dbus_string_free (&pid);
+          goto failed;
+        }
+
+      if (print_pid_fd > 2)
+        _dbus_close (print_pid_fd, NULL);
+      
+      _dbus_string_free (&pid);
+    }
+  
   /* Here we change our credentials if required,
    * as soon as we've set up our sockets and pidfile
    */
index 747e009..8f32d7a 100644 (file)
--- a/bus/bus.h
+++ b/bus/bus.h
@@ -57,7 +57,9 @@ typedef struct
 } BusLimits;
 
 BusContext*       bus_context_new                             (const DBusString *config_file,
+                                                               dbus_bool_t       force_fork,
                                                                int               print_addr_fd,
+                                                               int               print_pid_fd,
                                                                DBusError        *error);
 void              bus_context_shutdown                        (BusContext       *context);
 void              bus_context_ref                             (BusContext       *context);
index 3339aeb..73a88c9 100644 (file)
@@ -9,7 +9,7 @@ dbus-daemon-1 \- Message bus daemon
 .PP
 .B dbus-daemon-1
 dbus-daemon-1 [\-\-version] [\-\-session] [\-\-system] [\-\-config-file=FILE]
-[\-\-print-address[=DESCRIPTOR]]
+[\-\-print-address[=DESCRIPTOR]] [\-\-print-pid[=DESCRIPTOR]] [\-\-fork]
 
 .SH DESCRIPTION
 
@@ -62,11 +62,22 @@ The following options are supported:
 .I "--config-file=FILE"
 Use the given configuration file.
 .TP
+.I "--fork"
+Force the message bus to fork and become a daemon, even if 
+the configuration file does not specify that it should.
+In most contexts the configuration file already gets this
+right, though.
+.TP
 .I "--print-address[=DESCRIPTOR]"
 Print the address of the message bus to standard output, or 
 to the given file descriptor. This is used by programs that 
 launch the message bus.
 .TP
+.I "--print-pid[=DESCRIPTOR]"
+Print the process ID of the message bus to standard output, or 
+to the given file descriptor. This is used by programs that 
+launch the message bus.
+.TP
 .I "--session"
 Use the standard configuration file for the per-login-session message
 bus.
@@ -185,7 +196,8 @@ privileges for writing.
     
 .PP
 If present, the bus daemon becomes a real daemon (forks 
-into the background, etc.).
+into the background, etc.). This is generally used 
+rather than the \-\-fork command line option.
 
 .TP
 .I "<listen>"
index de78368..8c605be 100644 (file)
@@ -47,7 +47,7 @@ signal_handler (int sig)
 static void
 usage (void)
 {
-  fprintf (stderr, "dbus-daemon-1 [--version] [--session] [--system] [--config-file=FILE] [--print-address[=DESCRIPTOR]]\n");
+  fprintf (stderr, "dbus-daemon-1 [--version] [--session] [--system] [--config-file=FILE] [--print-address[=DESCRIPTOR]] [--print-pid[=DESCRIPTOR]] [--fork]\n");
   exit (1);
 }
 
@@ -86,24 +86,45 @@ check_two_addr_descriptors (const DBusString *addr_fd,
     }
 }
 
+static void
+check_two_pid_descriptors (const DBusString *pid_fd,
+                           const char       *extra_arg)
+{
+  if (_dbus_string_get_length (pid_fd) > 0)
+    {
+      fprintf (stderr, "--%s specified but printing pid to %s already requested\n",
+               extra_arg, _dbus_string_get_const_data (pid_fd));
+      exit (1);
+    }
+}
+
 int
 main (int argc, char **argv)
 {
   DBusError error;
   DBusString config_file;
   DBusString addr_fd;
+  DBusString pid_fd;
   const char *prev_arg;
   int print_addr_fd;
+  int print_pid_fd;
   int i;
   dbus_bool_t print_address;
+  dbus_bool_t print_pid;
+  dbus_bool_t force_fork;
   
   if (!_dbus_string_init (&config_file))
     return 1;
 
   if (!_dbus_string_init (&addr_fd))
     return 1;
+
+  if (!_dbus_string_init (&pid_fd))
+    return 1;
   
   print_address = FALSE;
+  print_pid = FALSE;
+  force_fork = FALSE;
   
   prev_arg = NULL;
   i = 1;
@@ -117,6 +138,8 @@ main (int argc, char **argv)
         usage ();
       else if (strcmp (arg, "--version") == 0)
         version ();
+      else if (strcmp (arg, "--fork") == 0)
+        force_fork = TRUE;
       else if (strcmp (arg, "--system") == 0)
         {
           check_two_config_files (&config_file, "system");
@@ -179,6 +202,32 @@ main (int argc, char **argv)
         }
       else if (strcmp (arg, "--print-address") == 0)
         print_address = TRUE; /* and we'll get the next arg if appropriate */
+      else if (strstr (arg, "--print-pid=") == arg)
+        {
+          const char *desc;
+
+          check_two_pid_descriptors (&pid_fd, "print-pid");
+          
+          desc = strchr (arg, '=');
+          ++desc;
+
+          if (!_dbus_string_append (&pid_fd, desc))
+            exit (1);
+
+          print_pid = TRUE;
+        }
+      else if (prev_arg &&
+               strcmp (prev_arg, "--print-pid") == 0)
+        {
+          check_two_pid_descriptors (&pid_fd, "print-pid");
+          
+          if (!_dbus_string_append (&pid_fd, arg))
+            exit (1);
+          
+          print_pid = TRUE;
+        }
+      else if (strcmp (arg, "--print-pid") == 0)
+        print_pid = TRUE; /* and we'll get the next arg if appropriate */
       else
         usage ();
       
@@ -213,9 +262,32 @@ main (int argc, char **argv)
           print_addr_fd = val;
         }
     }
+
+  print_pid_fd = -1;
+  if (print_pid)
+    {
+      print_pid_fd = 1; /* stdout */
+      if (_dbus_string_get_length (&pid_fd) > 0)
+        {
+          long val;
+          int end;
+          if (!_dbus_string_parse_int (&pid_fd, 0, &val, &end) ||
+              end != _dbus_string_get_length (&pid_fd) ||
+              val < 0 || val > _DBUS_INT_MAX)
+            {
+              fprintf (stderr, "Invalid file descriptor: \"%s\"\n",
+                       _dbus_string_get_const_data (&pid_fd));
+              exit (1);
+            }
+
+          print_pid_fd = val;
+        }
+    }
   
   dbus_error_init (&error);
-  context = bus_context_new (&config_file, print_addr_fd, &error);
+  context = bus_context_new (&config_file, force_fork,
+                             print_addr_fd, print_pid_fd,
+                             &error);
   _dbus_string_free (&config_file);
   if (context == NULL)
     {
index 012d1d0..f8d4c5f 100644 (file)
@@ -377,7 +377,7 @@ bus_context_new_test (const DBusString *test_data_dir,
     }
   
   dbus_error_init (&error);
-  context = bus_context_new (&config_file, -1, &error);
+  context = bus_context_new (&config_file, FALSE, -1, -1, &error);
   if (context == NULL)
     {
       _DBUS_ASSERT_ERROR_IS_SET (&error);
index b36aeba..18cb519 100644 (file)
@@ -440,10 +440,6 @@ AC_SUBST(DBUS_QT_LIBS)
 
 ### X11 detection
 AC_PATH_XTRA
-DBUS_X_LIBS="$X_LIBS $X_PRE_LIBS -lX11 $X_EXTRA_LIBS"
-DBUS_X_CFLAGS="$X_CFLAGS"
-AC_SUBST(DBUS_X_CFLAGS)
-AC_SUBST(DBUS_X_LIBS)
 
 ## for now enable_x11 just tracks have_x11, 
 ## there's no --enable-x11
@@ -457,8 +453,16 @@ fi
 
 if test x$enable_x11 = xyes ; then
    AC_DEFINE(DBUS_BUILD_X11,1,[Build X11-dependent code])
+   DBUS_X_LIBS="$X_LIBS $X_PRE_LIBS -lX11 $X_EXTRA_LIBS"
+   DBUS_X_CFLAGS="$X_CFLAGS"
+else
+   DBUS_X_LIBS=
+   DBUS_X_CFLAGS=
 fi
 
+AC_SUBST(DBUS_X_CFLAGS)
+AC_SUBST(DBUS_X_LIBS)
+
 ### Documentation
 
 AC_PATH_PROG(DOXYGEN, doxygen, no)
index c33caae..60b9dda 100644 (file)
@@ -19,7 +19,8 @@ dbus_launch_SOURCES=                          \
 
 dbus_send_LDADD= $(top_builddir)/dbus/libdbus-1.la
 dbus_monitor_LDADD= $(top_builddir)/glib/libdbus-glib-1.la
-dbus_launch_LDADD= $(top_builddir)/dbus/libdbus-1.la
+## dbus-launch doesn't link to anything
+dbus_launch_LDADD= $(DBUS_X_LIBS)
 
 man_MANS = dbus-send.1 dbus-monitor.1 dbus-launch.1
 EXTRA_DIST = $(man_MANS)
index 9342423..c0fb03f 100644 (file)
@@ -24,14 +24,19 @@ about D-BUS. See also the man page for \fIdbus-daemon-1\fP.
 
 .PP
 Here is an example of how to use \fIdbus-launch\fP with an 
-sh-compatible shell:
+sh-compatible shell to start the per-session bus daemon:
 .nf
 
-  VARIABLES=`dbus-launch`
-  eval $VARIABLES
-  echo "D-BUS per-session daemon address is: $DBUS_SESSION_BUS_ADDRESS"
+  ## test for an existing bus daemon, just to be safe
+  if test -z "$DBUS_SESSION_BUS_ADDRESS" ; then
+      ## if not found, launch a new one
+      eval `dbus-launch --exit-with-session`
+      echo "D-BUS per-session daemon address is: $DBUS_SESSION_BUS_ADDRESS"
+      export DBUS_SESSION_BUS_ADDRESS
+  fi
 
 .fi
+You might run something like that in your login scripts.
 
 .SH OPTIONS
 The following options are supported:
index b21c7e9..f0b9e58 100644 (file)
@@ -21,7 +21,6 @@
  *
  */
 #include <config.h>
-#include <dbus/dbus.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
+#include <signal.h>
+#include <stdarg.h>
 #ifdef DBUS_BUILD_X11
 #include <X11/Xlib.h>
 #endif
 
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#undef MAX
+#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
+
+static void
+verbose (const char *format,
+         ...)
+{
+  va_list args;
+  static int verbose = TRUE;
+  static int verbose_initted = FALSE;
+  
+  /* things are written a bit oddly here so that
+   * in the non-verbose case we just have the one
+   * conditional and return immediately.
+   */
+  if (!verbose)
+    return;
+  
+  if (!verbose_initted)
+    {
+      verbose = getenv ("DBUS_VERBOSE") != NULL;
+      verbose_initted = TRUE;
+      if (!verbose)
+        return;
+    }
+
+  fprintf (stderr, "%lu: ", (unsigned long) getpid ());
+  
+  va_start (args, format);
+  vfprintf (stderr, format, args);
+  va_end (args);
+}
+
 static void
 usage (void)
 {
@@ -52,13 +94,462 @@ version (void)
   exit (0);
 }
 
+typedef enum
+{
+  READ_STATUS_OK,    /**< Read succeeded */
+  READ_STATUS_ERROR, /**< Some kind of error */
+  READ_STATUS_EOF    /**< EOF returned */
+} ReadStatus;
+
+static ReadStatus
+read_line (int        fd,
+           char      *buf,
+           size_t     maxlen)
+{
+  size_t bytes = 0;
+  ReadStatus retval;
+
+  memset (buf, '\0', maxlen);
+  maxlen -= 1; /* ensure nul term */
+  
+  retval = READ_STATUS_OK;
+  
+  while (TRUE)
+    {
+      size_t chunk;    
+      size_t to_read;
+      
+    again:
+      to_read = maxlen - bytes;
+
+      if (to_read == 0)
+        break;
+      
+      chunk = read (fd,
+                    buf + bytes,
+                    to_read);
+      if (chunk < 0 && errno == EINTR)
+        goto again;
+          
+      if (chunk < 0)
+        {
+          retval = READ_STATUS_ERROR;
+          break;
+        }
+      else if (chunk == 0)
+        {
+          retval = READ_STATUS_EOF;
+          break; /* EOF */
+        }
+      else /* chunk > 0 */
+       bytes += chunk;
+    }
+
+  if (retval == READ_STATUS_EOF &&
+      bytes > 0)
+    retval = READ_STATUS_OK;
+  
+  /* whack newline */
+  if (retval != READ_STATUS_ERROR &&
+      bytes > 0 &&
+      buf[bytes-1] == '\n')
+    buf[bytes-1] = '\0';
+  
+  return retval;
+}
+
+static ReadStatus
+read_pid (int        fd,
+          pid_t     *buf)
+{
+  size_t bytes = 0;
+  ReadStatus retval;
+
+  retval = READ_STATUS_OK;
+  
+  while (TRUE)
+    {
+      size_t chunk;    
+      size_t to_read;
+      
+    again:
+      to_read = sizeof (pid_t) - bytes;
+
+      if (to_read == 0)
+        break;
+      
+      chunk = read (fd,
+                    ((char*)buf) + bytes,
+                    to_read);
+      if (chunk < 0 && errno == EINTR)
+        goto again;
+          
+      if (chunk < 0)
+        {
+          retval = READ_STATUS_ERROR;
+          break;
+        }
+      else if (chunk == 0)
+        {
+          retval = READ_STATUS_EOF;
+          break; /* EOF */
+        }
+      else /* chunk > 0 */
+       bytes += chunk;
+    }
+
+  return retval;
+}
+
+static void
+do_write (int fd, const void *buf, size_t count)
+{
+  size_t bytes_written;
+  int ret;
+  
+  bytes_written = 0;
+  
+ again:
+  
+  ret = write (fd, ((const char*)buf) + bytes_written, count - bytes_written);
+
+  if (ret < 0)
+    {
+      if (errno == EINTR)
+        goto again;
+      else
+        {
+          fprintf (stderr, "Failed to write data to pipe!\n");
+          exit (1); /* give up, we suck */
+        }
+    }
+  else
+    bytes_written += ret;
+  
+  if (bytes_written < count)
+    goto again;
+}
+
+static void
+write_pid (int   fd,
+           pid_t pid)
+{
+  do_write (fd, &pid, sizeof (pid));
+}
+
+static int
+do_waitpid (pid_t pid)
+{
+  int ret;
+  
+ again:
+  ret = waitpid (pid, NULL, 0);
+
+  if (ret < 0 &&
+      errno == EINTR)
+    goto again;
+
+  return ret;
+}
+
+static pid_t bus_pid_to_kill = -1;
+
+static void
+kill_bus_and_exit (void)
+{
+  verbose ("Killing message bus and exiting babysitter\n");
+  
+  /* in case these point to any NFS mounts, get rid of them immediately */
+  close (0);
+  close (1);
+  close (2);
+
+  kill (bus_pid_to_kill, SIGTERM);
+  sleep (3);
+  kill (bus_pid_to_kill, SIGKILL);
+
+  exit (0);
+}
+
+#ifdef DBUS_BUILD_X11
+static int
+x_io_error_handler (Display *xdisplay)
+{
+  verbose ("X IO error\n");
+  kill_bus_and_exit ();
+}
+#endif
+
+static int got_sighup = FALSE;
+
+static void
+signal_handler (int sig)
+{
+  switch (sig)
+    {
+    case SIGHUP:
+      got_sighup = TRUE;
+      break;
+    }
+}
+
+static void
+kill_bus_when_session_ends (void)
+{
+  int tty_fd;
+  int x_fd;
+  fd_set read_set;
+  fd_set err_set;
+  int ret;
+  struct sigaction act;
+  sigset_t empty_mask;
+#ifdef DBUS_BUILD_X11
+  Display *xdisplay;
+#endif
+  
+  /* install SIGHUP handler */
+  got_sighup = FALSE;
+  sigemptyset (&empty_mask);
+  act.sa_handler = signal_handler;
+  act.sa_mask    = empty_mask;
+  act.sa_flags   = 0;
+  sigaction (SIGHUP,  &act, 0);
+  
+#ifdef DBUS_BUILD_X11
+  xdisplay = XOpenDisplay (NULL);
+  if (xdisplay != NULL)
+    {
+      verbose ("Successfully opened X display\n");
+      x_fd = ConnectionNumber (xdisplay);
+      XSetIOErrorHandler (x_io_error_handler);
+    }
+  else
+    x_fd = -1;
+#else
+  verbose ("Compiled without X11 support\n");
+  x_fd = -1;
+#endif
+
+  if (isatty (0))
+    tty_fd = 0;
+  else
+    tty_fd = -1;
+
+  if (tty_fd >= 0)
+    verbose ("stdin isatty(), monitoring it\n");
+  else
+    verbose ("stdin was not a TTY, not monitoring it\n");  
+  
+  if (tty_fd < 0 && x_fd < 0)
+    {
+      fprintf (stderr, "No terminal on standard input and no X display; cannot attach message bus to session lifetime\n");
+      exit (1);
+    }
+  
+  while (TRUE)
+    {
+      FD_ZERO (&read_set);
+      FD_ZERO (&err_set);
+
+      if (tty_fd >= 0)
+        {
+          FD_SET (tty_fd, &read_set);
+          FD_SET (tty_fd, &err_set);
+        }
+
+      if (x_fd >= 0)
+        {
+          FD_SET (x_fd, &read_set);
+          FD_SET (x_fd, &err_set);
+        }
+      
+      ret = select (MAX (tty_fd, x_fd) + 1,
+                    &read_set, NULL, &err_set, NULL);
+
+      if (got_sighup)
+        {
+          verbose ("Got SIGHUP, exiting\n");
+          kill_bus_and_exit ();
+        }
+      
+#ifdef DBUS_BUILD_X11
+      /* Dump events on the floor, and let
+       * IO error handler run if we lose
+       * the X connection
+       */
+      if (x_fd >= 0)
+        verbose ("X fd condition reading = %d error = %d\n",
+                 FD_ISSET (x_fd, &read_set),
+                 FD_ISSET (x_fd, &err_set));
+      
+      if (xdisplay != NULL)
+        {      
+          while (XPending (xdisplay))
+            {
+              XEvent ignored;
+              XNextEvent (xdisplay, &ignored);
+            }
+        }
+#endif
+
+      if (tty_fd >= 0)
+        {
+          if (FD_ISSET (tty_fd, &read_set))
+            {
+              int bytes_read;
+              char discard[512];
+
+              verbose ("TTY ready for reading\n");
+              
+              bytes_read = read (tty_fd, discard, sizeof (discard));
+
+              verbose ("Read %d bytes from TTY errno = %d\n",
+                       bytes_read, errno);
+              
+              if (bytes_read == 0)
+                kill_bus_and_exit (); /* EOF */
+              else if (bytes_read < 0 && errno != EINTR)
+                {
+                  /* 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 ();
+                }
+            }
+          else if (FD_ISSET (tty_fd, &err_set))
+            {
+              verbose ("TTY has error condition\n");
+              
+              kill_bus_and_exit ();
+            }
+        }
+    }
+}
+
+static void
+babysit (int   exit_with_session,
+         pid_t child_pid,
+         int   read_bus_pid_fd,  /* read pid from here */
+         int   write_bus_pid_fd) /* forward pid to here */
+{
+  int ret;
+#define MAX_PID_LEN 64
+  char buf[MAX_PID_LEN];
+  long val;
+  char *end;
+  
+  /* 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,1,2 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)
+    {
+      fprintf (stderr, "Could not change to root directory: %s\n",
+               strerror (errno));
+      exit (1);
+    }      
+
+  ret = fork ();
+
+  if (ret < 0)
+    {
+      fprintf (stderr, "fork() failed in babysitter: %s\n",
+               strerror (errno));
+      exit (1);
+    }
+
+  if (ret > 0)
+    {
+      /* Parent reaps pre-fork part of bus daemon, then exits and is
+       * reaped so the babysitter isn't a zombie
+       */
+
+      verbose ("=== Babysitter's intermediate parent continues again\n");
+      
+      if (do_waitpid (child_pid) < 0)
+        {
+          /* shouldn't happen */
+          fprintf (stderr, "Failed waitpid() waiting for bus daemon's parent\n");
+          exit (1);
+        }
+
+      verbose ("Babysitter's intermediate parent exiting\n");
+      
+      exit (0);
+    }
+
+  /* Child continues */
+  verbose ("=== Babysitter process created\n");
+
+  verbose ("Reading PID from daemon\n");
+  /* Now read data */
+  switch (read_line (read_bus_pid_fd, 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_to_kill = val;
+
+  verbose ("Got PID %ld from daemon\n",
+           (long) bus_pid_to_kill);
+  
+  /* Write data to launcher */
+  write_pid (write_bus_pid_fd, bus_pid_to_kill);
+  close (write_bus_pid_fd);
+
+  if (exit_with_session)
+    {
+      /* Bus is now started and launcher has needed info;
+       * we connect to X display and tty and wait to
+       * kill bus if requested.
+       */
+      
+      kill_bus_when_session_ends ();
+    }
+
+  verbose ("Babysitter exiting\n");
+  
+  exit (0);
+}
+
+#define READ_END  0
+#define WRITE_END 1
+
 int
 main (int argc, char **argv)
 {
   const char *prev_arg;
-  dbus_bool_t exit_with_session;
+  int exit_with_session;
   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];
+  
   exit_with_session = FALSE;
   
   prev_arg = NULL;
@@ -82,8 +573,181 @@ main (int argc, char **argv)
       
       ++i;
     }
+
+  verbose ("--exit-with-session provided\n");
+
+  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-1 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-1",
+              "dbus-daemon-1",
+              "--fork",
+              "--session",
+              "--print-pid", write_pid_fd_as_string,
+              "--print-address", write_address_fd_as_string,
+              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]);
+
+      printf ("DBUS_SESSION_BUS_ADDRESS='%s'\n",
+              bus_address);
+
+      printf ("DBUS_SESSION_BUS_PID=%ld\n",
+              (long) bus_pid);
+
+      verbose ("dbus-launch exiting\n");
+      
+      exit (0);
+    } 
   
   return 0;
 }