[daemon-fix] fixed getting uid and pid when transport is not kdbus
[platform/upstream/dbus.git] / tools / dbus-launch.c
index 7e53e7e..2a2abbb 100644 (file)
@@ -1,4 +1,4 @@
-/* -*- mode: C; c-file-style: "gnu" -*- */
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 /* dbus-launch.c  dbus-launch utility
  *
  * Copyright (C) 2003, 2006 Red Hat, Inc.
  * 
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
+
+#include <config.h>
 #include "dbus-launch.h"
 #include <stdlib.h>
+#include <ctype.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <signal.h>
 extern Display *xdisplay;
 #endif
 
+/* PROCESSES
+ *
+ * If you are in a shell and run "dbus-launch myapp", here is what happens:
+ *
+ * shell [*]
+ *   \- main()               --exec--> myapp[*]
+ *      \- "intermediate parent"
+ *         \- bus-runner     --exec--> dbus-daemon --fork
+ *         \- babysitter[*]            \- final dbus-daemon[*]
+ *
+ * Processes marked [*] survive the initial flurry of activity.
+ *
+ * If you run "dbus-launch --sh-syntax" then the diagram is the same, except
+ * that main() prints variables and exits 0 instead of exec'ing myapp.
+ *
+ * PIPES
+ *
+ * dbus-daemon --print-pid     -> bus_pid_to_launcher_pipe     -> main
+ * dbus-daemon --print-address -> bus_address_to_launcher_pipe -> main
+ * main                        -> bus_pid_to_babysitter_pipe   -> babysitter
+ *
+ * The intermediate parent looks pretty useless at first glance. Its purpose
+ * is to avoid the bus-runner becoming a zombie: when the intermediate parent
+ * terminates, the bus-runner and babysitter are reparented to init, which
+ * reaps them if they have finished. We can't rely on main() to reap arbitrary
+ * children because it might exec myapp, after which it can't be relied on to
+ * reap its children. We *can* rely on main() to reap the intermediate parent,
+ * because that happens before it execs myapp.
+ *
+ * It's unclear why dbus-daemon needs to fork, but we explicitly tell it to
+ * for some reason, then wait for it. If we left it undefined, a forking
+ * dbus-daemon would get the parent process reparented to init and reaped
+ * when the intermediate parent terminated, and a non-forking dbus-daemon
+ * would get reparented to init and carry on there.
+ *
+ * myapp is exec'd by the process that initially ran main() so that it's
+ * the shell's child, so the shell knows how to do job control and stuff.
+ * This is desirable for the "dbus-launch an application" use-case, less so
+ * for the "dbus-launch a test suite in an isolated session" use-case.
+ */
+
 static char* machine_uuid = NULL;
 
 const char*
@@ -61,10 +105,55 @@ save_machine_uuid (const char *uuid_arg)
   machine_uuid = xstrdup (uuid_arg);
 }
 
