X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgdbusaddress.c;h=fbf67a46b717d46750fc5dcfd369e205bfb1a3dc;hb=853692bdfd9f8a87aed70d21f643dc13b57c92d1;hp=21c520334a54a101de894e3b4dc2664d6a12047f;hpb=ef296440630ca01a8bc0a9dc58722bf1d6dc1ab7;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c index 21c5203..fbf67a4 100644 --- a/gio/gdbusaddress.c +++ b/gio/gdbusaddress.c @@ -13,9 +13,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. + * Public License along with this library; if not, see . * * Author: David Zeuthen */ @@ -24,6 +22,8 @@ #include #include +#include +#include #include "gioerror.h" #include "gdbusutils.h" @@ -35,14 +35,23 @@ #include "giostream.h" #include "gasyncresult.h" #include "gsimpleasyncresult.h" +#include "glib-private.h" #include "gdbusprivate.h" +#include "giomodule-priv.h" +#include "gdbusdaemon.h" #ifdef G_OS_UNIX #include +#include +#endif + +#ifdef G_OS_WIN32 +#include +#include +#include #endif #include "glibintl.h" -#include "gioalias.h" /** * SECTION:gdbusaddress @@ -52,9 +61,11 @@ * * Routines for working with D-Bus addresses. A D-Bus address is a string * like "unix:tmpdir=/tmp/my-app-name". The exact format of addresses - * is explained in detail in the D-Bus specification. + * is explained in detail in the [D-Bus specification](http://dbus.freedesktop.org/doc/dbus-specification.html\#addresses). */ +static gchar *get_session_address_platform_specific (GError **error); + /* ---------------------------------------------------------------------------------------------------- */ /** @@ -135,7 +146,7 @@ is_valid_unix (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Unsupported key `%s' in address entry `%s'"), + _("Unsupported key '%s' in address entry '%s'"), key, address_entry); goto out; @@ -146,26 +157,23 @@ is_valid_unix (const gchar *address_entry, { if (tmpdir != NULL || abstract != NULL) goto meaningless; - /* TODO: validate path */ } else if (tmpdir != NULL) { if (path != NULL || abstract != NULL) goto meaningless; - /* TODO: validate tmpdir */ } else if (abstract != NULL) { if (path != NULL || tmpdir != NULL) goto meaningless; - /* TODO: validate abstract */ } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Address `%s' is invalid (need exactly one of path, tmpdir or abstract keys)"), + _("Address '%s' is invalid (need exactly one of path, tmpdir or abstract keys)"), address_entry); goto out; } @@ -178,7 +186,7 @@ is_valid_unix (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Meaningless key/value pair combination in address entry `%s'"), + _("Meaningless key/value pair combination in address entry '%s'"), address_entry); out: @@ -226,7 +234,7 @@ is_valid_nonce_tcp (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Unsupported key `%s' in address entry `%s'"), + _("Unsupported key '%s' in address entry '%s'"), key, address_entry); goto out; @@ -241,7 +249,7 @@ is_valid_nonce_tcp (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Error in address `%s' - the port attribute is malformed"), + _("Error in address '%s' - the port attribute is malformed"), address_entry); goto out; } @@ -252,11 +260,18 @@ is_valid_nonce_tcp (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Error in address `%s' - the family attribute is malformed"), + _("Error in address '%s' - the family attribute is malformed"), address_entry); goto out; } + if (host != NULL) + { + /* TODO: validate host */ + } + + nonce_file = nonce_file; /* To avoid -Wunused-but-set-variable */ + ret= TRUE; out: @@ -300,7 +315,7 @@ is_valid_tcp (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Unsupported key `%s' in address entry `%s'"), + _("Unsupported key '%s' in address entry '%s'"), key, address_entry); goto out; @@ -315,7 +330,7 @@ is_valid_tcp (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Error in address `%s' - the port attribute is malformed"), + _("Error in address '%s' - the port attribute is malformed"), address_entry); goto out; } @@ -326,11 +341,16 @@ is_valid_tcp (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Error in address `%s' - the family attribute is malformed"), + _("Error in address '%s' - the family attribute is malformed"), address_entry); goto out; } + if (host != NULL) + { + /* TODO: validate host */ + } + ret= TRUE; out: @@ -339,6 +359,16 @@ is_valid_tcp (const gchar *address_entry, return ret; } +static int +g_dbus_is_supported_address_kdbus (const gchar *transport_name) +{ + int supported = 0; + + supported = g_strcmp0 (transport_name, "kernel") == 0; + + return supported; +} + /** * g_dbus_is_supported_address: * @string: A string. @@ -380,12 +410,15 @@ g_dbus_is_supported_address (const gchar *string, goto out; supported = FALSE; - if (g_strcmp0 (transport_name, "unix") == 0) + if ((g_strcmp0 (transport_name, "unix") == 0) + || g_dbus_is_supported_address_kdbus (transport_name)) supported = is_valid_unix (a[n], key_value_pairs, error); else if (g_strcmp0 (transport_name, "tcp") == 0) 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 (a[n], "autolaunch:") == 0) + supported = TRUE; g_free (transport_name); g_hash_table_unref (key_value_pairs); @@ -428,7 +461,7 @@ _g_dbus_address_parse_entry (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Address element `%s', does not contain a colon (:)"), + _("Address element '%s' does not contain a colon (:)"), address_entry); goto out; } @@ -449,16 +482,28 @@ _g_dbus_address_parse_entry (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Key/Value pair %d, `%s', in address element `%s', does not contain an equal sign"), + _("Key/Value pair %d, '%s', in address element '%s' does not contain an equal sign"), n, kv_pair, address_entry); goto out; } - /* TODO: actually validate that no illegal characters are present before and after then '=' sign */ key = g_uri_unescape_segment (kv_pair, s, NULL); value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL); + if (key == NULL || value == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error unescaping key or value in Key/Value pair %d, '%s', in address element '%s'"), + n, + kv_pair, + address_entry); + g_free (key); + g_free (value); + goto out; + } g_hash_table_insert (key_value_pairs, key, value); } @@ -488,6 +533,12 @@ out: /* ---------------------------------------------------------------------------------------------------- */ +static GIOStream * +g_dbus_address_try_connect_one (const gchar *address_entry, + gchar **out_guid, + GCancellable *cancellable, + GError **error); + /* 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 @@ -512,7 +563,8 @@ g_dbus_address_connect (const gchar *address_entry, { } #ifdef G_OS_UNIX - else if (g_strcmp0 (transport_name, "unix") == 0) + if ((g_strcmp0 (transport_name, "unix") == 0) + || g_dbus_is_supported_address_kdbus (transport_name)) { const gchar *path; const gchar *abstract; @@ -523,8 +575,8 @@ g_dbus_address_connect (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Error in address `%s' - the unix transport requires exactly one of the " - "keys `path' or `abstract' to be set"), + _("Error in address '%s' - the unix transport requires exactly one of the " + "keys 'path' or 'abstract' to be set"), address_entry); } else if (path != NULL) @@ -547,7 +599,7 @@ g_dbus_address_connect (const gchar *address_entry, { const gchar *s; const gchar *host; - guint port; + glong port; gchar *endp; gboolean is_nonce; @@ -559,7 +611,7 @@ g_dbus_address_connect (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Error in address `%s' - the host attribute is missing or malformed"), + _("Error in address '%s' - the host attribute is missing or malformed"), address_entry); goto out; } @@ -573,7 +625,7 @@ g_dbus_address_connect (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Error in address `%s' - the port attribute is missing or malformed"), + _("Error in address '%s' - the port attribute is missing or malformed"), address_entry); goto out; } @@ -587,88 +639,146 @@ g_dbus_address_connect (const gchar *address_entry, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Error in address `%s' - the noncefile attribute is missing or malformed"), + _("Error in address '%s' - the noncefile attribute is missing or malformed"), address_entry); goto out; } } - /* TODO: deal with family */ + /* TODO: deal with family key/value-pair */ connectable = g_network_address_new (host, port); } + else if (g_strcmp0 (address_entry, "autolaunch:") == 0) + { + gchar *autolaunch_address; + autolaunch_address = get_session_address_platform_specific (error); + if (autolaunch_address != NULL) + { + ret = g_dbus_address_try_connect_one (autolaunch_address, NULL, cancellable, error); + g_free (autolaunch_address); + goto out; + } + else + { + g_prefix_error (error, _("Error auto-launching: ")); + } + } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Unknown or unsupported transport `%s' for address `%s'"), + _("Unknown or unsupported transport '%s' for address '%s'"), transport_name, address_entry); } if (connectable != NULL) { - GSocketClient *client; - GSocketConnection *connection; - g_assert (ret == NULL); - client = g_socket_client_new (); - connection = g_socket_client_connect (client, - connectable, - cancellable, - error); - g_object_unref (connectable); - g_object_unref (client); - if (connection == NULL) - goto out; + if (g_dbus_is_supported_address_kdbus (transport_name)) + { + GKdbusConnection *connection; + gboolean status; + + const gchar *path; + path = g_hash_table_lookup (key_value_pairs, "path"); - ret = G_IO_STREAM (connection); + g_assert (ret == NULL); + connection = _g_kdbus_connection_new (); + status = _g_kdbus_connection_connect (connection, + path, + cancellable, + error); + g_object_unref (connectable); + + if (!status) + goto out; + + ret = G_IO_STREAM (connection); + } + else + { + GSocketClient *client; + GSocketConnection *connection; + + g_assert (ret == NULL); + client = g_socket_client_new (); + connection = g_socket_client_connect (client, + connectable, + cancellable, + error); + g_object_unref (connectable); + g_object_unref (client); + if (connection == NULL) + goto out; + + ret = G_IO_STREAM (connection); + } if (nonce_file != NULL) { - gchar *nonce_contents; - gsize nonce_length; + gchar nonce_contents[16 + 1]; + size_t num_bytes_read; + FILE *f; - /* TODO: too dangerous to read the entire file? (think denial-of-service etc.) */ - if (!g_file_get_contents (nonce_file, - &nonce_contents, - &nonce_length, - error)) + /* be careful to read only 16 bytes - we also check that the file is only 16 bytes long */ + f = fopen (nonce_file, "rb"); + if (f == NULL) { - g_prefix_error (error, _("Error reading nonce file `%s':"), nonce_file); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error opening nonce file '%s': %s"), + nonce_file, + g_strerror (errno)); g_object_unref (ret); ret = NULL; goto out; } - - if (nonce_length != 16) + num_bytes_read = fread (nonce_contents, + sizeof (gchar), + 16 + 1, + f); + if (num_bytes_read != 16) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("The nonce-file `%s' was %" G_GSIZE_FORMAT " bytes. Expected 16 bytes."), - nonce_file, - nonce_length); - g_free (nonce_contents); + if (num_bytes_read == 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error reading from nonce file '%s': %s"), + nonce_file, + g_strerror (errno)); + } + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error reading from nonce file '%s', expected 16 bytes, got %d"), + nonce_file, + (gint) num_bytes_read); + } g_object_unref (ret); ret = NULL; + fclose (f); goto out; } + fclose (f); if (!g_output_stream_write_all (g_io_stream_get_output_stream (ret), nonce_contents, - nonce_length, + 16, NULL, cancellable, error)) { - g_prefix_error (error, _("Error write contents of nonce file `%s' to stream:"), nonce_file); + g_prefix_error (error, _("Error writing contents of nonce file '%s' to stream:"), nonce_file); g_object_unref (ret); ret = NULL; - g_free (nonce_contents); goto out; } - g_free (nonce_contents); } } @@ -706,7 +816,6 @@ g_dbus_address_try_connect_one (const gchar *address_entry, if (ret == NULL) goto out; - /* TODO: validate that guid is of correct format */ guid = g_hash_table_lookup (key_value_pairs, "guid"); if (guid != NULL && out_guid != NULL) *out_guid = g_strdup (guid); @@ -753,16 +862,13 @@ get_stream_thread_func (GSimpleAsyncResult *res, cancellable, &error); if (data->stream == NULL) - { - g_simple_async_result_set_from_error (res, error); - g_error_free (error); - } + g_simple_async_result_take_error (res, error); } /** * g_dbus_address_get_stream: * @address: A valid D-Bus address. - * @cancellable: A #GCancellable or %NULL. + * @cancellable: (allow-none): A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: Data to pass to @callback. * @@ -794,6 +900,7 @@ g_dbus_address_get_stream (const gchar *address, callback, user_data, g_dbus_address_get_stream); + g_simple_async_result_set_check_cancellable (res, cancellable); data = g_new0 (GetStreamData, 1); data->address = g_strdup (address); g_simple_async_result_set_op_res_gpointer (res, @@ -814,7 +921,7 @@ g_dbus_address_get_stream (const gchar *address, * * Finishes an operation started with g_dbus_address_get_stream(). * - * Returns: A #GIOStream or %NULL if @error is set. + * Returns: (transfer full): A #GIOStream or %NULL if @error is set. * * Since: 2.26 */ @@ -850,7 +957,7 @@ g_dbus_address_get_stream_finish (GAsyncResult *res, * g_dbus_address_get_stream_sync: * @address: A valid D-Bus address. * @out_guid: %NULL or return location to store the GUID extracted from @address, if any. - * @cancellable: A #GCancellable or %NULL. + * @cancellable: (allow-none): A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously connects to an endpoint specified by @address and @@ -860,7 +967,7 @@ g_dbus_address_get_stream_finish (GAsyncResult *res, * This is a synchronous failable function. See * g_dbus_address_get_stream() for the asynchronous version. * - * Returns: A #GIOStream or %NULL if @error is set. + * Returns: (transfer full): A #GIOStream or %NULL if @error is set. * * Since: 2.26 */ @@ -882,7 +989,7 @@ g_dbus_address_get_stream_sync (const gchar *address, last_error = NULL; addr_array = g_strsplit (address, ";", 0); - if (addr_array[0] == NULL) + if (addr_array != NULL && addr_array[0] == NULL) { last_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, @@ -931,26 +1038,468 @@ g_dbus_address_get_stream_sync (const gchar *address, /* ---------------------------------------------------------------------------------------------------- */ -/* TODO: implement for UNIX, Win32 and OS X */ +#ifdef G_OS_UNIX +static gchar * +get_session_address_dbus_launch (GError **error) +{ + gchar *ret; + gchar *machine_id; + gchar *command_line; + gchar *launch_stdout; + gchar *launch_stderr; + gint exit_status; + gchar *old_dbus_verbose; + gboolean restore_dbus_verbose; + + ret = NULL; + machine_id = NULL; + command_line = NULL; + launch_stdout = NULL; + launch_stderr = NULL; + restore_dbus_verbose = FALSE; + old_dbus_verbose = NULL; + + /* Don't run binaries as root if we're setuid. */ + if (GLIB_PRIVATE_CALL (g_check_setuid) ()) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Cannot spawn a message bus when setuid")); + goto out; + } + + machine_id = _g_dbus_get_machine_id (error); + if (machine_id == NULL) + { + g_prefix_error (error, _("Cannot spawn a message bus without a machine-id: ")); + goto out; + } + + /* We're using private libdbus facilities here. When everything + * (X11, Mac OS X, Windows) is spec'ed out correctly (not even the + * X11 property is correctly documented right now) we should + * consider using the spec instead of dbus-launch. + * + * --autolaunch=MACHINEID + * This option implies that dbus-launch should scan for a previ‐ + * ously-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. This option + * is for the exclusive use of libdbus, you do not want to use it + * manually. It may change in the future. + */ + + /* TODO: maybe provide a variable for where to look for the dbus-launch binary? */ + command_line = g_strdup_printf ("dbus-launch --autolaunch=%s --binary-syntax --close-stderr", machine_id); + + if (G_UNLIKELY (_g_dbus_debug_address ())) + { + _g_dbus_debug_print_lock (); + g_print ("GDBus-debug:Address: Running '%s' to get bus address (possibly autolaunching)\n", command_line); + old_dbus_verbose = g_strdup (g_getenv ("DBUS_VERBOSE")); + restore_dbus_verbose = TRUE; + g_setenv ("DBUS_VERBOSE", "1", TRUE); + _g_dbus_debug_print_unlock (); + } + + if (!g_spawn_command_line_sync (command_line, + &launch_stdout, + &launch_stderr, + &exit_status, + error)) + { + goto out; + } + + if (!g_spawn_check_exit_status (exit_status, error)) + { + g_prefix_error (error, _("Error spawning command line '%s': "), command_line); + goto out; + } + + /* From the dbus-launch(1) man page: + * + * --binary-syntax Write to stdout a nul-terminated bus address, + * then the bus PID as a binary integer of size sizeof(pid_t), + * then the bus X window ID as a binary integer of size + * sizeof(long). Integers are in the machine's byte order, not + * network byte order or any other canonical byte order. + */ + ret = g_strdup (launch_stdout); + + out: + if (G_UNLIKELY (_g_dbus_debug_address ())) + { + gchar *s; + _g_dbus_debug_print_lock (); + g_print ("GDBus-debug:Address: dbus-launch output:"); + if (launch_stdout != NULL) + { + s = _g_dbus_hexdump (launch_stdout, strlen (launch_stdout) + 1 + sizeof (pid_t) + sizeof (long), 2); + g_print ("\n%s", s); + g_free (s); + } + else + { + g_print (" (none)\n"); + } + g_print ("GDBus-debug:Address: dbus-launch stderr output:"); + if (launch_stderr != NULL) + g_print ("\n%s", launch_stderr); + else + g_print (" (none)\n"); + _g_dbus_debug_print_unlock (); + } + + g_free (machine_id); + g_free (command_line); + g_free (launch_stdout); + g_free (launch_stderr); + if (G_UNLIKELY (restore_dbus_verbose)) + { + if (old_dbus_verbose != NULL) + g_setenv ("DBUS_VERBOSE", old_dbus_verbose, TRUE); + else + g_unsetenv ("DBUS_VERBOSE"); + } + g_free (old_dbus_verbose); + return ret; +} +#endif + +#ifdef G_OS_WIN32 + +#define DBUS_DAEMON_ADDRESS_INFO "DBusDaemonAddressInfo" +#define DBUS_DAEMON_MUTEX "DBusDaemonMutex" +#define UNIQUE_DBUS_INIT_MUTEX "UniqueDBusInitMutex" +#define DBUS_AUTOLAUNCH_MUTEX "DBusAutolaunchMutex" + +static void +release_mutex (HANDLE mutex) +{ + ReleaseMutex (mutex); + CloseHandle (mutex); +} + +static HANDLE +acquire_mutex (const char *mutexname) +{ + HANDLE mutex; + DWORD res; + + mutex = CreateMutexA (NULL, FALSE, mutexname); + if (!mutex) + return 0; + + res = WaitForSingleObject (mutex, INFINITE); + switch (res) + { + case WAIT_ABANDONED: + release_mutex (mutex); + return 0; + case WAIT_FAILED: + case WAIT_TIMEOUT: + return 0; + } + + return mutex; +} + +static gboolean +is_mutex_owned (const char *mutexname) +{ + HANDLE mutex; + gboolean res = FALSE; + + mutex = CreateMutexA (NULL, FALSE, mutexname); + if (WaitForSingleObject (mutex, 10) == WAIT_TIMEOUT) + res = TRUE; + else + ReleaseMutex (mutex); + CloseHandle (mutex); + + return res; +} + +static char * +read_shm (const char *shm_name) +{ + HANDLE shared_mem; + char *shared_data; + char *res; + int i; + + res = NULL; + + for (i = 0; i < 20; i++) + { + shared_mem = OpenFileMappingA (FILE_MAP_READ, FALSE, shm_name); + if (shared_mem != 0) + break; + Sleep (100); + } + + if (shared_mem != 0) + { + shared_data = MapViewOfFile (shared_mem, FILE_MAP_READ, 0, 0, 0); + if (shared_data != NULL) + { + res = g_strdup (shared_data); + UnmapViewOfFile (shared_data); + } + CloseHandle (shared_mem); + } + + return res; +} + +static HANDLE +set_shm (const char *shm_name, const char *value) +{ + HANDLE shared_mem; + char *shared_data; + + shared_mem = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, + 0, strlen (value) + 1, shm_name); + if (shared_mem == 0) + return 0; + + shared_data = MapViewOfFile (shared_mem, FILE_MAP_WRITE, 0, 0, 0 ); + if (shared_data == NULL) + return 0; + + strcpy (shared_data, value); + + UnmapViewOfFile (shared_data); + + return shared_mem; +} + +/* These keep state between publish_session_bus and unpublish_session_bus */ +static HANDLE published_daemon_mutex; +static HANDLE published_shared_mem; + +static gboolean +publish_session_bus (const char *address) +{ + HANDLE init_mutex; + + init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX); + + published_daemon_mutex = CreateMutexA (NULL, FALSE, DBUS_DAEMON_MUTEX); + if (WaitForSingleObject (published_daemon_mutex, 10 ) != WAIT_OBJECT_0) + { + release_mutex (init_mutex); + CloseHandle (published_daemon_mutex); + published_daemon_mutex = NULL; + return FALSE; + } + + published_shared_mem = set_shm (DBUS_DAEMON_ADDRESS_INFO, address); + if (!published_shared_mem) + { + release_mutex (init_mutex); + CloseHandle (published_daemon_mutex); + published_daemon_mutex = NULL; + return FALSE; + } + + release_mutex (init_mutex); + return TRUE; +} + +static void +unpublish_session_bus (void) +{ + HANDLE init_mutex; + + init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX); + + CloseHandle (published_shared_mem); + published_shared_mem = NULL; + + release_mutex (published_daemon_mutex); + published_daemon_mutex = NULL; + + release_mutex (init_mutex); +} + +static void +wait_console_window (void) +{ + FILE *console = fopen ("CONOUT$", "w"); + + SetConsoleTitleW (L"gdbus-daemon output. Type any character to close this window."); + fprintf (console, _("(Type any character to close this window)\n")); + fflush (console); + _getch (); +} + +static void +open_console_window (void) +{ + if (((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE || + (HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE) && AllocConsole ()) + { + if ((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE) + freopen ("CONOUT$", "w", stdout); + + if ((HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE) + freopen ("CONOUT$", "w", stderr); + + SetConsoleTitleW (L"gdbus-daemon debug output."); + + atexit (wait_console_window); + } +} +static void +idle_timeout_cb (GDBusDaemon *daemon, gpointer user_data) +{ + GMainLoop *loop = user_data; + g_main_loop_quit (loop); +} + +__declspec(dllexport) void CALLBACK g_win32_run_session_bus (HWND hwnd, HINSTANCE hinst, char *cmdline, int nCmdShow); + +__declspec(dllexport) void CALLBACK +g_win32_run_session_bus (HWND hwnd, HINSTANCE hinst, char *cmdline, int nCmdShow) +{ + GDBusDaemon *daemon; + GMainLoop *loop; + const char *address; + GError *error = NULL; + + if (g_getenv ("GDBUS_DAEMON_DEBUG") != NULL) + open_console_window (); + + loop = g_main_loop_new (NULL, FALSE); + + address = "nonce-tcp:"; + daemon = _g_dbus_daemon_new (address, NULL, &error); + if (daemon == NULL) + { + g_printerr ("Can't init bus: %s\n", error->message); + return; + } + + g_signal_connect (daemon, "idle-timeout", G_CALLBACK (idle_timeout_cb), loop); + + if ( publish_session_bus (_g_dbus_daemon_get_address (daemon))) + { + g_main_loop_run (loop); + + unpublish_session_bus (); + } + + g_main_loop_unref (loop); + g_object_unref (daemon); +} + +static gchar * +get_session_address_dbus_launch (GError **error) +{ + HANDLE autolaunch_mutex, init_mutex; + char *address = NULL; + wchar_t gio_path[MAX_PATH+1+200]; + + autolaunch_mutex = acquire_mutex (DBUS_AUTOLAUNCH_MUTEX); + + init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX); + + if (is_mutex_owned (DBUS_DAEMON_MUTEX)) + address = read_shm (DBUS_DAEMON_ADDRESS_INFO); + + release_mutex (init_mutex); + + if (address == NULL) + { + gio_path[MAX_PATH] = 0; + if (GetModuleFileNameW (_g_io_win32_get_module (), gio_path, MAX_PATH)) + { + PROCESS_INFORMATION pi = { 0 }; + STARTUPINFOW si = { 0 }; + BOOL res; + wchar_t gio_path_short[MAX_PATH]; + wchar_t rundll_path[MAX_PATH*2]; + wchar_t args[MAX_PATH*4]; + + GetShortPathNameW (gio_path, gio_path_short, MAX_PATH); + + GetWindowsDirectoryW (rundll_path, MAX_PATH); + wcscat (rundll_path, L"\\rundll32.exe"); + if (GetFileAttributesW (rundll_path) == INVALID_FILE_ATTRIBUTES) + { + GetSystemDirectoryW (rundll_path, MAX_PATH); + wcscat (rundll_path, L"\\rundll32.exe"); + } + + wcscpy (args, L"\""); + wcscat (args, rundll_path); + wcscat (args, L"\" "); + wcscat (args, gio_path_short); +#if defined(_WIN64) || defined(_M_X64) || defined(_M_AMD64) + wcscat (args, L",g_win32_run_session_bus"); +#elif defined (_MSC_VER) + wcscat (args, L",_g_win32_run_session_bus@16"); +#else + wcscat (args, L",g_win32_run_session_bus@16"); +#endif + + res = CreateProcessW (rundll_path, args, + 0, 0, FALSE, + NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | DETACHED_PROCESS, + 0, NULL /* TODO: Should be root */, + &si, &pi); + if (res) + address = read_shm (DBUS_DAEMON_ADDRESS_INFO); + } + } + + release_mutex (autolaunch_mutex); + + if (address == NULL) + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Session dbus not running, and autolaunch failed")); + + return address; +} +#endif + +/* ---------------------------------------------------------------------------------------------------- */ + static gchar * -get_session_address_platform_specific (void) +get_session_address_platform_specific (GError **error) { - return NULL; + gchar *ret; +#if defined (G_OS_UNIX) || defined(G_OS_WIN32) + /* need to handle OS X in a different way since 'dbus-launch --autolaunch' probably won't work there */ + ret = get_session_address_dbus_launch (error); +#else + /* TODO: implement for OS X */ + ret = NULL; + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Cannot determine session bus address (not implemented for this OS)")); +#endif + return ret; } /* ---------------------------------------------------------------------------------------------------- */ /** * g_dbus_address_get_for_bus_sync: - * @bus_type: A #GBusType. - * @cancellable: A #GCancellable or %NULL. - * @error: Return location for error or %NULL. + * @bus_type: a #GBusType + * @cancellable: (allow-none): a #GCancellable or %NULL + * @error: return location for error or %NULL * * Synchronously looks up the D-Bus address for the well-known message * bus instance specified by @bus_type. This may involve using various * platform specific mechanisms. * - * Returns: A valid D-Bus address string for @bus_type or %NULL if @error is set. + * Returns: a valid D-Bus address string for @bus_type or %NULL if + * @error is set * * Since: 2.26 */ @@ -960,11 +1509,42 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type, GError **error) { gchar *ret; + const gchar *system_bus; + const gchar *session_bus; const gchar *starter_bus; + GError *local_error; g_return_val_if_fail (error == NULL || *error == NULL, NULL); ret = NULL; + local_error = NULL; + + if (G_UNLIKELY (_g_dbus_debug_address ())) + { + guint n; + _g_dbus_debug_print_lock (); + g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type '%s'\n", + _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type)); + for (n = 0; n < 3; n++) + { + const gchar *k; + const gchar *v; + switch (n) + { + case 0: k = "DBUS_SESSION_BUS_ADDRESS"; break; + case 1: k = "DBUS_SYSTEM_BUS_ADDRESS"; break; + case 2: k = "DBUS_STARTER_BUS_TYPE"; break; + default: g_assert_not_reached (); + } + v = g_getenv (k); + g_print ("GDBus-debug:Address: env var %s", k); + if (v != NULL) + g_print ("='%s'\n", v); + else + g_print (" is not set\n"); + } + _g_dbus_debug_print_unlock (); + } switch (bus_type) { @@ -980,43 +1560,53 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type, ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS")); if (ret == NULL) { - ret = get_session_address_platform_specific (); - if (ret == NULL) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("Cannot determine session bus address (TODO: run dbus-launch to find out)")); - } + ret = get_session_address_platform_specific (&local_error); } break; + case G_BUS_TYPE_MACHINE: + system_bus = g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"); + if (system_bus == NULL) + ret = g_strdup ("kernel:path=/sys/fs/kdbus/0-system/bus;unix:path=/var/run/dbus/system_bus_socket"); + else + ret = g_strdup_printf ("kernel:path=/sys/fs/kdbus/0-system/bus;%s", system_bus); + break; + + case G_BUS_TYPE_USER: + session_bus = g_getenv ("DBUS_SESSION_BUS_ADDRESS"); + if (session_bus == NULL) + ret = g_strdup_printf ("kernel:path=/sys/fs/kdbus/%d-user/bus;%s", getuid(), + get_session_address_platform_specific (&local_error)); + else + ret = g_strdup_printf ("kernel:path=/sys/fs/kdbus/%d-user/bus;%s", getuid(), session_bus); + break; + case G_BUS_TYPE_STARTER: starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE"); if (g_strcmp0 (starter_bus, "session") == 0) { - ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, error); + ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, &local_error); goto out; } else if (g_strcmp0 (starter_bus, "system") == 0) { - ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, error); + ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, &local_error); goto out; } else { if (starter_bus != NULL) { - g_set_error (error, + g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable" - " - unknown value `%s'"), + " - unknown value '%s'"), starter_bus); } else { - g_set_error_literal (error, + g_set_error_literal (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment " @@ -1026,7 +1616,7 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type, break; default: - g_set_error (error, + g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Unknown bus type %d"), @@ -1035,8 +1625,73 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type, } out: + if (G_UNLIKELY (_g_dbus_debug_address ())) + { + _g_dbus_debug_print_lock (); + if (ret != NULL) + { + g_print ("GDBus-debug:Address: Returning address '%s' for bus type '%s'\n", + ret, + _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type)); + } + else + { + g_print ("GDBus-debug:Address: Cannot look-up address bus type '%s': %s\n", + _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type), + local_error ? local_error->message : ""); + } + _g_dbus_debug_print_unlock (); + } + + if (local_error != NULL) + g_propagate_error (error, local_error); + return ret; } -#define __G_DBUS_ADDRESS_C__ -#include "gioaliasdef.c" +/** + * g_dbus_address_escape_value: + * @string: an unescaped string to be included in a D-Bus address + * as the value in a key-value pair + * + * Escape @string so it can appear in a D-Bus address as the value + * part of a key-value pair. + * + * For instance, if @string is "/run/bus-for-:0", + * this function would return "/run/bus-for-%3A0", + * which could be used in a D-Bus address like + * "unix:nonce-tcp:host=127.0.0.1,port=42,noncefile=/run/bus-for-%3A0". + * + * Returns: (transfer full): a copy of @string with all + * non-optionally-escaped bytes escaped + * + * Since: 2.36 + */ +gchar * +g_dbus_address_escape_value (const gchar *string) +{ + GString *s; + gsize i; + + g_return_val_if_fail (string != NULL, NULL); + + /* There will often not be anything needing escaping at all. */ + s = g_string_sized_new (strlen (string)); + + /* D-Bus address escaping is mostly the same as URI escaping... */ + g_string_append_uri_escaped (s, string, "\\/", FALSE); + + /* ... but '~' is an unreserved character in URIs, but a + * non-optionally-escaped character in D-Bus addresses. */ + for (i = 0; i < s->len; i++) + { + if (G_UNLIKELY (s->str[i] == '~')) + { + s->str[i] = '%'; + g_string_insert (s, i + 1, "7E"); + i += 2; + } + } + + return g_string_free (s, FALSE); +}