Merge "[lib-fix] Fixed return value of kdbus_write_msg Change-Id: Ia3a45a6a79b4078929...
[platform/upstream/dbus.git] / tools / dbus-launch-x11.c
index 67aef04..c7e3330 100644 (file)
@@ -1,4 +1,4 @@
-/* -*- mode: C; c-file-style: "gnu" -*- */
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 /* dbus-launch.h  dbus-launch utility
  *
  * Copyright (C) 2006 Thiago Macieira <thiago@kde.org>
  * 
  * 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"
 
 #ifdef DBUS_BUILD_X11
 #include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -46,42 +50,60 @@ x_io_error_handler (Display *xdisplay)
   return 0;
 }
 
-static char *
-get_local_hostname (void)
+static void
+remove_prefix (char *s,
+               char *prefix)
 {
-  static const int increment = 128;
-  static char *cache = NULL;
-  char *buffer = NULL;
-  int size = 0;
+  int plen;
 
-  while (cache == NULL)
+  plen = strlen (prefix);
+
+  if (strncmp (s, prefix, plen) == 0)
     {
-      size += increment;
-      buffer = realloc (buffer, size);
-      if (buffer == NULL)
-        return NULL;            /* out of memory */
+      memmove (s, s + plen, strlen (s) - plen + 1);
+    }
+}
 
-      if (gethostname (buffer, size - 1) == -1 &&
-          errno != ENAMETOOLONG)
-        return NULL;
+static const char*
+get_homedir (void)
+{
+  const char *home;
+  
+  home = getenv ("HOME");
+  if (home == NULL)
+    {
+      /* try from the user database */
+      struct passwd *user = getpwuid (getuid());
+      if (user != NULL)
+        home = user->pw_dir;
+    }
 
-      buffer[size - 1] = '\0';  /* to make sure */
-      cache = buffer;
+  if (home == NULL)
+    {
+      fprintf (stderr, "Can't get user home directory\n");
+      exit (1);
     }
 
-  return cache;
+  return home;
 }
 