+#ifdef DBUS_BUILD_X11
+#define UUID_MAXLEN 40
+/* Read the machine uuid from file if needed. Returns TRUE if machine_uuid is
+ * set after this function */
+static int
+read_machine_uuid_if_needed (void)
+{
+  FILE *f;
+  char uuid[UUID_MAXLEN];
+  size_t len;
+  int ret = FALSE;
+
+  if (machine_uuid != NULL)
+    return TRUE;
+
+  f = fopen (DBUS_MACHINE_UUID_FILE, "r");
+  if (f == NULL)
+    return FALSE;
+
+  if (fgets (uuid, UUID_MAXLEN, f) == NULL)
+    goto out;
+
+  len = strlen (uuid);
+  if (len < 32)
+    goto out;
+
+  /* rstrip the read uuid */
+  while (len > 31 && isspace(uuid[len - 1]))
+    len--;
+
+  if (len != 32)
+    goto out;
+
+  uuid[len] = '\0';
+  machine_uuid = xstrdup (uuid);
+  verbose ("UID: %s\n", machine_uuid);
+  ret = TRUE;
+
+out:
+  fclose(f);
+  return ret;
+}
+#endif /* DBUS_BUILD_X11 */
+
 void
 verbose (const char *format,
          ...)
 {
+#ifdef DBUS_ENABLE_VERBOSE_MODE
   va_list args;
   static int verbose = TRUE;
   static int verbose_initted = FALSE;
@@ -89,12 +178,16 @@ verbose (const char *format,
   va_start (args, format);
   vfprintf (stderr, format, args);
   va_end (args);
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
 }
 
 static void
 usage (int ecode)
 {
-  fprintf (stderr, "dbus-launch [--version] [--help] [--sh-syntax] [--csh-syntax] [--auto-syntax] [--exit-with-session]\n");
+  fprintf (stderr, "dbus-launch [--version] [--help] [--sh-syntax]"
+           " [--csh-syntax] [--auto-syntax] [--binary-syntax] [--close-stderr]"
+           " [--exit-with-session] [--autolaunch=MACHINEID]"
+           " [--config-file=FILENAME] [PROGRAM] [ARGS...]\n");
   exit (ecode);
 }
 
@@ -290,19 +383,24 @@ do_waitpid (pid_t pid)
 
 static pid_t bus_pid_to_kill = -1;
 
+static void
+kill_bus(void)
+{
+  verbose ("Killing message bus and exiting babysitter\n");
+  kill (bus_pid_to_kill, SIGTERM);
+  sleep (3);
+  kill (bus_pid_to_kill, SIGKILL);
+}
+
 void
 kill_bus_and_exit (int exitcode)
 {
-  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);
+  kill_bus();
 
   exit (exitcode);
 }
