diff options
Diffstat (limited to 'tools/dbus-launch-x11.c')
| -rw-r--r-- | tools/dbus-launch-x11.c | 392 | 
1 files changed, 392 insertions, 0 deletions
diff --git a/tools/dbus-launch-x11.c b/tools/dbus-launch-x11.c new file mode 100644 index 00000000..ac356219 --- /dev/null +++ b/tools/dbus-launch-x11.c @@ -0,0 +1,392 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-launch.h  dbus-launch utility + * + * Copyright (C) 2006 Thiago Macieira <thiago@kde.org> + * + * Licensed under the Academic Free License version 2.1 + *  + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + *  + * 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 + * + */ +#include "dbus-launch.h" + +#ifdef DBUS_BUILD_X11 +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <pwd.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +Display *xdisplay; +static Atom selection_atom; +static Atom address_atom; +static Atom pid_atom; + +static int +x_io_error_handler (Display *xdisplay) +{ +  verbose ("X IO error\n"); +  kill_bus_and_exit (0); +  return 0; +} + +static char * +get_local_hostname (void) +{ +  static const int increment = 128; +  static char *cache = NULL; +  char *buffer = NULL; +  int size = 0; + +  while (cache == NULL) +    { +      size += increment; +      buffer = realloc (buffer, size); +      if (buffer == NULL) +        return NULL;            /* out of memory */ + +      if (gethostname (buffer, size - 1) == -1 && +          errno != ENAMETOOLONG) +        return NULL; + +      buffer[size - 1] = '\0';  /* to make sure */ +      cache = buffer; +    } + +  return cache; +} + +static char * +get_session_file (void) +{ +  static const char prefix[] = "/.dbus-session-file_"; +  char *hostname; +  char *display; +  char *home; +  char *result; +  char *p; + +  display = xstrdup (getenv ("DISPLAY")); +  if (display == NULL) +    { +      verbose ("X11 integration disabled because X11 is not running\n"); +      return NULL; +    } + +  /* remove the screen part of the display name */ +  p = strrchr (display, ':'); +  if (p != NULL) +    for ( ; *p; ++p) +      if (*p == '.') +        { +          *p = '\0'; +          break; +        } + +  /* replace the : in the display with _ */ +  for (p = display; *p; ++p) +    if (*p == ':') +      *p = '_'; + +  hostname = get_local_hostname (); +  if (hostname == NULL) +    { +      /* out of memory */ +      free (display); +      return NULL; +    } + +  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) + +                   strlen (display) + 2); +  if (result == NULL) +    { +      /* out of memory */ +      free (display); +      return NULL; +    } + +  strcpy (result, home); +  strcat (result, prefix); +  strcat (result, hostname); +  strcat (result, "_"); +  strcat (result, display); +  free (display); + +  verbose ("session file: %s\n", result); +  return result; +} + +static Display * +open_x11 (void) +{ +  if (xdisplay != NULL) +    return xdisplay; + +  xdisplay = XOpenDisplay (NULL); +  if (xdisplay != NULL) +    { +      verbose ("Connected to X11 display '%s'\n", DisplayString (xdisplay)); +      XSetIOErrorHandler (x_io_error_handler); +    } +  return xdisplay; +} + +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 int init = FALSE; +  char *atom_name; +  char *hostname; +  char *user_name; +  struct passwd *user; + +  if (init) +    return TRUE; + +  user = getpwuid (getuid ()); +  if (user == NULL) +    { +      verbose ("Could not determine the user informations; 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 + +                      MAX (strlen (selection_prefix), +                           MAX (strlen (address_prefix), +                                strlen (pid_prefix)))); +  if (atom_name == NULL) +    { +      verbose ("Could not create X11 atoms; aborting X11 integration.\n"); +      free (user_name); +      return FALSE; +    } + +  /* create the selection atom */ +  strcpy (atom_name, selection_prefix); +  strcat (atom_name, user_name); +  strcat (atom_name, "_"); +  strcat (atom_name, hostname); +  selection_atom = XInternAtom (display, atom_name, FALSE); + +  /* create the address property atom */ +  strcpy (atom_name, address_prefix); +  address_atom = XInternAtom (display, atom_name, FALSE); + +  /* create the PID property atom */ +  strcpy (atom_name, pid_prefix); +  pid_atom = XInternAtom (display, atom_name, FALSE); + +  free (atom_name); +  free (user_name); +  init = TRUE; +  return TRUE; +} + +/* + * Gets the daemon address from the X11 display. + * Returns FALSE if there was an error. Returning + * TRUE does not mean the address exists. + */ +int +x11_get_address (char **paddress, pid_t *pid, long *wid) +{ +  Atom type; +  Window owner; +  int format; +  unsigned long items; +  unsigned long after; +  char *data; + +  *paddress = NULL; + +  /* locate the selection owner */ +  owner = XGetSelectionOwner (xdisplay, selection_atom); +  if (owner == None) +    return TRUE;                /* no owner */ +  if (wid != NULL) +    *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) +    return FALSE;               /* error */ + +  *paddress = xstrdup (data); +  XFree (data); + +  /* get the PID */ +  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) +        *pid = (pid_t) *(long*) data; +      XFree (data); +    } + +  return TRUE;                  /* success */ +} + +/* + * Saves the address in the X11 display. Returns 0 on success. + * If an error occurs, returns -1. If the selection already exists, + * returns 1. (i.e. another daemon is already running) + */ +static Window +set_address_in_x11(char *address, pid_t pid) +{ +  char *current_address; +  Window wid; + +  /* lock the X11 display to make sure we're doing this atomically */ +  XGrabServer (xdisplay); + +  if (!x11_get_address (¤t_address, NULL, NULL)) +    { +      /* error! */ +      XUngrabServer (xdisplay); +      return None; +    } + +  if (current_address != NULL) +    { +      /* someone saved the address in the meantime */ +      XUngrabServer (xdisplay); +      free (current_address); +      return None; +    } + +  /* Create our window */ +  wid = XCreateSimpleWindow (xdisplay, RootWindow (xdisplay, 0), -20, -20, 10, 10, +                             0, WhitePixel (xdisplay, 0), +                             BlackPixel (xdisplay, 0)); +  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)); +  XChangeProperty (xdisplay, wid, pid_atom, XA_CARDINAL, 32, PropModeReplace, +                   (unsigned char *)&pid, sizeof(pid) / 4); + +  /* Now grab the selection */ +  XSetSelectionOwner (xdisplay, selection_atom, wid, CurrentTime); + +  /* Ungrab the server to let other people use it too */ +  XUngrabServer (xdisplay); + +  XFlush (xdisplay); + +  return wid; +} + +/* + * Saves the session address in session file. Returns TRUE on + * success, FALSE if an error occurs. + */ +static int +set_address_in_file (char *address, pid_t pid, Window wid) +{ +  char *session_file; +  FILE *f; + +  session_file = get_session_file(); +  if (session_file == NULL) +    return FALSE; + +  f = fopen (session_file, "w"); +  if (f == NULL) +    return FALSE;               /* some kind of error */ +  fprintf (f, "%s\n%ld\n%ld\n", address, (long)pid, (long)wid); + +  fclose (f); +  free (session_file); + +  return TRUE; +} + +int +x11_save_address (char *address, pid_t pid, long *wid) +{ +  Window id = set_address_in_x11 (address, pid); +  if (id != None) +    { +      if (!set_address_in_file (address, pid, id)) +        return FALSE; + +      if (wid != NULL) +        *wid = (long) id; +      return TRUE; +    } +  return FALSE; +} + +int +x11_init (void) +{ +  return open_x11 () != NULL && init_x_atoms (xdisplay); +} + +void +x11_handle_event (void) +{ +  if (xdisplay != NULL) +    {       +      while (XPending (xdisplay)) +        { +          XEvent ignored; +          XNextEvent (xdisplay, &ignored); +        } +    } +}   + +#else +void dummy_dbus_launch_x11 (void) { } +#endif  | 