+#define DBUS_DIR ".dbus"
+#define DBUS_SESSION_BUS_DIR "session-bus"
+
 static char *
 get_session_file (void)
 {
-  static const char prefix[] = "/.dbus-session-file_";
-  char *hostname;
+  static const char prefix[] = "/" DBUS_DIR "/" DBUS_SESSION_BUS_DIR "/";
+  const char *machine;
+  const char *home;
   char *display;
-  char *home;
   char *result;
   char *p;
 
+  machine = get_machine_uuid ();
+  if (machine == NULL)
+    return NULL;
+
   display = xstrdup (getenv ("DISPLAY"));
   if (display == NULL)
     {
@@ -92,43 +114,44 @@ get_session_file (void)
   /* remove the screen part of the display name */
   p = strrchr (display, ':');
   if (p != NULL)
-    for ( ; *p; ++p)
-      if (*p == '.')
+    {
+      for ( ; *p; ++p)
         {
-          *p = '\0';
-          break;
+          if (*p == '.')
+            {
+              *p = '\0';
+              break;
+            }
         }
+    }
 
-  /* replace the : in the display with _ */
+  /* Note that we leave the hostname in the display most of the
+   * time. The idea is that we want to be per-(machine,display,user)
+   * triplet to be extra-sure we get a bus we can connect to. Ideally
+   * we'd recognize when the hostname matches the machine we're on in
+   * all cases; we do try to drop localhost and localhost.localdomain
+   * as a special common case so that alternate spellings of DISPLAY
+   * don't result in extra bus instances.
+   *
+   * We also kill the ":" if there's nothing in front of it. This
+   * avoids an ugly double underscore in the filename.
+   */
+  remove_prefix (display, "localhost.localdomain:");
+  remove_prefix (display, "localhost:");
+  remove_prefix (display, ":");
+
+  /* replace the : in the display with _ if the : is still there.
+   * use _ instead of - since it can't be in hostnames.
+   */
   for (p = display; *p; ++p)
-    if (*p == ':')
-      *p = '_';
-
-  hostname = get_local_hostname ();
-  if (hostname == NULL)
     {
-      /* out of memory */
-      free (display);
-      return NULL;
+      if (*p == ':')
+        *p = '_';
     }
-
-  home = getenv ("HOME");
-  if (home == NULL)
-    {
-      /* try from the user database */
-      struct passwd *user = getpwuid (getuid());
-      if (user == NULL)
-        {
-          verbose ("X11 integration disabled because the home directory"
-                   " could not be determined\n");
-          free (display);
-          return NULL;
-        }
-
-      home = user->pw_dir;
-    }
-
-  result = malloc (strlen (home) + strlen (prefix) + strlen (hostname) +
+  
+  home = get_homedir ();
+  
+  result = malloc (strlen (home) + strlen (prefix) + strlen (machine) +
                    strlen (display) + 2);
   if (result == NULL)
     {
@@ -139,8 +162,8 @@ get_session_file (void)
 
   strcpy (result, home);
   strcat (result, prefix);
-  strcat (result, hostname);
-  strcat (result, "_");
+  strcat (result, machine);
+  strcat (result, "-");
   strcat (result, display);
   free (display);
 
@@ -148,6 +171,46 @@ get_session_file (void)
   return result;
 }
 
+static void
+ensure_session_directory (void)
+{
+  const char *home;
+  char *dir;
+  
+  home = get_homedir ();
+
+  /* be sure we have space for / and nul */
+  dir = malloc (strlen (home) + strlen (DBUS_DIR) + strlen (DBUS_SESSION_BUS_DIR) + 3);
+  if (dir == NULL)
+    {
+      fprintf (stderr, "no memory\n");
+      exit (1);
+    }
+  
+  strcpy (dir, home);
+  strcat (dir, "/");
+  strcat (dir, DBUS_DIR);
+
+  if (mkdir (dir, 0700) < 0)
+    {
+      /* only print a warning here, writing the session file itself will fail later */
+      if (errno != EEXIST)
+        fprintf (stderr, "Unable to create %s\n", dir);
+    }
+
+  strcat (dir, "/");
+  strcat (dir, DBUS_SESSION_BUS_DIR);
+
+  if (mkdir (dir, 0700) < 0)
+    {
+      /* only print a warning here, writing the session file itself will fail later */
+      if (errno != EEXIST)
+        fprintf (stderr, "Unable to create %s\n", dir);
+    }
+  
+  free (dir);
+}
+
 static Display *
 open_x11 (void)
 {
@@ -166,35 +229,31 @@ open_x11 (void)
 static int
 init_x_atoms (Display *display)
 {
-  static const char selection_prefix[] = "DBUS_SESSION_SELECTION_";
-  static const char address_prefix[] = "DBUS_SESSION_ADDRESS";
-  static const char pid_prefix[] = "DBUS_SESSION_PID";
+  static const char selection_prefix[] = "_DBUS_SESSION_BUS_SELECTION_";
+  static const char address_prefix[] = "_DBUS_SESSION_BUS_ADDRESS";
+  static const char pid_prefix[] = "_DBUS_SESSION_BUS_PID";
   static int init = FALSE;
   char *atom_name;
-  char *hostname;
+  const char *machine;
   char *user_name;
   struct passwd *user;
 
   if (init)
     return TRUE;
 
+  machine = get_machine_uuid ();
+  if (machine == NULL)
+    return FALSE;
+
   user = getpwuid (getuid ());
   if (user == NULL)
     {
-      verbose ("Could not determine the user informations; aborting X11 integration.\n");
+      verbose ("Could not determine user information; aborting X11 integration.\n");
       return FALSE;
     }
   user_name = xstrdup(user->pw_name);
 
-  hostname = get_local_hostname ();
-  if (hostname == NULL)
-    {
-      verbose ("Could not create X11 atoms; aborting X11 integration.\n");
-      free (user_name);
-      return FALSE;
-    }
-
-  atom_name = malloc (strlen (hostname) + strlen (user_name) + 2 +
+  atom_name = malloc (strlen (machine) + strlen (user_name) + 2 +
                       MAX (strlen (selection_prefix),
                            MAX (strlen (address_prefix),
                                 strlen (pid_prefix))));
@@ -209,7 +268,7 @@ init_x_atoms (Display *display)
   strcpy (atom_name, selection_prefix);
   strcat (atom_name, user_name);
   strcat (atom_name, "_");
-  strcat (atom_name, hostname);
+  strcat (atom_name, machine);
   selection_atom = XInternAtom (display, atom_name, FALSE);
 
   /* create the address property atom */
@@ -234,6 +293,7 @@ init_x_atoms (Display *display)
 int
 x11_get_address (char **paddress, pid_t *pid, long *wid)
 {
+  int result;
   Atom type;
   Window owner;
   int format;
@@ -251,10 +311,10 @@ x11_get_address (char **paddress, pid_t *pid, long *wid)
     *wid = (long) owner;
 
   /* get the bus address */
-  XGetWindowProperty (xdisplay, owner, address_atom, 0, 1024, False,
-                      XA_STRING, &type, &format, &items, &after,
-                      (unsigned char **) &data);
-  if (type == None || after != 0 || data == NULL || format != 8)
+  result = XGetWindowProperty (xdisplay, owner, address_atom, 0, 1024, False,
+                              XA_STRING, &type, &format, &items, &after,
+                              (unsigned char **) &data);
+  if (result != Success || type == None || after != 0 || data == NULL || format != 8)
     return FALSE;               /* error */
 
   *paddress = xstrdup (data);
@@ -264,10 +324,10 @@ x11_get_address (char **paddress, pid_t *pid, long *wid)
   if (pid != NULL)
     {
       *pid = 0;
-      XGetWindowProperty (xdisplay, owner, pid_atom, 0, sizeof pid, False,
-                          XA_CARDINAL, &type, &format, &items, &after,
-                          (unsigned char **) &data);
-      if (type != None && after == 0 && data != NULL && format == 32)
+      result = XGetWindowProperty (xdisplay, owner, pid_atom, 0, sizeof pid, False,
+                                   XA_CARDINAL, &type, &format, &items, &after,
+                                   (unsigned char **) &data);
+      if (result == Success && type != None && after == 0 && data != NULL && format == 32)
         *pid = (pid_t) *(long*) data;
       XFree (data);
     }
@@ -284,44 +344,46 @@ static Window
 set_address_in_x11(char *address, pid_t pid)
 {
   char *current_address;
-  Window wid;
-
+  Window wid = None;
+  unsigned long pid32; /* Xlib property functions want _long_ not 32-bit for format "32" */
+  
   /* lock the X11 display to make sure we're doing this atomically */
   XGrabServer (xdisplay);
 
   if (!x11_get_address (&current_address, NULL, NULL))
     {
       /* error! */
-      XUngrabServer (xdisplay);
-      return None;
+      goto out;
     }
 
   if (current_address != NULL)
     {
       /* someone saved the address in the meantime */
-      XUngrabServer (xdisplay);
       free (current_address);
-      return None;
+      goto out;
     }
 
   /* Create our window */
-  wid = XCreateSimpleWindow (xdisplay, RootWindow (xdisplay, 0), -20, -20, 10, 10,
-                             0, WhitePixel (xdisplay, 0),
-                             BlackPixel (xdisplay, 0));
+  wid = XCreateWindow (xdisplay, RootWindow (xdisplay, 0), -20, -20, 10, 10,
+                       0, CopyFromParent, InputOnly, CopyFromParent,
+                       0, NULL);
   verbose ("Created window %d\n", wid);
 
   /* Save the property in the window */
   XChangeProperty (xdisplay, wid, address_atom, XA_STRING, 8, PropModeReplace,
                    (unsigned char *)address, strlen (address));
+  pid32 = pid;
   XChangeProperty (xdisplay, wid, pid_atom, XA_CARDINAL, 32, PropModeReplace,
-                   (unsigned char *)&pid, sizeof(pid) / 4);
+                   (unsigned char *)&pid32, 1);
 
   /* Now grab the selection */
   XSetSelectionOwner (xdisplay, selection_atom, wid, CurrentTime);
 
+ out:
   /* Ungrab the server to let other people use it too */
   XUngrabServer (xdisplay);
 
+  /* And make sure that the ungrab gets sent to X11 */
   XFlush (xdisplay);
 
   return wid;
@@ -337,17 +399,29 @@ set_address_in_file (char *address, pid_t pid, Window wid)
   char *session_file;
   FILE *f;
 
+  ensure_session_directory ();
   session_file = get_session_file();
   if (session_file == NULL)
     return FALSE;
 
   f = fopen (session_file, "w");
+  free (session_file);
   if (f == NULL)
     return FALSE;               /* some kind of error */
-  fprintf (f, "%s\n%ld\n%ld\n", address, (long)pid, (long)wid);
+  fprintf (f,
+           "# This file allows processes on the machine with id %s using \n"
+           "# display %s to find the D-Bus session bus with the below address.\n"
+           "# If the DBUS_SESSION_BUS_ADDRESS environment variable is set, it will\n"
+           "# be used rather than this file.\n"
+           "# See \"man dbus-launch\" for more details.\n"
+           "DBUS_SESSION_BUS_ADDRESS=%s\n"
+           "DBUS_SESSION_BUS_PID=%ld\n"
+           "DBUS_SESSION_BUS_WINDOWID=%ld\n",
+           get_machine_uuid (),
+           getenv ("DISPLAY"),
+           address, (long)pid, (long)wid);
 
   fclose (f);
-  free (session_file);
 
   return TRUE;
 }
@@ -388,5 +462,7 @@ x11_handle_event (void)
 }  
 
 #else
+void dummy_dbus_launch_x11 (void);
+
 void dummy_dbus_launch_x11 (void) { }
 #endif