DBus on OS X: reintroducing "classical" X11 (auto-)start as a fall-back

René J.V. Bertin <[email protected]>
Newsgroups gmane.comp.freedesktop.dbus
Message-ID <2917932.zIAPxt7Q6U@patux>
Hi,

The current DBus implementation for OS X only supports (auto) starting through launchd, when the user is logged in a local, "Aqua" session. On the one hand that certainly caters to the large majority of use cases, but on the other hand DBus is used (mostly?) with applications that weren't written specifically for OS X using native APIs. It doesn't seem far-fetched to assume that users who install gnome/gtk applications or Qt's xcb platform plugin expect to be able to use those applications also on a remote X11 display (I do, and from feedback elsewhere I deduce that I'm not alone). That would require a functional DBus that works and is started just like it would be on a "non-Mac Unix system" (to quote a comment in dbus source code).

After an exchange with Thiago Macieira (started because I was surprised to see Qt5 applications that don't even use DBus give an error when started on a remote X11 connection) I've started looking into modifying the code so that it would start a DBus daemon linked to the X11 connection. I'd need and appreciate some guidance:

I've followed the idea that it would be best to let the OS X default launchd starting mechanism fail gracefully, and then attempt to do the X11 default start as a fallback. It turns out to be rather easy to check if we're running in a context where launchd would allow to start the DBus. The launchd plist instructs it to do this only in Aqua sessions (which makes sense I guess), and one can obtain the current session type from launchctl. That would allow to "punt and fall back" as commented in dbus-sysdeps-unix.c .

With the attached patch (against 1.8.16), I get a failure with the error

%> dbus-launch --csh-syntax
Failed to start message bus: Unknown address type 'launchd'
EOF in dbus-launch reading address from bus daemon
Exit 1

It looks like I'm also going to need to modify dbus_server_listen (dbus-server.c) and/or bus_context_new (bus.c) so that other methods or addresses are tried if the launchd one fails. I haven't yet been able to follow where dbus_server_listen() and bus_context_new() get their list of options to try from. A config file, or something hard-coded?

Is there a better approach to accomplish this than the one I'm following? I'd like this to be as transparent as possible, so that no changes outside of DBus are required, and a daemon is started when needed both in an "Aqua" session (current behaviour) and when logged in from a remote host and displaying over X11 on that host.

Thanks,
René

_______________________________________________
dbus mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/dbus
patch-dbus-enable-nonlaunchd-start.diff (text/x-patch, 6.5 KB)
diff --git a/dbus/dbus-server-unix.c b/dbus/dbus-server-unix.c
index d995240..fccb861 100644
--- a/dbus/dbus-server-unix.c
+++ b/dbus/dbus-server-unix.c
@@ -195,6 +195,14 @@ _dbus_server_listen_platform_specific (DBusAddressEntry *entry,
 #ifdef DBUS_ENABLE_LAUNCHD
   else if (strcmp (method, "launchd") == 0)
     {
+      if (!_dbus_match_launchd_session_type("Aqua", error))
+        {
+          /* We don't handle this method when the session type is inappropriate;
+           * we return NULL with the error unset
+           */
+          _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+          return DBUS_SERVER_LISTEN_NOT_HANDLED;
+        }
       const char *launchd_env_var = dbus_address_entry_get_value (entry, "env");
       if (launchd_env_var == NULL)
         {
diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c
index 9e3f057..8e87b1e 100644
--- a/dbus/dbus-sysdeps-unix.c
+++ b/dbus/dbus-sysdeps-unix.c
@@ -3647,11 +3647,106 @@ _dbus_read_local_machine_uuid (DBusGUID   *machine_id,
 }
 
 /**
- * quries launchd for a specific env var which holds the socket path.
+ * queries launchd for the current session type: this will be Aqua
+ * when we're called running under an "Aqua" session on the local screen,
+ * regardless of what kind of application (native, X11, commandline, ...)
+ * we are. It will be something else (typically Background) when we're
+ * called from a remote connection
+ * @param session_type DBusString that will contain the returned type
+ * @param error a DBusError to store the error in case of failure
+ * @return true on success, false on error
+ */
+dbus_bool_t
+_dbus_lookup_launchd_session_type (DBusString *session_type,
+                             DBusError  *error)
+{
+#ifdef DBUS_ENABLE_LAUNCHD
+  char *argv[3];
+  int i;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  if (_dbus_check_setuid ())
+    {
+      dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED,
+                            "Using launchd is not supported when setuid");
+      return FALSE;
+    }
+
+  i = 0;
+  argv[i] = "launchctl";
+  ++i;
+  // from `man launchctl`:
+  // This prints the name of the launchd job manager which manages the current bootstrap.
+  // See LimitLoad-ToSessionType in launchd.plist(5) for more details.
+  argv[i] = "managername";
+  ++i;
+  argv[i] = NULL;
+  ++i;
+
+  _dbus_assert (i == _DBUS_N_ELEMENTS (argv));
+
+  if (!_read_subprocess_line_argv(argv[0], TRUE, argv, session_type, error))
+    {
+      return FALSE;
+    }
+
+  /* no error, but no result either */
+  if (_dbus_string_get_length(session_type) == 0)
+    {
+      return FALSE;
+    }
+
+  /* strip the carriage-return */
+  _dbus_string_shorten(session_type, 1);
+  return TRUE;
+#else /* DBUS_ENABLE_LAUNCHD */
+  dbus_set_error(error, DBUS_ERROR_NOT_SUPPORTED,
+                "can't lookup session type from launchd; launchd support not compiled in");
+  return FALSE;
+#endif
+}
+
+/**
+ * queries launchd for the current session type and checks if it matches
+ * the expected type.
+ * @param expected the expected type
+ * @param error a DBusError to store the error in case of failure
+ * @return true on success, false on error
+ */
+dbus_bool_t
+_dbus_match_launchd_session_type (const char *expected,
+                             DBusError  *error)
+{
+  DBusString session_type;
+  if (!_dbus_string_init (&session_type))
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+  if (!_dbus_lookup_launchd_session_type (&session_type, error) || dbus_error_is_set(error))
+    {
+      _dbus_string_free(&session_type);
+      return FALSE;
+    }
+  else if (!_dbus_string_equal_c_str (&session_type, expected))
+    {
+      // the launchd plist limits us to using Aqua sessions
+      dbus_set_error(error, "unexpected launchctl session type",
+                _dbus_string_get_const_data(&session_type));
+      _dbus_string_free(&session_type);
+      return FALSE;
+    }
+  _dbus_string_free(&session_type);
+  return TRUE;
+}
+
+/**
+ * queries launchd for a specific env var which holds the socket path.
  * @param socket_path append the socket path to this DBusString
  * @param launchd_env_var the env var to look up
  * @param error a DBusError to store the error in case of failure
- * @return the value of the env var
+ * @return true on success, false on error
  */
 dbus_bool_t
 _dbus_lookup_launchd_socket (DBusString *socket_path,
@@ -3706,7 +3801,7 @@ _dbus_lookup_launchd_socket (DBusString *socket_path,
 
 #ifdef DBUS_ENABLE_LAUNCHD
 static dbus_bool_t
-_dbus_lookup_session_address_launchd (DBusString *address, DBusError  *error)
+_dbus_lookup_session_address_launchd (dbus_bool_t *supported, DBusString *address, DBusError  *error)
 {
   dbus_bool_t valid_socket;
   DBusString socket_path;
@@ -3718,6 +3813,13 @@ _dbus_lookup_session_address_launchd (DBusString *address, DBusError  *error)
       return FALSE;
     }
 
+  *supported = TRUE;
+  if (!_dbus_match_launchd_session_type("Aqua", error))
+    {
+      *supported = FALSE;
+      return FALSE;
+    }
+
   if (!_dbus_string_init (&socket_path))
     {
       _DBUS_SET_OOM (error);
@@ -3784,8 +3886,10 @@ _dbus_lookup_session_address (dbus_bool_t *supported,
                               DBusError   *error)
 {
 #ifdef DBUS_ENABLE_LAUNCHD
-  *supported = TRUE;
-  return _dbus_lookup_session_address_launchd (address, error);
+  dbus_bool_t retval = _dbus_lookup_session_address_launchd (supported, address, error);
+  // _dbus_lookup_session_address_launchd returns FALSE with *supported==FALSE if we're not
+  // running in an Aqua session; in that case we'll try to behave like on non-Mac Unix platforms.
+  return (!*supported)? TRUE : retval;
 #else
   /* On non-Mac Unix platforms, if the session address isn't already
    * set in DBUS_SESSION_BUS_ADDRESS environment variable, we punt and
diff --git a/dbus/dbus-sysdeps-unix.h b/dbus/dbus-sysdeps-unix.h
index a265b33..f0a4e32 100644
--- a/dbus/dbus-sysdeps-unix.h
+++ b/dbus/dbus-sysdeps-unix.h
@@ -83,6 +83,10 @@ dbus_bool_t _dbus_read_credentials (int               client_fd,
 dbus_bool_t _dbus_send_credentials (int              server_fd,
                                     DBusError       *error);
 
+dbus_bool_t _dbus_lookup_launchd_session_type (DBusString *session_type,
+                                               DBusError  *error);
+dbus_bool_t _dbus_match_launchd_session_type (const char *expected,
+                                              DBusError  *error);
 dbus_bool_t _dbus_lookup_launchd_socket (DBusString *socket_path,
                                          const char *launchd_env_var,
                                          DBusError  *error);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.