[rename] renamed kdbus related macros
[platform/upstream/dbus.git] / dbus / dbus-transport-unix.c
index 2f2a3da..9a9fea5 100644 (file)
  *
  */
 
+#include <config.h>
+
+#include <stdio.h>
+
 #include "dbus-internals.h"
 #include "dbus-connection-internal.h"
 #include "dbus-transport-unix.h"
@@ -28,6 +32,7 @@
 #include "dbus-transport-protected.h"
 #include "dbus-watch.h"
 #include "dbus-sysdeps-unix.h"
+#include "dbus-test.h"
 
 /**
  * @defgroup DBusTransportUnix DBusTransport implementations for UNIX
@@ -107,6 +112,108 @@ _dbus_transport_new_for_domain_socket (const char     *path,
 }
 
 /**
+ * Creates a new transport for the given binary and arguments. This
+ * creates a client-side of a transport. The process will be forked
+ * off and executed with stdin/stdout connected to a local AF_UNIX
+ * socket.
+ *
+ * @param path the path to the domain socket.
+ * @param argv Parameters list
+ * @param error address where an error can be returned.
+ * @returns a new transport, or #NULL on failure.
+ */
+static DBusTransport*
+_dbus_transport_new_for_exec (const char     *path,
+                              char *const     argv[],
+                              DBusError      *error)
+{
+  int fd;
+  DBusTransport *transport;
+  DBusString address;
+  unsigned i;
+  char *escaped;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  if (!_dbus_string_init (&address))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      return NULL;
+    }
+
+  fd = -1;
+
+  escaped = dbus_address_escape_value (path);
+  if (!escaped)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed;
+    }
+
+  if (!_dbus_string_append (&address, "unixexec:path=") ||
+      !_dbus_string_append (&address, escaped))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      dbus_free (escaped);
+      goto failed;
+    }
+
+  dbus_free (escaped);
+
+  if (argv)
+    {
+      for (i = 0; argv[i]; i++)
+        {
+          dbus_bool_t success;
+
+          escaped = dbus_address_escape_value (argv[i]);
+          if (!escaped)
+            {
+              dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+              goto failed;
+            }
+
+          success = _dbus_string_append_printf (&address, ",argv%u=%s", i, escaped);
+          dbus_free (escaped);
+
+          if (!success)
+            {
+              dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+              goto failed;
+            }
+        }
+    }
+
+  fd = _dbus_connect_exec (path, argv, error);
+  if (fd < 0)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      goto failed;
+    }
+
+  _dbus_verbose ("Successfully connected to process %s\n",
+                 path);
+
+  transport = _dbus_transport_new_for_socket (fd, NULL, &address);
+  if (transport == NULL)
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed;
+    }
+
+  _dbus_string_free (&address);
+
+  return transport;
+
+ failed:
+  if (fd >= 0)
+    _dbus_close_socket (fd, NULL);
+
+  _dbus_string_free (&address);
+  return NULL;
+}
+
+/**
  * Opens platform specific transport types.
  * 
  * @param entry the address entry to try opening
@@ -167,8 +274,135 @@ _dbus_transport_open_platform_specific (DBusAddressEntry  *entry,
         {
           _DBUS_ASSERT_ERROR_IS_CLEAR (error);
           return DBUS_TRANSPORT_OPEN_OK;
-        }      
+        }
+    }
+  else if (strcmp (method, "unixexec") == 0)
+    {
+      const char *path;
+      unsigned i;
+      char **argv;
+
+      path = dbus_address_entry_get_value (entry, "path");
+      if (path == NULL)
+        {
+          _dbus_set_bad_address (error, NULL, NULL,
+                                 "No process path specified");
+          return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+        }
+
+      /* First count argv arguments */
+      for (i = 1; ; i++)
+        {
+          char t[4+20+1]; /* "argv" plus space for a formatted base 10 64bit integer, plus NUL */
+
+          snprintf (t, sizeof(t), "argv%u", i);
+
+          if (!dbus_address_entry_get_value (entry, t))
+            break;
+        }
+
+      /* Allocate string array */
+      argv = dbus_new0 (char*, i+1);
+      if (!argv)
+        {
+          dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+          return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+        }
+
+      /* Fill in string array */
+      for (i = 0; ; i++)
+        {
+          char t[4+20+1];
+          const char *p;
+
+          snprintf (t, sizeof(t), "argv%u", i);
+
+          p = dbus_address_entry_get_value (entry, t);
+          if (!p)
+            {
+              if (i == 0)
+                /* If argv0 isn't specified, fill in the path instead */
+                p = path;
+              else
+                break;
+            }
+
+          argv[i] = _dbus_strdup (p);
+          if (!argv[i])
+            {
+              dbus_free_string_array (argv);
+              dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+              return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+            }
+        }
+
+      *transport_p = _dbus_transport_new_for_exec (path, argv, error);
+      dbus_free_string_array (argv);
+
+      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;
+        }
     }
+#ifdef DBUS_ENABLE_LAUNCHD
+  else if (strcmp (method, "launchd") == 0)
+    {
+      DBusError tmp_error = DBUS_ERROR_INIT;
+      const char *launchd_env_var = dbus_address_entry_get_value (entry, "env");
+      const char *launchd_socket;
+      DBusString socket_path;
+      dbus_bool_t valid_socket;
+
+      if (!_dbus_string_init (&socket_path))
+        {
+          _DBUS_SET_OOM (error);
+          return FALSE;
+        }
+
+      if (launchd_env_var == NULL)
+        {
+          _dbus_set_bad_address (error, "launchd", "env", NULL);
+          return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+        }
+
+      valid_socket = _dbus_lookup_launchd_socket (&socket_path, launchd_env_var, error);
+
+      if (dbus_error_is_set(error))
+        {
+          _dbus_string_free(&socket_path);
+          return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+        }
+
+      if (!valid_socket)
+        {
+          dbus_set_error(&tmp_error, DBUS_ERROR_BAD_ADDRESS,
+                         "launchd's env var %s does not exist", launchd_env_var);
+          dbus_error_free(error);
+          dbus_move_error(&tmp_error, error);
+          return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+        }
+
+      launchd_socket = _dbus_string_get_const_data(&socket_path);
+      *transport_p = _dbus_transport_new_for_domain_socket (launchd_socket, FALSE, 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;
+        }
+    }
+#endif
   else
     {
       _DBUS_ASSERT_ERROR_IS_CLEAR (error);
@@ -177,3 +411,32 @@ _dbus_transport_open_platform_specific (DBusAddressEntry  *entry,
 }
 
 /** @} */
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+
+dbus_bool_t
+_dbus_transport_unix_test (void)
+{
+  DBusConnection *c;
+  DBusError error;
+  dbus_bool_t ret;
+  const char *address;
+
+  dbus_error_init (&error);
+
+  c = dbus_connection_open ("unixexec:argv0=false,argv1=foobar,path=/bin/false", &error);
+  _dbus_assert (c != NULL);
+  _dbus_assert (!dbus_error_is_set (&error));
+
+  address = _dbus_connection_get_address (c);
+  _dbus_assert (address != NULL);
+
+  /* Let's see if the address got parsed, reordered and formatted correctly */
+  ret = strcmp (address, "unixexec:path=/bin/false,argv0=false,argv1=foobar") == 0;
+
+  dbus_connection_unref (c);
+
+  return ret;
+}
+
+#endif