@@ -314,9 +412,9 @@ print_variables (const char *bus_address, pid_t bus_pid, long bus_wid,
 {
   if (binary_syntax)
     {
-      write (1, bus_address, strlen (bus_address) + 1);
-      write (1, &bus_pid, sizeof bus_pid);
-      write (1, &bus_wid, sizeof bus_wid);
+      do_write (1, bus_address, strlen (bus_address) + 1);
+      do_write (1, &bus_pid, sizeof bus_pid);
+      do_write (1, &bus_wid, sizeof bus_wid);
       return;
     }
   else if (c_shell_syntax)
@@ -324,17 +422,16 @@ print_variables (const char *bus_address, pid_t bus_pid, long bus_wid,
       printf ("setenv DBUS_SESSION_BUS_ADDRESS '%s';\n", bus_address); 
       printf ("set DBUS_SESSION_BUS_PID=%ld;\n", (long) bus_pid);
       if (bus_wid)
-       printf ("set DBUS_SESSION_BUS_WINDOWID=%ld;\n", (long) bus_wid);
+        printf ("set DBUS_SESSION_BUS_WINDOWID=%ld;\n", (long) bus_wid);
       fflush (stdout);
     }
   else if (bourne_shell_syntax)
     {
       printf ("DBUS_SESSION_BUS_ADDRESS='%s';\n", bus_address);
-      if (bourne_shell_syntax)
-       printf ("export DBUS_SESSION_BUS_ADDRESS;\n");
+      printf ("export DBUS_SESSION_BUS_ADDRESS;\n");
       printf ("DBUS_SESSION_BUS_PID=%ld;\n", (long) bus_pid);
       if (bus_wid)
-       printf ("DBUS_SESSION_BUS_WINDOWID=%ld;\n", (long) bus_wid);
+        printf ("DBUS_SESSION_BUS_WINDOWID=%ld;\n", (long) bus_wid);
       fflush (stdout);
     }
   else
@@ -354,7 +451,10 @@ signal_handler (int sig)
 {
   switch (sig)
     {
+#ifdef SIGHUP
     case SIGHUP:
+#endif
+    case SIGINT:
     case SIGTERM:
       got_sighup = TRUE;
       break;
@@ -379,6 +479,7 @@ kill_bus_when_session_ends (void)
   act.sa_flags   = 0;
   sigaction (SIGHUP,  &act, NULL);
   sigaction (SIGTERM,  &act, NULL);
+  sigaction (SIGINT,  &act, NULL);
   
 #ifdef DBUS_BUILD_X11
   x11_init();
@@ -397,11 +498,20 @@ kill_bus_when_session_ends (void)
   else
     tty_fd = -1;
 
-  if (tty_fd >= 0)
-    verbose ("stdin isatty(), monitoring it\n");
+  if (x_fd >= 0)
+    {
+      verbose ("session lifetime is defined by X, not monitoring stdin\n");
+      tty_fd = -1;
+    }
+  else if (tty_fd >= 0)
+    {
+      verbose ("stdin isatty(), monitoring it\n");
+    }
   else
-    verbose ("stdin was not a TTY, not monitoring it\n");  
-  
+    {
+      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");
@@ -410,6 +520,17 @@ kill_bus_when_session_ends (void)
   
   while (TRUE)
     {
+#ifdef DBUS_BUILD_X11
+      /* Dump events on the floor, and let
+       * IO error handler run if we lose
+       * the X connection. It's important to
+       * run this before going into select() since
+       * we might have queued outgoing messages or
+       * events.
+       */
+      x11_handle_event ();
+#endif
+      
       FD_ZERO (&read_set);
       FD_ZERO (&err_set);
 
@@ -424,7 +545,7 @@ kill_bus_when_session_ends (void)
           FD_SET (x_fd, &read_set);
           FD_SET (x_fd, &err_set);
         }
-      
+
       select (MAX (tty_fd, x_fd) + 1,
               &read_set, NULL, &err_set, NULL);
 
@@ -435,15 +556,12 @@ kill_bus_when_session_ends (void)
         }
       
 #ifdef DBUS_BUILD_X11
-      /* Dump events on the floor, and let
-       * IO error handler run if we lose
-       * the X connection
+      /* Events will be processed before we select again
        */
       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));
-      x11_handle_event ();
 #endif
 
       if (tty_fd >= 0)
@@ -521,6 +639,7 @@ babysit (int   exit_with_session,
       s = getenv ("DBUS_DEBUG_OUTPUT");
       if (s == NULL || *s == '\0')
         dup2 (dev_null_fd, 2);
+      close (dev_null_fd);
     }
   else
     {
@@ -596,6 +715,96 @@ babysit (int   exit_with_session,
   exit (0);
 }
 
+static void
+do_close_stderr (void)
+{
+  int fd;
+
+  fflush (stderr);
+
+  /* dbus-launch is a Unix-only program, so we can rely on /dev/null being there.
+   * We're including unistd.h and we're dealing with sh/csh launch sequences...
+   */
+  fd = open ("/dev/null", O_RDWR);
+  if (fd == -1)
+    {
+      fprintf (stderr, "Internal error: cannot open /dev/null: %s", strerror (errno));
+      exit (1);
+    }
+
+  close (2);
+  if (dup2 (fd, 2) == -1)
+    {
+      /* error; we can't report an error anymore... */
+      exit (1);
+    }
+  close (fd);
+}
+
+static void
+pass_info (const char *runprog, const char *bus_address, pid_t bus_pid,
+           long bus_wid, int c_shell_syntax, int bourne_shell_syntax,
+           int binary_syntax,
+           int argc, char **argv, int remaining_args)
+{
+  char *envvar = NULL;
+  char **args = NULL;
+
+  if (runprog)
+    {
+      int i;
+
+      envvar = malloc (strlen ("DBUS_SESSION_BUS_ADDRESS=") +
+          strlen (bus_address) + 1);
+      args = malloc (sizeof (char *) * ((argc-remaining_args)+2));
+
+      if (envvar == NULL || args == NULL)
+        goto oom;
+
+      args[0] = xstrdup (runprog);
+      if (!args[0])
+        goto oom;
+      for (i = 1; i <= (argc-remaining_args); i++)
+        {
+          size_t len = strlen (argv[remaining_args+i-1])+1;
+          args[i] = malloc (len);
+          if (!args[i])
+            goto oom;
+          strncpy (args[i], argv[remaining_args+i-1], len);
+        }
+      args[i] = NULL;
+
+      strcpy (envvar, "DBUS_SESSION_BUS_ADDRESS=");
+      strcat (envvar, bus_address);
+      putenv (envvar);
+
+      execvp (runprog, args);
+      fprintf (stderr, "Couldn't exec %s: %s\n", runprog, strerror (errno));
+      exit (1);
+    }
+   else
+    {
+      print_variables (bus_address, bus_pid, bus_wid, c_shell_syntax,
+          bourne_shell_syntax, binary_syntax);
+    }
+  verbose ("dbus-launch exiting\n");
+
+  fflush (stdout);
+  fflush (stderr);
+  close (1);
+  close (2);
+  exit (0);
+oom:
+  if (envvar)
+    free (envvar);
+
+  if (args)
+    free (args);
+
+  fprintf (stderr, "Out of memory!");
+  exit (1);
+}
+
 #define READ_END  0
 #define WRITE_END 1
 
@@ -613,7 +822,9 @@ main (int argc, char **argv)
   int auto_shell_syntax = FALSE;
   int autolaunch = FALSE;
   int requires_arg = FALSE;
-  int i;  
+  int close_stderr = FALSE;
+  int kdbus = FALSE;
+  int i;
   int ret;
   int bus_pid_to_launcher_pipe[2];
   int bus_pid_to_babysitter_pipe[2];
@@ -647,6 +858,10 @@ main (int argc, char **argv)
         version ();
       else if (strcmp (arg, "--exit-with-session") == 0)
         exit_with_session = TRUE;
+      else if (strcmp (arg, "--close-stderr") == 0)
+        close_stderr = TRUE;
+      else if (strcmp (arg, "--kdbus") == 0)
+        kdbus = TRUE;
       else if (strstr (arg, "--autolaunch=") == arg)
         {
           const char *s;
@@ -762,7 +977,11 @@ main (int argc, char **argv)
       fprintf (stderr, "Autolaunch requested, but X11 support not compiled in.\n"
               "Cannot continue.\n");
       exit (1);
-#else
+#else /* DBUS_BUILD_X11 */
+#ifndef DBUS_ENABLE_X11_AUTOLAUNCH
+      fprintf (stderr, "X11 autolaunch support disabled at compile time.\n");
+      exit (1);
+#else /* DBUS_ENABLE_X11_AUTOLAUNCH */
       char *address;
       pid_t pid;
       long wid;
@@ -773,16 +992,6 @@ main (int argc, char **argv)
           exit (1);
         }
 
-      /* FIXME right now autolaunch always does print_variables(), but it should really
-       * exec the child program instead if a child program was specified. For now
-       * we just exit if this conflict arises.
-       */
-      if (runprog)
-        {
-          fprintf (stderr, "Currently --autolaunch does not support running a program\n");
-          exit (1);
-        }
-      
       verbose ("Autolaunch enabled (using X11).\n");
       if (!exit_with_session)
        {
@@ -803,15 +1012,21 @@ main (int argc, char **argv)
        }
 
       if (address != NULL)
-       {          
+       {
          verbose ("dbus-daemon is already running. Returning existing parameters.\n");
-         print_variables (address, pid, wid, c_shell_syntax,
-                          bourne_shell_syntax, binary_syntax);
+         pass_info (runprog, address, pid, wid, c_shell_syntax,
+                          bourne_shell_syntax, binary_syntax, argc, argv, remaining_args);
          exit (0);
        }
-#endif
+#endif /* DBUS_ENABLE_X11_AUTOLAUNCH */
+    }
+  else if (read_machine_uuid_if_needed())
+    {
+      x11_init();
+#endif /* DBUS_BUILD_X11 */
     }
 
+
   if (pipe (bus_pid_to_launcher_pipe) < 0 ||
       pipe (bus_address_to_launcher_pipe) < 0 ||
       pipe (bus_pid_to_babysitter_pipe) < 0)
@@ -837,6 +1052,13 @@ main (int argc, char **argv)
       char write_pid_fd_as_string[MAX_FD_LEN];
       char write_address_fd_as_string[MAX_FD_LEN];
 
+#ifdef DBUS_BUILD_X11
+      xdisplay = NULL;
+#endif
+
+      if (close_stderr)
+       do_close_stderr ();
+
       verbose ("=== Babysitter's intermediate parent created\n");
 
       /* Fork once more to create babysitter */
@@ -859,7 +1081,7 @@ main (int argc, char **argv)
           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
@@ -887,7 +1109,7 @@ main (int argc, char **argv)
 
       verbose ("Calling exec()\n");
  
-#ifdef DBUS_BUILD_TESTS 
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
       /* exec from testdir */
       if (getenv("DBUS_USE_TEST_BINARY") != NULL)
         {
@@ -904,16 +1126,32 @@ main (int argc, char **argv)
                    "Failed to execute test message bus daemon %s: %s. Will try again with the system path.\n",
                    TEST_BUS_BINARY, strerror (errno));
         }
- #endif /* DBUS_BUILD_TESTS */
+ #endif /* DBUS_ENABLE_EMBEDDED_TESTS */
 
-      execl (DBUS_DAEMONDIR"/dbus-daemon",
+      if (kdbus)
+        {
+          execl (DBUS_DAEMONDIR"/dbus-daemon",
              DBUS_DAEMONDIR"/dbus-daemon",
              "--fork",
              "--print-pid", write_pid_fd_as_string,
              "--print-address", write_address_fd_as_string,
+             "--address=kdbus:",
              config_file ? "--config-file" : "--session",
              config_file, /* has to be last in this varargs list */
              NULL);
+        }
+      else
+        {
+          execl (DBUS_DAEMONDIR"/dbus-daemon",
+             DBUS_DAEMONDIR"/dbus-daemon",
+             "--fork",
+             "--print-pid", write_pid_fd_as_string,
+             "--print-address", write_address_fd_as_string,
+             config_file ? "--config-file" : "--session",
+             config_file, /* has to be last in this varargs list */
+             NULL);
+        }
+
 
       fprintf (stderr,
                "Failed to execute message bus daemon %s: %s.  Will try again without full path.\n",
@@ -925,14 +1163,29 @@ main (int argc, char **argv)
        * file and the dbus-daemon will not be in the install location during
        * build time.
        */
-      execlp ("dbus-daemon",
+      if (kdbus)
+        {
+          execlp ("dbus-daemon",
               "dbus-daemon",
               "--fork",
               "--print-pid", write_pid_fd_as_string,
               "--print-address", write_address_fd_as_string,
+              "--address=kdbus:",
               config_file ? "--config-file" : "--session",
               config_file, /* has to be last in this varargs list */
               NULL);
+        }
+      else
+        {
+          execlp ("dbus-daemon",
+              "dbus-daemon",
+              "--fork",
+              "--print-pid", write_pid_fd_as_string,
+              "--print-address", write_address_fd_as_string,
+              config_file ? "--config-file" : "--session",
+              config_file, /* has to be last in this varargs list */
+              NULL);
+        }
 
       fprintf (stderr,
                "Failed to execute message bus daemon: %s\n",
@@ -949,7 +1202,6 @@ main (int argc, char **argv)
       char *end;
       long wid = 0;
       long val;
-      int ret2;
 
       verbose ("=== Parent dbus-launch continues\n");
       
@@ -1020,38 +1272,38 @@ main (int argc, char **argv)
 
       close (bus_pid_to_launcher_pipe[READ_END]);
 
-#ifdef DBUS_BUILD_X11
-      /* FIXME the runprog == NULL is broken - we need to launch the runprog with the existing bus,
-       * instead of just doing print_variables() if there's an existing bus.
-       */
-      if (xdisplay != NULL && runprog == NULL)
+#ifdef DBUS_ENABLE_X11_AUTOLAUNCH
+      if (xdisplay != NULL)
         {
+          int ret2;
+
+          verbose("Saving x11 address\n");
           ret2 = x11_save_address (bus_address, bus_pid, &wid);
-          if (ret2 == 0)
+          /* Only get an existing dbus session when autolaunching */
+          if (autolaunch)
             {
-              /* another window got added. Return its address */
-              char *address;
-              pid_t pid;
-              long wid;
-              
-              if (x11_get_address (&address, &pid, &wid) && address != NULL)
+              if (ret2 == 0)
                 {
-                  verbose ("dbus-daemon is already running. Returning existing parameters.\n");
-                  print_variables (address, pid, wid, c_shell_syntax,
-                                   bourne_shell_syntax, binary_syntax);
-                  free (address);
-                  
+                  char *address = NULL;
+                  /* another window got added. Return its address */
                   bus_pid_to_kill = bus_pid;
-                  kill_bus_and_exit (0);
+                  if (x11_get_address (&address, &bus_pid, &wid)
+                       && address != NULL)
+                    {
+                      verbose ("dbus-daemon is already running. Returning existing parameters.\n");
+                      /* Kill the old bus */
+                      kill_bus();
+                      pass_info (runprog, address, bus_pid, wid,
+                         c_shell_syntax, bourne_shell_syntax, binary_syntax,
+                         argc, argv, remaining_args);
+                    }
+                  }
+              if (ret2 < 0)
+                {
+                  fprintf (stderr, "Error saving bus information.\n");
+                  bus_pid_to_kill = bus_pid;
+                  kill_bus_and_exit (1);
                 }
-              
-              /* if failed, fall through */
-            }
-          if (ret2 <= 0)
-            {
-              fprintf (stderr, "Error saving bus information.\n");
-              bus_pid_to_kill = bus_pid;
-              kill_bus_and_exit (1);
             }
         }
 #endif
@@ -1060,56 +1312,9 @@ main (int argc, char **argv)
       write_pid (bus_pid_to_babysitter_pipe[WRITE_END], bus_pid);
       close (bus_pid_to_babysitter_pipe[WRITE_END]);
 
-      if (runprog)
-       {
-         char *envvar;
-         char **args;
-
-         envvar = malloc (strlen ("DBUS_SESSION_BUS_ADDRESS=") + strlen (bus_address) + 1);
-         args = malloc (sizeof (char *) * ((argc-remaining_args)+2));
-
-         if (envvar == NULL || args == NULL)
-           goto oom;
-
-         args[0] = xstrdup (runprog);
-         if (!args[0])
-           goto oom;
-         for (i = 1; i <= (argc-remaining_args); i++)
-           {
-             size_t len = strlen (argv[remaining_args+i-1])+1;
-             args[i] = malloc (len);
-             if (!args[i])
-               goto oom;
-             strncpy (args[i], argv[remaining_args+i-1], len);
-           }
-         args[i] = NULL;
-
-         strcpy (envvar, "DBUS_SESSION_BUS_ADDRESS=");
-         strcat (envvar, bus_address);
-         putenv (envvar);
-
-         execvp (runprog, args);
-         fprintf (stderr, "Couldn't exec %s: %s\n", runprog, strerror (errno));
-         exit (1);
-       }
-      else
-       {
-         print_variables (bus_address, bus_pid, wid, c_shell_syntax,
-                          bourne_shell_syntax, binary_syntax);
-       }
-         
-      verbose ("dbus-launch exiting\n");
+       pass_info (runprog, bus_address, bus_pid, wid, c_shell_syntax,
+              bourne_shell_syntax, binary_syntax, argc, argv, remaining_args);
+    }
 
-      fflush (stdout);
-      fflush (stderr);
-      close (1);
-      close (2);
-      
-      exit (0);
-    } 
-  
   return 0;
- oom:
-  fprintf (stderr, "Out of memory!");
-  exit (1);
 }