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. --- ChangeLog | 27 ++++ configure.in | 5 + dbus/dbus-bus.c | 7 + dbus/dbus-connection.c | 2 +- dbus/dbus-internals.h | 2 + dbus/dbus-sysdeps-unix.c | 113 ++++++++++++++ dbus/dbus-sysdeps.h | 3 + dbus/dbus-transport.c | 107 ++++++++++++- tools/Makefile.am | 3 +- tools/dbus-launch-x11.c | 392 +++++++++++++++++++++++++++++++++++++++++++++++ tools/dbus-launch.1 | 9 +- tools/dbus-launch.c | 301 +++++++++++++++++++++--------------- tools/dbus-launch.h | 56 +++++++ 13 files changed, 900 insertions(+), 127 deletions(-) create mode 100644 tools/dbus-launch-x11.c create mode 100644 tools/dbus-launch.h diff --git a/ChangeLog b/ChangeLog index 49a32704..55a88c88 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +2006-09-30 Thiago Macieira + + * 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. + 2006-09-18 John (J5) Palmieri * tools/Makefile.am: use @EXPANDED_DATADIR@ instead of @DATADIRNAME@ diff --git a/configure.in b/configure.in index e87fdef0..0c70bb31 100644 --- a/configure.in +++ b/configure.in @@ -1052,6 +1052,11 @@ fi AC_SUBST(DBUS_DAEMONDIR) AC_DEFINE_UNQUOTED(DBUS_DAEMONDIR,"$DBUS_DAEMONDIR", [Directory for installing the DBUS daemon]) +#### Directory to install the other binaries +DBUS_BINDIR=$EXPANDED_BINDIR +AC_SUBST(DBUS_BINDIR) +AC_DEFINE_UNQUOTED(DBUS_BINDIR,"$DBUS_BINDIR", [Directory for installing the binaries]) + #### Tell tests where to find certain stuff in builddir DBUS_PWD=`pwd` diff --git a/dbus/dbus-bus.c b/dbus/dbus-bus.c index 5f5c3395..eb6b3ab3 100644 --- a/dbus/dbus-bus.c +++ b/dbus/dbus-bus.c @@ -175,6 +175,13 @@ init_connections_unlocked (void) if (!get_from_env (&bus_connection_addresses[DBUS_BUS_SESSION], "DBUS_SESSION_BUS_ADDRESS")) return FALSE; + + if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL) + bus_connection_addresses[DBUS_BUS_SESSION] = + _dbus_strdup (DBUS_SESSION_BUS_DEFAULT_ADDRESS); + if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL) + return FALSE; + _dbus_verbose (" \"%s\"\n", bus_connection_addresses[DBUS_BUS_SESSION] ? bus_connection_addresses[DBUS_BUS_SESSION] : "none set"); } diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index 2b4dd8d8..d60816c3 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -2783,7 +2783,7 @@ _dbus_connection_block_pending_call (DBusPendingCall *pending) error_msg = generate_local_error_message (client_serial, DBUS_ERROR_DISCONNECTED, - "Connection was dissconnected before a reply was recived"); + "Connection was disconnected before a reply was received"); /* on OOM error_msg is set to NULL */ complete_pending_call_and_unlock (connection, pending, error_msg); diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index a12821db..520d6dfe 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -37,6 +37,8 @@ DBUS_BEGIN_DECLS +#define DBUS_SESSION_BUS_DEFAULT_ADDRESS "autolaunch:" + void _dbus_warn (const char *format, ...) _DBUS_GNUC_PRINTF (1, 2); diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 38adebb1..c0540175 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -27,6 +27,7 @@ #include "dbus-sysdeps-unix.h" #include "dbus-threads.h" #include "dbus-protocol.h" +#include "dbus-transport.h" #include "dbus-string.h" #include #include @@ -2275,6 +2276,118 @@ _dbus_get_tmpdir(void) return tmpdir; } +/** + * Determines the address of the session bus by querying a + * platform-specific method. + * + * If successful, returns #TRUE and appends the address to @p + * address. If a failure happens, returns #FALSE and + * sets an error in @p error. + * + * @param address a DBusString where the address can be stored + * @param error a DBusError to store the error in case of failure + * @returns #TRUE on success, #FALSE if an error happened + */ +dbus_bool_t +_dbus_get_autolaunch_address (DBusString *address, DBusError *error) +{ + static char *argv[] = { DBUS_BINDIR "/dbus-launch", "--autolaunch", + "--binary-syntax", NULL }; + int address_pipe[2]; + pid_t pid; + int ret; + int status; + int orig_len = _dbus_string_get_length (address); + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + +#define READ_END 0 +#define WRITE_END 1 + if (pipe (address_pipe) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to create a pipe: %s", + _dbus_strerror (errno)); + _dbus_verbose ("Failed to create a pipe to call dbus-launch: %s\n", + _dbus_strerror (errno)); + return FALSE; + } + + pid = fork (); + if (pid < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to fork(): %s", + _dbus_strerror (errno)); + _dbus_verbose ("Failed to fork() to call dbus-launch: %s\n", + _dbus_strerror (errno)); + return FALSE; + } + + if (pid == 0) + { + /* child process */ + int fd = open ("/dev/null", O_RDWR); + if (fd == -1) + /* huh?! can't open /dev/null? */ + _exit (1); + + /* set-up stdXXX */ + close (address_pipe[READ_END]); + close (0); /* close stdin */ + close (1); /* close stdout */ + close (2); /* close stderr */ + + if (dup2 (fd, 0) == -1) + _exit (1); + if (dup2 (address_pipe[WRITE_END], 1) == -1) + _exit (1); + if (dup2 (fd, 2) == -1) + _exit (1); + + close (fd); + close (address_pipe[WRITE_END]); + + execv (argv[0], argv); + + /* failed, try searching PATH */ + argv[0] = "dbus-launch"; + execvp ("dbus-launch", argv); + + /* still nothing, we failed */ + _exit (1); + } + + /* parent process */ + close (address_pipe[WRITE_END]); + ret = 0; + do + { + ret = _dbus_read (address_pipe[READ_END], address, 1024); + } + while (ret > 0); + + /* reap the child process to avoid it lingering as zombie */ + do + { + ret = waitpid (pid, &status, 0); + } + while (ret == -1 && errno == EINTR); + + /* We succeeded if the process exited with status 0 and + anything was read */ + if (!WIFEXITED (status) || WEXITSTATUS (status) != 0 || + _dbus_string_get_length (address) == orig_len) + { + /* The process ended with error */ + _dbus_string_set_length (address, orig_len); + dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED, + "Failed to execute dbus-launch to autolaunch D-Bus session"); + return FALSE; + } + return TRUE; +} + /** @} end of sysdeps */ /* tests in dbus-sysdeps-util.c */ diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index c1ed533a..3541a721 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -376,6 +376,9 @@ dbus_bool_t _dbus_user_at_console (const char *username, dbus_bool_t _dbus_parse_uid (const DBusString *uid_str, dbus_uid_t *uid); +dbus_bool_t _dbus_get_autolaunch_address (DBusString *address, + DBusError *error); + DBUS_END_DECLS #endif /* DBUS_SYSDEPS_H */ diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index 89d79ffe..f43dd3d2 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -201,13 +201,118 @@ _dbus_transport_finalize_base (DBusTransport *transport) dbus_free (transport->expected_guid); } + +/** + * Verifies if a given D-Bus address is a valid address + * by attempting to connect to it. If it is, returns the + * opened DBusTransport object. If it isn't, returns #NULL + * and sets @p error. + * + * @param error address where an error can be returned. + * @returns a new transport, or #NULL on failure. + */ +static DBusTransport* +check_address (const char *address, DBusError *error) +{ + DBusAddressEntry **entries; + DBusTransport *transport = NULL; + int len, i; + + _dbus_assert (address != NULL); + _dbus_assert (*address != '\0'); + + if (!dbus_parse_address (address, &entries, &len, error)) + return FALSE; /* not a valid address */ + + for (i = 0; i < len; i++) + { + transport = _dbus_transport_open (entries[i], error); + if (transport != NULL) + break; + } + + dbus_address_entries_free (entries); + return transport; +} + +/** + * Creates a new transport for the "autostart" method. + * This creates a client-side of a transport. + * + * @param error address where an error can be returned. + * @returns a new transport, or #NULL on failure. + */ +static DBusTransport* +_dbus_transport_new_for_autolaunch (DBusError *error) +{ + DBusString address; + DBusTransport *result = NULL; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (!_dbus_string_init (&address)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + if (!_dbus_get_autolaunch_address (&address, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto out; + } + + result = check_address (_dbus_string_get_const_data (&address), error); + if (result == NULL) + _DBUS_ASSERT_ERROR_IS_SET (error); + else + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + out: + _dbus_string_free (&address); + return result; +} + +static DBusTransportOpenResult +_dbus_transport_open_autolaunch (DBusAddressEntry *entry, + DBusTransport **transport_p, + DBusError *error) +{ + const char *method; + + method = dbus_address_entry_get_method (entry); + _dbus_assert (method != NULL); + + if (strcmp (method, "autolaunch") == 0) + { + *transport_p = _dbus_transport_new_for_autolaunch (error); + + if (*transport_p == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_OK; + } + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return DBUS_TRANSPORT_OPEN_NOT_HANDLED; + } +} + static const struct { DBusTransportOpenResult (* func) (DBusAddressEntry *entry, DBusTransport **transport_p, DBusError *error); } open_funcs[] = { { _dbus_transport_open_socket }, - { _dbus_transport_open_platform_specific } + { _dbus_transport_open_platform_specific }, + { _dbus_transport_open_autolaunch } #ifdef DBUS_BUILD_TESTS , { _dbus_transport_open_debug_pipe } #endif diff --git a/tools/Makefile.am b/tools/Makefile.am index eb19ddb7..05c3618e 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -13,7 +13,8 @@ dbus_monitor_SOURCES= \ dbus-print-message.h dbus_launch_SOURCES= \ - dbus-launch.c + dbus-launch.c \ + dbus-launch-x11.c dbus_cleanup_sockets_SOURCES= \ dbus-cleanup-sockets.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 diff --git a/tools/dbus-launch.1 b/tools/dbus-launch.1 index a3180f69..3c505e7b 100644 --- a/tools/dbus-launch.1 +++ b/tools/dbus-launch.1 @@ -7,7 +7,7 @@ dbus-launch \- Utility to start a message bus from a shell script .SH SYNOPSIS .PP -.B dbus-launch [\-\-version] [\-\-sh-syntax] [\-\-csh-syntax] [\-\-auto-syntax] [\-\-exit-with-session] [\-\-config-file=FILENAME] [PROGRAM] [ARGS...] +.B dbus-launch [\-\-version] [\-\-sh-syntax] [\-\-csh-syntax] [\-\-auto-syntax] [\-\-exit-with-session] [\-\-autolaunch] [\-\-config-file=FILENAME] [PROGRAM] [ARGS...] .SH DESCRIPTION @@ -89,6 +89,13 @@ created that watches stdin for HUP and tries to connect to the X server. If this process gets a HUP on stdin or loses its X connection, it kills the message bus daemon. +.TP +.I "--autolaunch" +This option implies that \fIdbus-launch\fP should scan for a +previously-started session and reuse the values found there. If no +session is found, it will start a new session. The +\-\-exit-with-session option is implied if \-\-autolaunch is given. + .TP .I "--sh-syntax" Emit Bourne-shell compatible code to set up environment variables. diff --git a/tools/dbus-launch.c b/tools/dbus-launch.c index 18a6d156..fb993e4e 100644 --- a/tools/dbus-launch.c +++ b/tools/dbus-launch.c @@ -20,7 +20,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include +#include "dbus-launch.h" #include #include #include @@ -32,22 +32,14 @@ #include #include #include +#include + #ifdef DBUS_BUILD_X11 #include +extern Display *xdisplay; #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 +void verbose (const char *format, ...) { @@ -95,7 +87,7 @@ version (void) exit (0); } -static char * +char * xstrdup (const char *str) { int len; @@ -276,8 +268,8 @@ do_waitpid (pid_t pid) static pid_t bus_pid_to_kill = -1; -static void -kill_bus_and_exit (void) +void +kill_bus_and_exit (int exitcode) { verbose ("Killing message bus and exiting babysitter\n"); @@ -290,18 +282,38 @@ kill_bus_and_exit (void) sleep (3); kill (bus_pid_to_kill, SIGKILL); - exit (0); + exit (exitcode); } -#ifdef DBUS_BUILD_X11 -static int -x_io_error_handler (Display *xdisplay) +static void +print_variables (const char *bus_address, pid_t bus_pid, long bus_wid, + int c_shell_syntax, int bourne_shell_syntax, + int binary_syntax) { - verbose ("X IO error\n"); - kill_bus_and_exit (); - return 0; + 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); + return; + } + else if (c_shell_syntax) + { + 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); + } + else + { + printf ("DBUS_SESSION_BUS_ADDRESS='%s';\n", bus_address); + if (bourne_shell_syntax) + 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); + } } -#endif static int got_sighup = FALSE; @@ -326,9 +338,6 @@ kill_bus_when_session_ends (void) fd_set err_set; struct sigaction act; sigset_t empty_mask; -#ifdef DBUS_BUILD_X11 - Display *xdisplay; -#endif /* install SIGHUP handler */ got_sighup = FALSE; @@ -340,17 +349,14 @@ kill_bus_when_session_ends (void) sigaction (SIGTERM, &act, NULL); #ifdef DBUS_BUILD_X11 - xdisplay = XOpenDisplay (NULL); + x11_init(); 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 @@ -393,7 +399,7 @@ kill_bus_when_session_ends (void) if (got_sighup) { verbose ("Got SIGHUP, exiting\n"); - kill_bus_and_exit (); + kill_bus_and_exit (0); } #ifdef DBUS_BUILD_X11 @@ -405,15 +411,7 @@ kill_bus_when_session_ends (void) 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); - } - } + x11_handle_event (); #endif if (tty_fd >= 0) @@ -431,7 +429,7 @@ kill_bus_when_session_ends (void) bytes_read, errno); if (bytes_read == 0) - kill_bus_and_exit (); /* EOF */ + kill_bus_and_exit (0); /* EOF */ else if (bytes_read < 0 && errno != EINTR) { /* This shouldn't happen I don't think; to avoid @@ -439,14 +437,14 @@ kill_bus_when_session_ends (void) */ fprintf (stderr, "dbus-launch: error reading from stdin: %s\n", strerror (errno)); - kill_bus_and_exit (); + kill_bus_and_exit (0); } } else if (FD_ISSET (tty_fd, &err_set)) { verbose ("TTY has error condition\n"); - kill_bus_and_exit (); + kill_bus_and_exit (0); } } } @@ -455,19 +453,14 @@ kill_bus_when_session_ends (void) 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 read_bus_pid_fd) /* read pid from here */ { int ret; -#define MAX_PID_LEN 64 - char buf[MAX_PID_LEN]; - long val; - char *end; int dev_null_fd; const char *s; - verbose ("babysitting, exit_with_session = %d, child_pid = %ld, read_bus_pid_fd = %d, write_bus_pid_fd = %d\n", - exit_with_session, (long) child_pid, read_bus_pid_fd, write_bus_pid_fd); + verbose ("babysitting, exit_with_session = %d, child_pid = %ld, read_bus_pid_fd = %d\n", + exit_with_session, (long) child_pid, read_bus_pid_fd); /* We chdir ("/") since we are persistent and daemon-like, and fork * again so dbus-launch can reap the parent. However, we don't @@ -536,41 +529,26 @@ babysit (int exit_with_session, /* 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)) + verbose ("Reading PID from bus\n"); + + switch (read_pid (read_bus_pid_fd, &bus_pid_to_kill)) { case READ_STATUS_OK: break; case READ_STATUS_EOF: - fprintf (stderr, "EOF reading PID from bus daemon\n"); + fprintf (stderr, "EOF in dbus-launch 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)); + fprintf (stderr, "Error in dbus-launch 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; @@ -597,9 +575,11 @@ main (int argc, char **argv) const char *runprog = NULL; int remaining_args = 0; int exit_with_session; + int binary_syntax = FALSE; int c_shell_syntax = FALSE; int bourne_shell_syntax = FALSE; int auto_shell_syntax = FALSE; + int autolaunch = FALSE; int i; int ret; int bus_pid_to_launcher_pipe[2]; @@ -628,10 +608,14 @@ main (int argc, char **argv) else if (strcmp (arg, "-s") == 0 || strcmp (arg, "--sh-syntax") == 0) bourne_shell_syntax = TRUE; + else if (strcmp (arg, "--binary-syntax") == 0) + binary_syntax = TRUE; else if (strcmp (arg, "--version") == 0) version (); else if (strcmp (arg, "--exit-with-session") == 0) exit_with_session = TRUE; + else if (strcmp (arg, "--autolaunch") == 0) + autolaunch = TRUE; else if (strstr (arg, "--config-file=") == arg) { const char *file; @@ -672,9 +656,6 @@ main (int argc, char **argv) ++i; } - if (exit_with_session) - verbose ("--exit-with-session enabled\n"); - if (auto_shell_syntax) { if ((shname = getenv ("SHELL")) != NULL) @@ -688,8 +669,52 @@ main (int argc, char **argv) bourne_shell_syntax = TRUE; } + if (exit_with_session) + verbose ("--exit-with-session enabled\n"); + + if (autolaunch) + { +#ifndef DBUS_BUILD_X11 + fprintf (stderr, "Autolaunch requested, but X11 support not compiled in.\n" + "Cannot continue.\n"); + exit (1); +#else + char *address; + pid_t pid; + long wid; + + verbose ("Autolaunch enabled (using X11).\n"); + if (!exit_with_session) + { + verbose ("--exit-with-session automatically enabled\n"); + exit_with_session = TRUE; + } + + if (!x11_init ()) + { + fprintf (stderr, "Autolaunch error: X11 initialization failed.\n"); + exit (1); + } + + if (!x11_get_address (&address, &pid, &wid)) + { + fprintf (stderr, "Autolaunch error: X11 communication error.\n"); + exit (1); + } + + 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); + exit (0); + } +#endif + } + if (pipe (bus_pid_to_launcher_pipe) < 0 || - pipe (bus_address_to_launcher_pipe) < 0) + pipe (bus_address_to_launcher_pipe) < 0 || + pipe (bus_pid_to_babysitter_pipe) < 0) { fprintf (stderr, "Failed to create pipe: %s\n", @@ -697,9 +722,6 @@ main (int argc, char **argv) exit (1); } - bus_pid_to_babysitter_pipe[READ_END] = -1; - bus_pid_to_babysitter_pipe[WRITE_END] = -1; - ret = fork (); if (ret < 0) { @@ -716,17 +738,9 @@ main (int argc, char **argv) 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) { @@ -741,6 +755,7 @@ main (int argc, char **argv) verbose ("=== Babysitter's intermediate parent continues\n"); close (bus_pid_to_launcher_pipe[READ_END]); + close (bus_pid_to_launcher_pipe[WRITE_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]); @@ -750,8 +765,7 @@ main (int argc, char **argv) * daemon */ babysit (exit_with_session, ret, - bus_pid_to_babysitter_pipe[READ_END], - bus_pid_to_launcher_pipe[WRITE_END]); + bus_pid_to_babysitter_pipe[READ_END]); exit (0); } @@ -763,10 +777,10 @@ main (int argc, char **argv) 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]); + close (bus_pid_to_babysitter_pipe[WRITE_END]); sprintf (write_pid_fd_as_string, - "%d", bus_pid_to_babysitter_pipe[WRITE_END]); + "%d", bus_pid_to_launcher_pipe[WRITE_END]); sprintf (write_address_fd_as_string, "%d", bus_address_to_launcher_pipe[WRITE_END]); @@ -809,14 +823,20 @@ main (int argc, char **argv) else { /* Parent */ -#define MAX_ADDR_LEN 512 +#define MAX_PID_LEN 64 pid_t bus_pid; char bus_address[MAX_ADDR_LEN]; + char buf[MAX_PID_LEN]; + char *end; + long wid = 0; + long val; + int ret2; verbose ("=== Parent dbus-launch continues\n"); close (bus_pid_to_launcher_pipe[WRITE_END]); close (bus_address_to_launcher_pipe[WRITE_END]); + close (bus_pid_to_babysitter_pipe[READ_END]); verbose ("Waiting for babysitter's intermediate parent\n"); @@ -851,25 +871,70 @@ main (int argc, char **argv) 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; - } + verbose ("Reading PID from daemon\n"); + /* Now read data */ + switch (read_line (bus_pid_to_launcher_pipe[READ_END], 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 = val; close (bus_pid_to_launcher_pipe[READ_END]); - + +#ifdef DBUS_BUILD_X11 + ret2 = x11_save_address (bus_address, bus_pid, &wid); + if (ret2 == 0) + { + /* another window got added. Return its address */ + char *address; + pid_t pid; + long wid; + + if (x11_get_address (&address, &pid, &wid) && 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); + free (address); + + bus_pid_to_kill = bus_pid; + kill_bus_and_exit (0); + } + + /* 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 + + /* Forward the pid to the babysitter */ + write_pid (bus_pid_to_babysitter_pipe[WRITE_END], bus_pid); + close (bus_pid_to_babysitter_pipe[WRITE_END]); + if (runprog) { char *envvar; @@ -904,18 +969,8 @@ main (int argc, char **argv) } else { - if (c_shell_syntax) - printf ("setenv DBUS_SESSION_BUS_ADDRESS '%s';\n", bus_address); - else - { - printf ("DBUS_SESSION_BUS_ADDRESS='%s';\n", bus_address); - if (bourne_shell_syntax) - printf ("export DBUS_SESSION_BUS_ADDRESS;\n"); - } - if (c_shell_syntax) - printf ("set DBUS_SESSION_BUS_PID=%ld;\n", (long) bus_pid); - else - printf ("DBUS_SESSION_BUS_PID=%ld;\n", (long) bus_pid); + print_variables (bus_address, bus_pid, wid, c_shell_syntax, + bourne_shell_syntax, binary_syntax); } verbose ("dbus-launch exiting\n"); diff --git a/tools/dbus-launch.h b/tools/dbus-launch.h new file mode 100644 index 00000000..1b22f3df --- /dev/null +++ b/tools/dbus-launch.h @@ -0,0 +1,56 @@ +/* -*- 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 + * + */ + +#ifndef DBUS_LAUNCH_H +#define DBUS_LAUNCH_H + +#include +#include + +#ifndef TRUE +#define TRUE (1) +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +#undef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#define MAX_ADDR_LEN 512 + +/* defined in dbus-launch.c */ +void verbose (const char *format, ...); +char *xstrdup (const char *str); +void kill_bus_and_exit (int exitcode); + +#ifdef DBUS_BUILD_X11 +/* defined in dbus-launch-x11.c */ +int x11_init (void); +int x11_get_address (char **paddress, pid_t *pid, long *wid); +int x11_save_address (char *address, pid_t pid, long *wid); +void x11_handle_event (void); +#endif + +#endif -- cgit