Blob Blame History Raw
From fd6e9787723c954bb4113f862d5ecd3a77ed633c Mon Sep 17 00:00:00 2001
From: Tom Gundersen <teg@jklm.no>
Date: Thu, 1 Mar 2018 12:00:09 +0100
Subject: [PATCH] bus-launch: add dbus-broker support

Both dbus-daemon and dbus-broker are now optional at compile-time, though
at least one must be configured. A new configuration option is introduce in
order to select the default implementation attempted at runtime. The other
implementation will function as a fall-back (in case support for both are
compiled in). If no default is selected, dbus-daemon remains the default as
before.

Unlike dbus-daemon, dbus-broker requires at-spi-bus-launch to create the
listening socket and pass it in, rather than having the bus do that and send
back the address. For now we follow what dbus-daemon does, and create a socket
in the abstract namespace, though it might be more suitable to create a socket
in $XDG_RUNTIME_DIR.

The only difference users should observe is that daemons are no longer spawned
by the bus implementation, but spawned and managed by the systemd user instance,
though this should not lead to a difference in behavior. In particular this
applies to `org.a11y.atspi.Registry`.

For non-linux and non-systemd systems, dbus-daemon should continue to be used.

[v2:
   - drop the --verbose switch, which is no longer supported
   - make dbus-daemon optional too
   - allow the default implementation to be selected]
Signed-off-by: Tom Gundersen <teg@jklm.no>
---
 bus/at-spi-bus-launcher.c | 196 ++++++++++++++++++++++++++++++--------
 bus/meson.build           |  32 +++++--
 meson_options.txt         |   9 ++
 3 files changed, 191 insertions(+), 46 deletions(-)

diff --git a/bus/at-spi-bus-launcher.c b/bus/at-spi-bus-launcher.c
index eb2b8e33011c48e45774f07defbf46149a643bb9..354c04eec7ba1a1ee034a245ee25c0139c48a1e5 100644
--- a/bus/at-spi-bus-launcher.c
+++ b/bus/at-spi-bus-launcher.c
@@ -1,10 +1,10 @@
 /* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
  * 
  * at-spi-bus-launcher: Manage the a11y bus as a child process 
  *
- * Copyright 2011 Red Hat, Inc.
+ * Copyright 2011-2018 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
@@ -23,10 +23,15 @@
 #include "config.h"
 
 #include <unistd.h>
 #include <string.h>
 #include <signal.h>
+#ifdef __linux
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
 #include <sys/wait.h>
 #include <errno.h>
 #include <stdio.h>
 
 #include <gio/gio.h>
@@ -56,10 +61,11 @@ typedef struct {
   A11yBusState state;
   /* -1 == error, 0 == pending, > 0 == running */
   int a11y_bus_pid;
   char *a11y_bus_address;
   int pipefd[2];
+  int listenfd;
   char *a11y_launch_error_message;
 } A11yBusLauncher;
 
 static A11yBusLauncher *_global_app = NULL;
 
@@ -213,27 +219,10 @@ name_appeared_handler (GDBusConnection *connection,
   A11yBusLauncher *app = user_data;
 
   register_client (app);
 }
 
-static void
-setup_bus_child (gpointer data)
-{
-  A11yBusLauncher *app = data;
-  (void) app;
-
-  close (app->pipefd[0]);
-  dup2 (app->pipefd[1], 3);
-  close (app->pipefd[1]);
-
-  /* On Linux, tell the bus process to exit if this process goes away */
-#ifdef __linux
-#include <sys/prctl.h>
-  prctl (PR_SET_PDEATHSIG, 15);
-#endif  
-}
-
 /**
  * unix_read_all_fd_to_string:
  *
  * Read all data from a file descriptor to a C string buffer.
  */
@@ -274,37 +263,43 @@ on_bus_exited (GPid     pid,
         app->a11y_launch_error_message = g_strdup_printf ("Bus stopped by signal %d", WSTOPSIG (status));
     }
   g_main_loop_quit (app->loop);
 } 
 
+#ifdef DBUS_DAEMON
+static void
+setup_bus_child_daemon (gpointer data)
+{
+  A11yBusLauncher *app = data;
+  (void) app;
+
+  close (app->pipefd[0]);
+  dup2 (app->pipefd[1], 3);
+  close (app->pipefd[1]);
+
+  /* On Linux, tell the bus process to exit if this process goes away */
+#ifdef __linux
+  prctl (PR_SET_PDEATHSIG, 15);
+#endif
+}
+
 static gboolean
