From f6fa010403cb2badd88ce096ae91f664418508d1 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 30 Sep 2006 19:38:34 +0000 Subject: * configure.in: add DBUS_BINDIR as a #define to C source code. * tools/dbus-launch.c * tools/dbus-launch.h * tools/dbus-launch-x11.c: * tools/dbus-launch.1: Add the --autolaunch option to dbus-launch, which makes it scan for an existing session started with --autolaunch. With that option, it also creates an X11 window and saves the bus address and PID to it. * dbus/dbus-sysdeps.h: * dbus/dbus-sysdeps-unix.c (_dbus_get_autolaunch_address): Add a function that runs "dbus-launch --autolaunch" to retrieve the running D-Bus session address (or start one if none was running) * dbus/dbus-transport.c: Add the handler for the "autolaunch:" address protocol, which tries to get the running session from dbus-launch. * dbus/dbus-bus.c: * dbus/dbus-internals.h: Make "autolaunch:" be the default D-Bus session bus address. * dbus/dbus-connection.c: Fix horrible typo in error message. --- tools/dbus-launch-x11.c | 392 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 392 insertions(+) create mode 100644 tools/dbus-launch-x11.c (limited to 'tools/dbus-launch-x11.c') 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 -- cgit