mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-09-12 21:36:14 +02:00
gio: implement unixexec dbus server address support
This commit is contained in:
parent
4144341e7a
commit
df71a47e5a
@ -41,13 +41,21 @@
|
||||
#include "glib-private.h"
|
||||
#include "gdbusprivate.h"
|
||||
#include "gstdio.h"
|
||||
#include "gspawn.h"
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
#include <gio/gunixsocketaddress.h>
|
||||
#include <gio/gunixinputstream.h>
|
||||
#include <gio/gunixoutputstream.h>
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <windows.h>
|
||||
@ -74,6 +82,8 @@
|
||||
*
|
||||
* Since GLib 2.72, `unix:` addresses are supported on Windows with `AF_UNIX`
|
||||
* support (Windows 10).
|
||||
*
|
||||
* `unixexec:` address type support.
|
||||
*/
|
||||
|
||||
static gchar *get_session_address_platform_specific (GError **error);
|
||||
@ -288,6 +298,30 @@ is_valid_nonce_tcp (const gchar *address_entry,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
static gboolean
|
||||
is_valid_unixexec (const gchar *address_entry,
|
||||
GHashTable *key_value_pairs,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
gchar *path = g_hash_table_lookup (key_value_pairs, "path");
|
||||
|
||||
if (path == NULL ||
|
||||
path[0] == '\0' )
|
||||
{
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_ARGUMENT,
|
||||
_("Error in address “%s” — missing key “path”"),
|
||||
address_entry);
|
||||
ret = FALSE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static gboolean
|
||||
is_valid_tcp (const gchar *address_entry,
|
||||
GHashTable *key_value_pairs,
|
||||
@ -413,6 +447,8 @@ g_dbus_is_supported_address (const gchar *string,
|
||||
supported = is_valid_tcp (a[n], key_value_pairs, error);
|
||||
else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
|
||||
supported = is_valid_nonce_tcp (a[n], key_value_pairs, error);
|
||||
else if (g_strcmp0 (transport_name, "unixexec") == 0)
|
||||
supported = is_valid_unixexec (a[n], key_value_pairs, error);
|
||||
else if (g_strcmp0 (a[n], "autolaunch:") == 0)
|
||||
supported = TRUE;
|
||||
else
|
||||
@ -545,7 +581,42 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gint
|
||||
sane_dup2 (gint fd1, gint fd2)
|
||||
{
|
||||
gint ret;
|
||||
|
||||
retry:
|
||||
ret = dup2 (fd1, fd2);
|
||||
if (ret < 0 && errno == EINTR)
|
||||
goto retry;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
static void
|
||||
unixexec_prepare_child (gpointer data)
|
||||
{
|
||||
int fd;
|
||||
gint *s = (gint*)data;
|
||||
|
||||
/* We want to keep socket pair alive. Therefore we don't set CLOEXEC on it */
|
||||
fd = sane_dup2 (s[1], STDIN_FILENO);
|
||||
g_assert (fd == STDIN_FILENO);
|
||||
fd = sane_dup2 (s[1], STDOUT_FILENO);
|
||||
g_assert (fd == STDOUT_FILENO);
|
||||
}
|
||||
|
||||
static void
|
||||
unixexec_child_watch (GPid pid,
|
||||
gint status,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_io_stream_close (G_IO_STREAM (user_data), NULL, NULL);
|
||||
|
||||
g_spawn_close_pid (pid);
|
||||
}
|
||||
|
||||
static GIOStream *
|
||||
g_dbus_address_try_connect_one (const gchar *address_entry,
|
||||
@ -553,6 +624,53 @@ g_dbus_address_try_connect_one (const gchar *address_entry,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/* Mark it as static and keep it in gdbusaddress.c until some other glib
|
||||
file needs it
|
||||
*/
|
||||
static void
|
||||
g_socketpair (gint domain,
|
||||
gint type,
|
||||
gint protocol,
|
||||
gint *s,
|
||||
GError **error)
|
||||
{
|
||||
int r;
|
||||
int errsv;
|
||||
|
||||
#ifdef SOCK_CLOEXEC
|
||||
r = socketpair (domain, type | SOCK_CLOEXEC, protocol, s);
|
||||
errsv = errno;
|
||||
|
||||
/* It's possible that libc has SOCK_CLOEXEC but the kernel does not */
|
||||
if (r == -1 && (errsv == EINVAL || errsv == EPROTOTYPE))
|
||||
#endif
|
||||
r = socketpair (domain, type, protocol, s);
|
||||
|
||||
if (r == -1)
|
||||
{
|
||||
int errsv = errno;
|
||||
|
||||
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
|
||||
_("Unable to create socketpair: %s"), g_strerror (errsv));
|
||||
errno = errsv;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fcntl (s[0], F_SETFD, FD_CLOEXEC) < 0 ||
|
||||
fcntl (s[1], F_SETFD, FD_CLOEXEC) < 0)
|
||||
{
|
||||
int errsv = errno;
|
||||
g_close (s[0], NULL);
|
||||
g_close (s[1], NULL);
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
g_io_error_from_errno (errsv),
|
||||
_("Failed to call fcntl: %s"),
|
||||
g_strerror (errsv));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Declare an extension point called GDBusTransport (or similar)
|
||||
* and move code below to extensions implementing said extension
|
||||
* point. That way we can implement a D-Bus transport over X11 without
|
||||
@ -603,6 +721,70 @@ g_dbus_address_connect (const gchar *address_entry,
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
else if (g_strcmp0 (transport_name, "unixexec") == 0)
|
||||
{
|
||||
gint s[2];
|
||||
GPid child_pid;
|
||||
GSocket *fdsocket;
|
||||
const gchar **argv;
|
||||
guint i = 0;
|
||||
gchar *args;
|
||||
guint argnum = 1;
|
||||
|
||||
g_socketpair (AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0, s, error);
|
||||
|
||||
if (*error != NULL)
|
||||
goto out;
|
||||
|
||||
argv = g_malloc_n (g_hash_table_size (key_value_pairs) + 2, sizeof (gchar *));
|
||||
/* We know path exist if we made it here */
|
||||
argv[i++] = g_hash_table_lookup (key_value_pairs, "path");
|
||||
if (g_hash_table_contains (key_value_pairs, "argv0"))
|
||||
argv[i++] = g_hash_table_lookup (key_value_pairs, "argv0");
|
||||
else
|
||||
argv[i++] = g_hash_table_lookup (key_value_pairs, "path");
|
||||
|
||||
for (args = g_strdup_printf ("argv%u", argnum++); g_hash_table_contains (key_value_pairs, args);)
|
||||
{
|
||||
argv[i++] = g_hash_table_lookup (key_value_pairs, args);
|
||||
g_free (args);
|
||||
args = g_strdup_printf ("argv%u", argnum++);
|
||||
}
|
||||
|
||||
g_free (args);
|
||||
argv[i++] = NULL;
|
||||
|
||||
if (!g_spawn_async_with_pipes (NULL,
|
||||
(gchar **)argv,
|
||||
NULL,
|
||||
G_SPAWN_SEARCH_PATH |
|
||||
G_SPAWN_CHILD_INHERITS_STDIN |
|
||||
G_SPAWN_DO_NOT_REAP_CHILD |
|
||||
G_SPAWN_FILE_AND_ARGV_ZERO,
|
||||
unixexec_prepare_child,
|
||||
s,
|
||||
&child_pid,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
error))
|
||||
{
|
||||
g_close (s[0], NULL);
|
||||
g_close (s[1], NULL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
g_close (s[1], NULL);
|
||||
|
||||
fdsocket = g_socket_new_from_fd (s[0], error);
|
||||
if (fdsocket == NULL)
|
||||
{
|
||||
g_close (s[0], NULL);
|
||||
goto out;
|
||||
}
|
||||
ret = (GIOStream*)g_socket_connection_factory_create_connection (fdsocket);
|
||||
g_child_watch_add (child_pid, unixexec_child_watch, ret);
|
||||
}
|
||||
else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0)
|
||||
{
|
||||
const gchar *s;
|
||||
|
@ -125,9 +125,85 @@ test_unix_address (void)
|
||||
assert_not_supported_address ("unix:path=/tmp,dir=/tmp");
|
||||
assert_not_supported_address ("unix:abstract=/tmp/foo,dir=/tmp");
|
||||
assert_not_supported_address ("unix:");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_unixexec_address_connect (void)
|
||||
{
|
||||
/* If the host machine has socat program, test program uses
|
||||
* socat to connect to the session bus to retrieve dbus client
|
||||
* list */
|
||||
|
||||
GError *error = NULL;
|
||||
GDBusConnection *con;
|
||||
gchar *addr;
|
||||
gchar *unixexec;
|
||||
|
||||
if (g_find_program_in_path ("socat") == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
addr = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, NULL, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (addr);
|
||||
if (g_str_has_prefix (addr, "unix:path=") == FALSE)
|
||||
{
|
||||
g_free (addr);
|
||||
return;
|
||||
}
|
||||
unixexec = g_strdup_printf ("unixexec:path=socat,argv1=STDIO,argv2=%s", addr + 10);
|
||||
g_free (addr);
|
||||
|
||||
con = g_dbus_connection_new_for_address_sync (
|
||||
unixexec,
|
||||
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
|
||||
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
|
||||
NULL,
|
||||
NULL,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
g_free (unixexec);
|
||||
|
||||
g_dbus_connection_call_sync (
|
||||
con,
|
||||
"org.freedesktop.DBus",
|
||||
"/org/freedesktop/DBus",
|
||||
"org.freedesktop.DBus",
|
||||
"ListNames",
|
||||
NULL,
|
||||
G_VARIANT_TYPE("(as)"),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
}
|
||||
|
||||
static void
|
||||
test_unixexec_address (void)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GIOStream *s;
|
||||
|
||||
g_assert_true (g_dbus_is_supported_address ("unixexec:path=/abc,argv0=0,argv1=1,argv2=2", NULL));
|
||||
g_assert_true (g_dbus_is_supported_address ("unixexec:path=/abc", NULL));
|
||||
g_assert_true (g_dbus_is_supported_address ("unixexec:path=/abc,argv0=0", NULL));
|
||||
g_assert_true (g_dbus_is_supported_address ("unixexec:path=./abc,argv0=0", NULL));
|
||||
g_assert_false (g_dbus_is_supported_address ("unixexec:argv0=0,argv1=1,argv2=2", NULL));
|
||||
g_assert_false (g_dbus_is_supported_address ("unixexecpath=/abc", NULL));
|
||||
g_assert_false (g_dbus_is_supported_address ("unixexec:argv0=0,argv1=1,argv2=2", NULL));
|
||||
s = g_dbus_address_get_stream_sync ("unixexec:path=cat",
|
||||
NULL,
|
||||
NULL,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
g_object_unref (s);
|
||||
|
||||
test_unixexec_address_connect ();
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
test_nonce_tcp_address (void)
|
||||
{
|
||||
@ -215,6 +291,7 @@ main (int argc,
|
||||
g_test_add_func ("/gdbus/unsupported-address", test_unsupported_address);
|
||||
g_test_add_func ("/gdbus/address-parsing", test_address_parsing);
|
||||
g_test_add_func ("/gdbus/unix-address", test_unix_address);
|
||||
g_test_add_func ("/gdbus/unixexec-address", test_unixexec_address);
|
||||
g_test_add_func ("/gdbus/nonce-tcp-address", test_nonce_tcp_address);
|
||||
g_test_add_func ("/gdbus/tcp-address", test_tcp_address);
|
||||
g_test_add_func ("/gdbus/autolaunch-address", test_autolaunch_address);
|
||||
|
Loading…
Reference in New Issue
Block a user