-ensure_a11y_bus (A11yBusLauncher *app)
+ensure_a11y_bus_daemon (A11yBusLauncher *app, char *config_path)
 {
+  char *argv[] = { DBUS_DAEMON, config_path, "--nofork", "--print-address", "3", NULL };
   GPid pid;
-  char *argv[] = { DBUS_DAEMON, NULL, "--nofork", "--print-address", "3", NULL };
   char addr_buf[2048];
   GError *error = NULL;
-  const char *config_path = NULL;
-
-  if (app->a11y_bus_pid != 0)
-    return FALSE;
-
-  if (g_file_test (SYSCONFDIR"/at-spi2/accessibility.conf", G_FILE_TEST_EXISTS))
-      config_path = "--config-file="SYSCONFDIR"/at-spi2/accessibility.conf";
-  else
-      config_path = "--config-file="DATADIR"/defaults/at-spi2/accessibility.conf";
-
-  argv[1] = config_path;
 
   if (pipe (app->pipefd) < 0)
     g_error ("Failed to create pipe: %s", strerror (errno));
   
   if (!g_spawn_async (NULL,
                       argv,
                       NULL,
                       G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
-                      setup_bus_child,
+                      setup_bus_child_daemon,
                       app,
                       &pid,
                       &error))
     {
       app->a11y_bus_pid = -1;
@@ -333,10 +328,140 @@ ensure_a11y_bus (A11yBusLauncher *app)
 
   /* Trim the trailing newline */
   app->a11y_bus_address = g_strchomp (g_strdup (addr_buf));
   g_debug ("a11y bus address: %s", app->a11y_bus_address);
 
+  return TRUE;
+
+error:
+  close (app->pipefd[0]);
+  close (app->pipefd[1]);
+  app->state = A11Y_BUS_STATE_ERROR;
+
+  return FALSE;
+}
+#else
+static gboolean
+ensure_a11y_bus_daemon (A11yBusLauncher *app, char *config_path)
+{
+	return FALSE;
+}
+#endif
+
+#ifdef DBUS_BROKER
+static void
+setup_bus_child_broker (gpointer data)
+{
+  A11yBusLauncher *app = data;
+  gchar *pid_str;
+  (void) app;
+
+  dup2 (app->listenfd, 3);
+  close (app->listenfd);
+  g_setenv("LISTEN_FDS", "1", TRUE);
+
+  pid_str = g_strdup_printf("%u", getpid());
+  g_setenv("LISTEN_PID", pid_str, TRUE);
+  g_free(pid_str);
+
+  /* Tell the bus process to exit if this process goes away */
+  prctl (PR_SET_PDEATHSIG, SIGTERM);
+}
+
+static gboolean
+ensure_a11y_bus_broker (A11yBusLauncher *app, char *config_path)
+{
+  char *argv[] = { DBUS_BROKER, config_path, "--scope", "user", NULL };
+  struct sockaddr_un addr = { .sun_family = AF_UNIX };
+  socklen_t addr_len = sizeof(addr);
+  GPid pid;
+  GError *error = NULL;
+
+  if ((app->listenfd = socket (PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) < 0)
+    g_error ("Failed to create listening socket: %s", strerror (errno));
+
+  if (bind (app->listenfd, (struct sockaddr *)&addr, sizeof(sa_family_t)) < 0)
+    g_error ("Failed to bind listening socket: %s", strerror (errno));
+
+  if (getsockname (app->listenfd, (struct sockaddr *)&addr, &addr_len) < 0)
+    g_error ("Failed to get socket name for listening socket: %s", strerror(errno));
+
+  if (listen (app->listenfd, 1024) < 0)
+    g_error ("Failed to listen on socket: %s", strerror(errno));
+
+  if (!g_spawn_async (NULL,
+                      argv,
+                      NULL,
+                      G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+                      setup_bus_child_broker,
+                      app,
+                      &pid,
+                      &error))
+    {
+      app->a11y_bus_pid = -1;
+      app->a11y_launch_error_message = g_strdup (error->message);
+      g_clear_error (&error);
+      goto error;
+    }
+
+  close (app->listenfd);
+  app->listenfd = -1;
+
+  g_child_watch_add (pid, on_bus_exited, app);
+  app->a11y_bus_pid = pid;
+  g_debug ("Launched a11y bus, child is %ld", (long) pid);
+  app->state = A11Y_BUS_STATE_RUNNING;
+
+  app->a11y_bus_address = g_strconcat("unix:abstract=", addr.sun_path + 1, NULL);
+  g_debug ("a11y bus address: %s", app->a11y_bus_address);
+
+  return TRUE;
+
+error:
+  close (app->listenfd);
+  app->state = A11Y_BUS_STATE_ERROR;
+
+  return FALSE;
+}
+#else
+static gboolean
+ensure_a11y_bus_broker (A11yBusLauncher *app, char *config_path)
+{
+	return FALSE;
+}
+#endif
+
+static gboolean
+ensure_a11y_bus (A11yBusLauncher *app)
+{
+  char *config_path = NULL;
+  gboolean success = FALSE;
+
+  if (app->a11y_bus_pid != 0)
+    return FALSE;
+
+  if (g_file_test (SYSCONFDIR"/at-spi2/accessibility.conf", G_FILE_TEST_EXISTS))
+      config_path = "--config-file="SYSCONFDIR"/at-spi2/accessibility.conf";
+  else
+      config_path = "--config-file="DATADIR"/defaults/at-spi2/accessibility.conf";
+
+#ifdef WANT_DBUS_BROKER
+    success = ensure_a11y_bus_broker (app, config_path);
+    if (!success)
+      {
+        if (!ensure_a11y_bus_daemon (app, config_path))
+            return FALSE;
+      }
+#else
+    success = ensure_a11y_bus_daemon (app, config_path);
+    if (!success)
+      {
+        if (!ensure_a11y_bus_broker (app, config_path))
+            return FALSE;
+      }
+#endif
+
 #ifdef HAVE_X11
   {
     Display *display = XOpenDisplay (NULL);
     if (display)
       {
@@ -351,17 +476,10 @@ ensure_a11y_bus (A11yBusLauncher *app)
       }
   }
 #endif
 
   return TRUE;
-  
- error:
-  close (app->pipefd[0]);
-  close (app->pipefd[1]);
-  app->state = A11Y_BUS_STATE_ERROR;
-
-  return FALSE;
 }
 
 static void
 handle_method_call (GDBusConnection       *connection,
                     const gchar           *sender,
diff --git a/bus/meson.build b/bus/meson.build
index 909103ce1275e1f9cf812f0615ba2d87030d142c..5c7e03d348bab1af06d27f2b8cd91ce4f1d6f650 100644
--- a/bus/meson.build
+++ b/bus/meson.build
@@ -29,27 +29,45 @@ configure_file(input: 'at-spi-dbus-bus.service.in',
                output: 'at-spi-dbus-bus.service',
                configuration: libexec_conf,
                install: true,
                install_dir: systemd_user_dir)
 
+launcher_args = [
+             '-DSYSCONFDIR="@0@"'.format(atspi_sysconfdir),
+             '-DDATADIR="@0@"'.format(atspi_datadir),
+           ]
+
 if get_option('dbus_daemon') != 'default'
-  dbus_daemon = get_option('dbus_daemon')
+  launcher_args += '-DDBUS_DAEMON="@0@"'.format(get_option('dbus_daemon'))
 else
   dbus_daemon = find_program('dbus-daemon',
                              '/sbin/dbus-daemon',
                              '/usr/sbin/dbus-daemon',
                              '/libexec/dbus-daemon',
                              '/usr/libexec/dbus-daemon',
                              '/usr/pkg/bin/dbus-daemon',
-                             required: true).path()
+                             required: false)
+  if dbus_daemon.found()
+    launcher_args += '-DDBUS_DAEMON="@0@"'.format(dbus_daemon.path())
+  endif
+endif
+
+if get_option('dbus_broker') != 'default'
+  launcher_args += '-DDBUS_BROKER="@0@"'.format(get_option('dbus_broker'))
+else
+  dbus_broker = find_program('dbus-broker-launch',
+                             required: false)
+  if dbus_broker.found()
+    launcher_args += '-DDBUS_BROKER="@0@"'.format(dbus_broker.path())
+  endif
+endif
+
+if get_option('default_bus') == 'dbus-broker'
+  launcher_args += '-DWANT_DBUS_BROKER'
 endif
 
 executable('at-spi-bus-launcher', 'at-spi-bus-launcher.c',
            include_directories: [ root_inc, include_directories('.') ],
            dependencies: [ gio_dep, x11_deps ],
-           c_args: [
-             '-DSYSCONFDIR="@0@"'.format(atspi_sysconfdir),
-             '-DDATADIR="@0@"'.format(atspi_datadir),
-             '-DDBUS_DAEMON="@0@"'.format(dbus_daemon),
-           ],
+           c_args: launcher_args,
            install: true,
            install_dir: atspi_libexecdir)
diff --git a/meson_options.txt b/meson_options.txt
index c9afe2faaba76c85091f51e554fe33567e4a1e13..f8ec8621a53d0f53398af240ea9b18f1d732b6f0 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -4,10 +4,19 @@ option('dbus_services_dir',
        value: 'default')
 option('dbus_daemon',
        description: 'The path of the DBus daemon',
        type: 'string',
        value: 'default')
+option('dbus_broker',
+       description: 'The path of the DBus broker',
+       type: 'string',
+       value: 'default')
+option('default_bus',
+       description: 'The default DBus implementation to use',
+       type: 'combo',
+       choices: ['dbus-daemon', 'dbus-broker'],
+       value: 'dbus-daemon')
 option('systemd_user_dir',
        description: 'Location of the systemd user services',
        type: 'string',
        value: 'default')
 option('enable_docs',
-- 
2.19.0.rc0