mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-31 08:22:16 +01:00 
			
		
		
		
	This adds cross-platform support for it: on glibc, musl and BSD’s libc, the flag is natively supported. On Windows, convert it to the `N` flag, which similarly indicates that an open file shouldn’t be inherited by child processes. This allows us to unconditionally pass `e` to `g_fopen()` so `O_CLOEXEC` can easily be set on its FDs. Also do the same for `g_freopen()`, since it shares the same underlying mode handling code. Signed-off-by: Philip Withnall <pwithnall@gnome.org>
		
			
				
	
	
		
			2688 lines
		
	
	
		
			79 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2688 lines
		
	
	
		
			79 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* GDBus - GLib D-Bus Library
 | ||
|  *
 | ||
|  * Copyright (C) 2008-2010 Red Hat, Inc.
 | ||
|  *
 | ||
|  * SPDX-License-Identifier: LGPL-2.1-or-later
 | ||
|  *
 | ||
|  * This library is free software; you can redistribute it and/or
 | ||
|  * modify it under the terms of the GNU Lesser General Public
 | ||
|  * License as published by the Free Software Foundation; either
 | ||
|  * version 2.1 of the License, or (at your option) any later version.
 | ||
|  *
 | ||
|  * This library is distributed in the hope that it will be useful,
 | ||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||
|  * 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, see <http://www.gnu.org/licenses/>.
 | ||
|  *
 | ||
|  * Author: David Zeuthen <davidz@redhat.com>
 | ||
|  */
 | ||
| 
 | ||
| #include "config.h"
 | ||
| 
 | ||
| #include <stdlib.h>
 | ||
| #include <string.h>
 | ||
| #include <stdio.h>
 | ||
| #include <locale.h>
 | ||
| 
 | ||
| #include <gio/gio.h>
 | ||
| 
 | ||
| #ifdef G_OS_UNIX
 | ||
| #include <gio/gunixfdlist.h>
 | ||
| #endif
 | ||
| 
 | ||
| #include <gi18n.h>
 | ||
| #include <glib/gstdio.h>
 | ||
| 
 | ||
| #ifdef G_OS_WIN32
 | ||
| #include "glib/glib-private.h"
 | ||
| #endif
 | ||
| 
 | ||
| #include "gdbusprivate.h"
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| /* Escape values for console colors */
 | ||
| #define UNDERLINE     "\033[4m"
 | ||
| #define BLUE          "\033[34m"
 | ||
| #define CYAN          "\033[36m"
 | ||
| #define GREEN         "\033[32m"
 | ||
| #define MAGENTA       "\033[35m"
 | ||
| #define RED           "\033[31m"
 | ||
| #define YELLOW        "\033[33m"
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| G_GNUC_UNUSED static void completion_debug (const gchar *format, ...);
 | ||
| 
 | ||
| /* Uncomment to get debug traces in /tmp/gdbus-completion-debug.txt (nice
 | ||
|  * to not have it interfere with stdout/stderr)
 | ||
|  */
 | ||
| #if 0
 | ||
| G_GNUC_UNUSED static void
 | ||
| completion_debug (const gchar *format, ...)
 | ||
| {
 | ||
|   va_list var_args;
 | ||
|   gchar *s;
 | ||
|   static FILE *f = NULL;
 | ||
| 
 | ||
|   va_start (var_args, format);
 | ||
|   s = g_strdup_vprintf (format, var_args);
 | ||
|   if (f == NULL)
 | ||
|     {
 | ||
|       f = g_fopen ("/tmp/gdbus-completion-debug.txt", "a+e");
 | ||
|     }
 | ||
|   fprintf (f, "%s\n", s);
 | ||
|   g_free (s);
 | ||
| }
 | ||
| #else
 | ||
| static void
 | ||
| completion_debug (const gchar *format, ...)
 | ||
| {
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| 
 | ||
| static void
 | ||
| remove_arg (gint num, gint *argc, gchar **argv[])
 | ||
| {
 | ||
|   gint n;
 | ||
| 
 | ||
|   g_assert (num <= (*argc));
 | ||
| 
 | ||
|   for (n = num; (*argv)[n] != NULL; n++)
 | ||
|     (*argv)[n] = (*argv)[n+1];
 | ||
|   (*argv)[n] = NULL;
 | ||
|   (*argc) = (*argc) - 1;
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| usage (gint *argc, gchar **argv[], gboolean use_stdout)
 | ||
| {
 | ||
|   GOptionContext *o;
 | ||
|   gchar *s;
 | ||
|   gchar *program_name;
 | ||
| 
 | ||
|   o = g_option_context_new (_("COMMAND"));
 | ||
|   g_option_context_set_help_enabled (o, FALSE);
 | ||
|   /* Ignore parsing result */
 | ||
|   g_option_context_parse (o, argc, argv, NULL);
 | ||
|   program_name = (*argc > 0) ? g_path_get_basename ((*argv)[0]) : g_strdup ("gdbus-tool");
 | ||
|   s = g_strdup_printf (_("Commands:\n"
 | ||
|                          "  help         Shows this information\n"
 | ||
|                          "  introspect   Introspect a remote object\n"
 | ||
|                          "  monitor      Monitor a remote object\n"
 | ||
|                          "  call         Invoke a method on a remote object\n"
 | ||
|                          "  emit         Emit a signal\n"
 | ||
|                          "  wait         Wait for a bus name to appear\n"
 | ||
|                          "\n"
 | ||
|                          "Use “%s COMMAND --help” to get help on each command.\n"),
 | ||
|                        program_name);
 | ||
|   g_free (program_name);
 | ||
|   g_option_context_set_description (o, s);
 | ||
|   g_free (s);
 | ||
|   s = g_option_context_get_help (o, FALSE, NULL);
 | ||
|   if (use_stdout)
 | ||
|     g_print ("%s", s);
 | ||
|   else
 | ||
|     g_printerr ("%s", s);
 | ||
|   g_free (s);
 | ||
|   g_option_context_free (o);
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| modify_argv0_for_command (gint *argc, gchar **argv[], const gchar *command)
 | ||
| {
 | ||
|   gchar *s;
 | ||
|   gchar *program_name;
 | ||
| 
 | ||
|   /* TODO:
 | ||
|    *  1. get a g_set_prgname() ?; or
 | ||
|    *  2. save old argv[0] and restore later
 | ||
|    */
 | ||
| 
 | ||
|   g_assert (*argc > 1);
 | ||
|   g_assert (g_strcmp0 ((*argv)[1], command) == 0);
 | ||
|   remove_arg (1, argc, argv);
 | ||
| 
 | ||
|   program_name = g_path_get_basename ((*argv)[0]);
 | ||
|   s = g_strdup_printf ("%s %s", program_name, command);
 | ||
|   (*argv)[0] = s;
 | ||
|   g_free (program_name);
 | ||
| }
 | ||
| 
 | ||
| static GOptionContext *
 | ||
| command_option_context_new (const gchar        *parameter_string,
 | ||
|                             const gchar        *summary,
 | ||
|                             const GOptionEntry *entries,
 | ||
|                             gboolean            request_completion)
 | ||
| {
 | ||
|   GOptionContext *o = NULL;
 | ||
| 
 | ||
|   o = g_option_context_new (parameter_string);
 | ||
|   if (request_completion)
 | ||
|     g_option_context_set_ignore_unknown_options (o, TRUE);
 | ||
|   g_option_context_set_help_enabled (o, FALSE);
 | ||
|   g_option_context_set_summary (o, summary);
 | ||
|   g_option_context_add_main_entries (o, entries, GETTEXT_PACKAGE);
 | ||
| 
 | ||
|   return g_steal_pointer (&o);
 | ||
| }
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| static void
 | ||
| print_methods_and_signals (GDBusConnection *c,
 | ||
|                            const gchar     *name,
 | ||
|                            const gchar     *path,
 | ||
|                            gboolean         print_methods,
 | ||
|                            gboolean         print_signals)
 | ||
| {
 | ||
|   GVariant *result;
 | ||
|   GError *error;
 | ||
|   const gchar *xml_data;
 | ||
|   GDBusNodeInfo *node;
 | ||
|   guint n;
 | ||
|   guint m;
 | ||
| 
 | ||
|   error = NULL;
 | ||
|   result = g_dbus_connection_call_sync (c,
 | ||
|                                         name,
 | ||
|                                         path,
 | ||
|                                         DBUS_INTERFACE_INTROSPECTABLE,
 | ||
|                                         "Introspect",
 | ||
|                                         NULL,
 | ||
|                                         G_VARIANT_TYPE ("(s)"),
 | ||
|                                         G_DBUS_CALL_FLAGS_NONE,
 | ||
|                                         3000, /* 3 secs */
 | ||
|                                         NULL,
 | ||
|                                         &error);
 | ||
|   if (result == NULL)
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s\n"), error->message);
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   g_variant_get (result, "(&s)", &xml_data);
 | ||
| 
 | ||
|   error = NULL;
 | ||
|   node = g_dbus_node_info_new_for_xml (xml_data, &error);
 | ||
|   g_variant_unref (result);
 | ||
|   if (node == NULL)
 | ||
|     {
 | ||
|       g_printerr (_("Error parsing introspection XML: %s\n"), error->message);
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   for (n = 0; node->interfaces != NULL && node->interfaces[n] != NULL; n++)
 | ||
|     {
 | ||
|       const GDBusInterfaceInfo *iface = node->interfaces[n];
 | ||
|       for (m = 0; print_methods && iface->methods != NULL && iface->methods[m] != NULL; m++)
 | ||
|         {
 | ||
|           const GDBusMethodInfo *method = iface->methods[m];
 | ||
|           g_print ("%s.%s \n", iface->name, method->name);
 | ||
|         }
 | ||
|       for (m = 0; print_signals && iface->signals != NULL && iface->signals[m] != NULL; m++)
 | ||
|         {
 | ||
|           const GDBusSignalInfo *signal = iface->signals[m];
 | ||
|           g_print ("%s.%s \n", iface->name, signal->name);
 | ||
|         }
 | ||
|     }
 | ||
|   g_dbus_node_info_unref (node);
 | ||
| 
 | ||
|  out:
 | ||
|   ;
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| print_paths (GDBusConnection *c,
 | ||
|              const gchar *name,
 | ||
|              const gchar *path)
 | ||
| {
 | ||
|   GVariant *result;
 | ||
|   GError *error;
 | ||
|   const gchar *xml_data;
 | ||
|   GDBusNodeInfo *node;
 | ||
|   guint n;
 | ||
| 
 | ||
|   if (!g_dbus_is_name (name))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid name\n"), name);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (!g_variant_is_object_path (path))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid object path\n"), path);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   error = NULL;
 | ||
|   result = g_dbus_connection_call_sync (c,
 | ||
|                                         name,
 | ||
|                                         path,
 | ||
|                                         DBUS_INTERFACE_INTROSPECTABLE,
 | ||
|                                         "Introspect",
 | ||
|                                         NULL,
 | ||
|                                         G_VARIANT_TYPE ("(s)"),
 | ||
|                                         G_DBUS_CALL_FLAGS_NONE,
 | ||
|                                         3000, /* 3 secs */
 | ||
|                                         NULL,
 | ||
|                                         &error);
 | ||
|   if (result == NULL)
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s\n"), error->message);
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   g_variant_get (result, "(&s)", &xml_data);
 | ||
| 
 | ||
|   //g_printerr ("xml='%s'", xml_data);
 | ||
| 
 | ||
|   error = NULL;
 | ||
|   node = g_dbus_node_info_new_for_xml (xml_data, &error);
 | ||
|   g_variant_unref (result);
 | ||
|   if (node == NULL)
 | ||
|     {
 | ||
|       g_printerr (_("Error parsing introspection XML: %s\n"), error->message);
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   //g_printerr ("bar '%s'\n", path);
 | ||
| 
 | ||
|   if (node->interfaces != NULL)
 | ||
|     g_print ("%s \n", path);
 | ||
| 
 | ||
|   for (n = 0; node->nodes != NULL && node->nodes[n] != NULL; n++)
 | ||
|     {
 | ||
|       gchar *s;
 | ||
| 
 | ||
|       //g_printerr ("foo '%s'\n", node->nodes[n].path);
 | ||
| 
 | ||
|       if (g_strcmp0 (path, "/") == 0)
 | ||
|         s = g_strdup_printf ("/%s", node->nodes[n]->path);
 | ||
|       else
 | ||
|         s = g_strdup_printf ("%s/%s", path, node->nodes[n]->path);
 | ||
| 
 | ||
|       print_paths (c, name, s);
 | ||
| 
 | ||
|       g_free (s);
 | ||
|     }
 | ||
|   g_dbus_node_info_unref (node);
 | ||
| 
 | ||
|  out:
 | ||
|   ;
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| print_names (GDBusConnection *c,
 | ||
|              gboolean         include_unique_names)
 | ||
| {
 | ||
|   GVariant *result;
 | ||
|   GError *error;
 | ||
|   GVariantIter *iter;
 | ||
|   gchar *str;
 | ||
|   GHashTable *name_set;
 | ||
|   GPtrArray *keys;
 | ||
| 
 | ||
|   name_set = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 | ||
| 
 | ||
|   error = NULL;
 | ||
|   result = g_dbus_connection_call_sync (c,
 | ||
|                                         DBUS_SERVICE_DBUS,
 | ||
|                                         DBUS_PATH_DBUS,
 | ||
|                                         DBUS_INTERFACE_DBUS,
 | ||
|                                         "ListNames",
 | ||
|                                         NULL,
 | ||
|                                         G_VARIANT_TYPE ("(as)"),
 | ||
|                                         G_DBUS_CALL_FLAGS_NONE,
 | ||
|                                         3000, /* 3 secs */
 | ||
|                                         NULL,
 | ||
|                                         &error);
 | ||
|   if (result == NULL)
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s\n"), error->message);
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   g_variant_get (result, "(as)", &iter);
 | ||
|   while (g_variant_iter_loop (iter, "s", &str))
 | ||
|     g_hash_table_add (name_set, g_strdup (str));
 | ||
|   g_variant_iter_free (iter);
 | ||
|   g_variant_unref (result);
 | ||
| 
 | ||
|   error = NULL;
 | ||
|   result = g_dbus_connection_call_sync (c,
 | ||
|                                         DBUS_SERVICE_DBUS,
 | ||
|                                         DBUS_PATH_DBUS,
 | ||
|                                         DBUS_INTERFACE_DBUS,
 | ||
|                                         "ListActivatableNames",
 | ||
|                                         NULL,
 | ||
|                                         G_VARIANT_TYPE ("(as)"),
 | ||
|                                         G_DBUS_CALL_FLAGS_NONE,
 | ||
|                                         3000, /* 3 secs */
 | ||
|                                         NULL,
 | ||
|                                         &error);
 | ||
|   if (result == NULL)
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s\n"), error->message);
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   g_variant_get (result, "(as)", &iter);
 | ||
|   while (g_variant_iter_loop (iter, "s", &str))
 | ||
|     g_hash_table_add (name_set, g_strdup (str));
 | ||
|   g_variant_iter_free (iter);
 | ||
|   g_variant_unref (result);
 | ||
| 
 | ||
|   keys = g_hash_table_steal_all_keys (name_set);
 | ||
|   g_ptr_array_sort_values (keys, (GCompareFunc) g_strcmp0);
 | ||
|   for (guint i = 0; i < keys->len; ++i)
 | ||
|     {
 | ||
|       const gchar *name = g_ptr_array_index (keys, i);
 | ||
|       if (!include_unique_names && g_str_has_prefix (name, ":"))
 | ||
|         continue;
 | ||
| 
 | ||
|       g_print ("%s \n", name);
 | ||
|     }
 | ||
|   g_clear_pointer (&keys, g_ptr_array_unref);
 | ||
| 
 | ||
|  out:
 | ||
|   g_hash_table_unref (name_set);
 | ||
| }
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| static gboolean  opt_connection_system  = FALSE;
 | ||
| static gboolean  opt_connection_session = FALSE;
 | ||
| static gchar    *opt_connection_address = NULL;
 | ||
| 
 | ||
| static const GOptionEntry connection_entries[] =
 | ||
| {
 | ||
|   { "system", 'y', 0, G_OPTION_ARG_NONE, &opt_connection_system, N_("Connect to the system bus"), NULL},
 | ||
|   { "session", 'e', 0, G_OPTION_ARG_NONE, &opt_connection_session, N_("Connect to the session bus"), NULL},
 | ||
|   { "address", 'a', 0, G_OPTION_ARG_STRING, &opt_connection_address, N_("Connect to given D-Bus address"), N_("ADDRESS") },
 | ||
|   G_OPTION_ENTRY_NULL
 | ||
| };
 | ||
| 
 | ||
| static GOptionGroup *
 | ||
| connection_get_group (void)
 | ||
| {
 | ||
|   static GOptionGroup *g;
 | ||
| 
 | ||
|   g = g_option_group_new ("connection",
 | ||
|                           N_("Connection Endpoint Options:"),
 | ||
|                           N_("Options specifying the connection endpoint"),
 | ||
|                           NULL,
 | ||
|                           NULL);
 | ||
|   g_option_group_set_translation_domain (g, GETTEXT_PACKAGE);
 | ||
|   g_option_group_add_entries (g, connection_entries);
 | ||
| 
 | ||
|   return g;
 | ||
| }
 | ||
| 
 | ||
| static GDBusConnection *
 | ||
| connection_get_dbus_connection (gboolean   require_message_bus,
 | ||
|                                 GError   **error)
 | ||
| {
 | ||
|   GDBusConnection *c;
 | ||
| 
 | ||
|   c = NULL;
 | ||
| 
 | ||
|   /* First, ensure we have exactly one connect */
 | ||
|   if (!opt_connection_system && !opt_connection_session && opt_connection_address == NULL)
 | ||
|     {
 | ||
|       g_set_error (error,
 | ||
|                    G_IO_ERROR,
 | ||
|                    G_IO_ERROR_FAILED,
 | ||
|                    _("No connection endpoint specified"));
 | ||
|       goto out;
 | ||
|     }
 | ||
|   else if ((opt_connection_system && (opt_connection_session || opt_connection_address != NULL)) ||
 | ||
|            (opt_connection_session && (opt_connection_system || opt_connection_address != NULL)) ||
 | ||
|            (opt_connection_address != NULL && (opt_connection_system || opt_connection_session)))
 | ||
|     {
 | ||
|       g_set_error (error,
 | ||
|                    G_IO_ERROR,
 | ||
|                    G_IO_ERROR_FAILED,
 | ||
|                    _("Multiple connection endpoints specified"));
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (opt_connection_system)
 | ||
|     {
 | ||
|       c = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
 | ||
|     }
 | ||
|   else if (opt_connection_session)
 | ||
|     {
 | ||
|       c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
 | ||
|     }
 | ||
|   else if (opt_connection_address != NULL)
 | ||
|     {
 | ||
|       GDBusConnectionFlags flags = G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT;
 | ||
|       if (require_message_bus)
 | ||
|         flags |= G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION;
 | ||
|       c = g_dbus_connection_new_for_address_sync (opt_connection_address,
 | ||
|                                                   flags,
 | ||
|                                                   NULL, /* GDBusAuthObserver */
 | ||
|                                                   NULL, /* GCancellable */
 | ||
|                                                   error);
 | ||
|     }
 | ||
| 
 | ||
|  out:
 | ||
|   return c;
 | ||
| }
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| static GPtrArray *
 | ||
| call_helper_get_method_in_signature (GDBusConnection  *c,
 | ||
|                                      const gchar      *dest,
 | ||
|                                      const gchar      *path,
 | ||
|                                      const gchar      *interface_name,
 | ||
|                                      const gchar      *method_name,
 | ||
|                                      GError          **error)
 | ||
| {
 | ||
|   GPtrArray *ret;
 | ||
|   GVariant *result;
 | ||
|   GDBusNodeInfo *node_info;
 | ||
|   const gchar *xml_data;
 | ||
|   GDBusInterfaceInfo *interface_info;
 | ||
|   GDBusMethodInfo *method_info;
 | ||
|   guint n;
 | ||
| 
 | ||
|   ret = NULL;
 | ||
|   result = NULL;
 | ||
|   node_info = NULL;
 | ||
| 
 | ||
|   result = g_dbus_connection_call_sync (c,
 | ||
|                                         dest,
 | ||
|                                         path,
 | ||
|                                         DBUS_INTERFACE_INTROSPECTABLE,
 | ||
|                                         "Introspect",
 | ||
|                                         NULL,
 | ||
|                                         G_VARIANT_TYPE ("(s)"),
 | ||
|                                         G_DBUS_CALL_FLAGS_NONE,
 | ||
|                                         3000, /* 3 secs */
 | ||
|                                         NULL,
 | ||
|                                         error);
 | ||
|   if (result == NULL)
 | ||
|     goto out;
 | ||
| 
 | ||
|   g_variant_get (result, "(&s)", &xml_data);
 | ||
|   node_info = g_dbus_node_info_new_for_xml (xml_data, error);
 | ||
|   if (node_info == NULL)
 | ||
|       goto out;
 | ||
| 
 | ||
|   interface_info = g_dbus_node_info_lookup_interface (node_info, interface_name);
 | ||
|   if (interface_info == NULL)
 | ||
|     {
 | ||
|       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
 | ||
|                    _("Warning: According to introspection data, interface “%s” does not exist\n"),
 | ||
|                    interface_name);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   method_info = g_dbus_interface_info_lookup_method (interface_info, method_name);
 | ||
|   if (method_info == NULL)
 | ||
|     {
 | ||
|       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
 | ||
|                    _("Warning: According to introspection data, method “%s” does not exist on interface “%s”\n"),
 | ||
|                    method_name,
 | ||
|                    interface_name);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   ret = g_ptr_array_new_with_free_func ((GDestroyNotify) g_variant_type_free);
 | ||
|   for (n = 0; method_info->in_args != NULL && method_info->in_args[n] != NULL; n++)
 | ||
|     {
 | ||
|       g_ptr_array_add (ret, g_variant_type_new (method_info->in_args[n]->signature));
 | ||
|     }
 | ||
| 
 | ||
|  out:
 | ||
|   if (node_info != NULL)
 | ||
|     g_dbus_node_info_unref (node_info);
 | ||
|   if (result != NULL)
 | ||
|     g_variant_unref (result);
 | ||
| 
 | ||
|   return ret;
 | ||
| }
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| static GVariant *
 | ||
| _g_variant_parse_me_harder (GVariantType   *type,
 | ||
|                             const gchar    *given_str,
 | ||
|                             GError        **error)
 | ||
| {
 | ||
|   GVariant *value;
 | ||
|   gchar *s;
 | ||
|   guint n;
 | ||
|   GString *str;
 | ||
| 
 | ||
|   str = g_string_new ("\"");
 | ||
|   for (n = 0; given_str[n] != '\0'; n++)
 | ||
|     {
 | ||
|       if (G_UNLIKELY (given_str[n] == '\"'))
 | ||
|         g_string_append (str, "\\\"");
 | ||
|       else
 | ||
|         g_string_append_c (str, given_str[n]);
 | ||
|     }
 | ||
|   g_string_append_c (str, '"');
 | ||
|   s = g_string_free (str, FALSE);
 | ||
| 
 | ||
|   value = g_variant_parse (type,
 | ||
|                            s,
 | ||
|                            NULL,
 | ||
|                            NULL,
 | ||
|                            error);
 | ||
|   g_free (s);
 | ||
| 
 | ||
|   return value;
 | ||
| }
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| #ifdef G_OS_UNIX
 | ||
| static gboolean
 | ||
| walk_variant_for_handle (GVariantBuilder  *builder,
 | ||
|                          GUnixFDList      *fd_list,
 | ||
|                          GVariant         *value)
 | ||
| {
 | ||
|   g_assert (!g_variant_is_floating (value));
 | ||
| 
 | ||
|   if (g_variant_is_container (value))
 | ||
|     {
 | ||
|       gboolean res = TRUE;
 | ||
|       GVariantIter iter;
 | ||
|       GVariant *child;
 | ||
| 
 | ||
|       g_variant_iter_init (&iter, value);
 | ||
| 
 | ||
|       g_variant_builder_open (builder, g_variant_get_type (value));
 | ||
| 
 | ||
|       while ((child = g_variant_iter_next_value (&iter)) && res)
 | ||
|         {
 | ||
|           res = walk_variant_for_handle (builder, fd_list, child);
 | ||
|           g_variant_unref (child);
 | ||
|         }
 | ||
| 
 | ||
|       g_variant_builder_close (builder);
 | ||
| 
 | ||
|       if (!res)
 | ||
|         return FALSE;
 | ||
|     }
 | ||
|   else if (g_variant_is_of_type (value, G_VARIANT_TYPE_HANDLE))
 | ||
|     {
 | ||
|       GError *error = NULL;
 | ||
|       int fd_id = -1;
 | ||
| 
 | ||
|       if ((fd_id = g_unix_fd_list_append (fd_list, g_variant_get_handle (value), &error)) < 0)
 | ||
|         {
 | ||
|           g_printerr (_("Error adding handle %d: %s\n"),
 | ||
|                         g_variant_get_handle (value), error->message);
 | ||
|           g_error_free (error);
 | ||
|           return FALSE;
 | ||
|         }
 | ||
| 
 | ||
|       g_variant_builder_add_value (builder, g_variant_new_handle (fd_id));
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       g_variant_builder_add_value (builder, value);
 | ||
|     }
 | ||
| 
 | ||
|   return TRUE;
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| static gchar *opt_emit_dest = NULL;
 | ||
| static gchar *opt_emit_object_path = NULL;
 | ||
| static gchar *opt_emit_signal = NULL;
 | ||
| 
 | ||
| static const GOptionEntry emit_entries[] =
 | ||
| {
 | ||
|   { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_emit_dest, N_("Optional destination for signal (unique name)"), NULL},
 | ||
|   { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_emit_object_path, N_("Object path to emit signal on"), NULL},
 | ||
|   { "signal", 's', 0, G_OPTION_ARG_STRING, &opt_emit_signal, N_("Signal and interface name"), NULL},
 | ||
|   G_OPTION_ENTRY_NULL
 | ||
| };
 | ||
| 
 | ||
| static gboolean
 | ||
| handle_emit (gint        *argc,
 | ||
|              gchar      **argv[],
 | ||
|              gboolean     request_completion,
 | ||
|              const gchar *completion_cur,
 | ||
|              const gchar *completion_prev)
 | ||
| {
 | ||
|   gint ret;
 | ||
|   GOptionContext *o;
 | ||
|   gchar *s;
 | ||
|   GError *error;
 | ||
|   GDBusConnection *c;
 | ||
|   GVariant *parameters;
 | ||
|   gchar *interface_name;
 | ||
|   gchar *signal_name;
 | ||
|   GVariantBuilder builder;
 | ||
|   gboolean skip_dashes;
 | ||
|   guint parm;
 | ||
|   guint n;
 | ||
|   gboolean complete_names, complete_paths, complete_signals;
 | ||
| 
 | ||
|   ret = FALSE;
 | ||
|   c = NULL;
 | ||
|   parameters = NULL;
 | ||
|   interface_name = NULL;
 | ||
|   signal_name = NULL;
 | ||
| 
 | ||
|   modify_argv0_for_command (argc, argv, "emit");
 | ||
| 
 | ||
|   o = command_option_context_new (NULL, _("Emit a signal."),
 | ||
|                                   emit_entries, request_completion);
 | ||
|   g_option_context_add_group (o, connection_get_group ());
 | ||
| 
 | ||
|   complete_names = FALSE;
 | ||
|   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0)
 | ||
|     {
 | ||
|       complete_names = TRUE;
 | ||
|       remove_arg ((*argc) - 1, argc, argv);
 | ||
|     }
 | ||
| 
 | ||
|   complete_paths = FALSE;
 | ||
|   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0)
 | ||
|     {
 | ||
|       complete_paths = TRUE;
 | ||
|       remove_arg ((*argc) - 1, argc, argv);
 | ||
|     }
 | ||
| 
 | ||
|   complete_signals = FALSE;
 | ||
|   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--signal") == 0)
 | ||
|     {
 | ||
|       complete_signals = TRUE;
 | ||
|       remove_arg ((*argc) - 1, argc, argv);
 | ||
|     }
 | ||
| 
 | ||
|   if (!g_option_context_parse (o, argc, argv, NULL))
 | ||
|     {
 | ||
|       if (!request_completion)
 | ||
|         {
 | ||
|           s = g_option_context_get_help (o, FALSE, NULL);
 | ||
|           g_printerr ("%s", s);
 | ||
|           g_free (s);
 | ||
|           goto out;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|   error = NULL;
 | ||
|   c = connection_get_dbus_connection ((opt_emit_dest != NULL), &error);
 | ||
|   if (c == NULL)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         {
 | ||
|           if (g_strcmp0 (completion_prev, "--address") == 0)
 | ||
|             {
 | ||
|               g_print ("unix:\n"
 | ||
|                        "tcp:\n"
 | ||
|                        "nonce-tcp:\n");
 | ||
|             }
 | ||
|           else
 | ||
|             {
 | ||
|               g_print ("--system \n--session \n--address \n");
 | ||
|             }
 | ||
|         }
 | ||
|       else
 | ||
|         {
 | ||
|           g_printerr (_("Error connecting: %s\n"), error->message);
 | ||
|         }
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   /* validate and complete destination (bus name) */
 | ||
|   if (complete_names)
 | ||
|     {
 | ||
|       print_names (c, FALSE);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (request_completion && opt_emit_dest != NULL && g_strcmp0 ("--dest", completion_prev) == 0)
 | ||
|     {
 | ||
|       print_names (c, g_str_has_prefix (opt_emit_dest, ":"));
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (!request_completion && opt_emit_dest != NULL && !g_dbus_is_unique_name (opt_emit_dest))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid unique bus name.\n"), opt_emit_dest);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (opt_emit_dest == NULL && opt_emit_object_path == NULL && request_completion)
 | ||
|     {
 | ||
|       g_print ("--dest \n");
 | ||
|     }
 | ||
|   /* validate and complete object path */
 | ||
|   if (opt_emit_dest != NULL && complete_paths)
 | ||
|     {
 | ||
|       print_paths (c, opt_emit_dest, "/");
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (opt_emit_object_path == NULL)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         g_print ("--object-path \n");
 | ||
|       else
 | ||
|         g_printerr (_("Error: Object path is not specified\n"));
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (request_completion && g_strcmp0 ("--object-path", completion_prev) == 0)
 | ||
|     {
 | ||
|       if (opt_emit_dest != NULL)
 | ||
|         {
 | ||
|           gchar *p;
 | ||
|           s = g_strdup (opt_emit_object_path);
 | ||
|           p = strrchr (s, '/');
 | ||
|           if (p != NULL)
 | ||
|             {
 | ||
|               if (p == s)
 | ||
|                 p++;
 | ||
|               *p = '\0';
 | ||
|             }
 | ||
|           print_paths (c, opt_emit_dest, s);
 | ||
|           g_free (s);
 | ||
|         }
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (!request_completion && !g_variant_is_object_path (opt_emit_object_path))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid object path\n"), opt_emit_object_path);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   /* validate and complete signal (interface + signal name) */
 | ||
|   if (opt_emit_dest != NULL && opt_emit_object_path != NULL && complete_signals)
 | ||
|     {
 | ||
|       print_methods_and_signals (c, opt_emit_dest, opt_emit_object_path, FALSE, TRUE);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (opt_emit_signal == NULL)
 | ||
|     {
 | ||
|       /* don't keep repeatedly completing --signal */
 | ||
|       if (request_completion)
 | ||
|         {
 | ||
|           if (g_strcmp0 ("--signal", completion_prev) != 0)
 | ||
|             g_print ("--signal \n");
 | ||
|         }
 | ||
|       else
 | ||
|         {
 | ||
|           g_printerr (_("Error: Signal name is not specified\n"));
 | ||
|         }
 | ||
| 
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (request_completion && opt_emit_dest != NULL && opt_emit_object_path != NULL &&
 | ||
|       g_strcmp0 ("--signal", completion_prev) == 0)
 | ||
|     {
 | ||
|       print_methods_and_signals (c, opt_emit_dest, opt_emit_object_path, FALSE, TRUE);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   s = strrchr (opt_emit_signal, '.');
 | ||
|   if (!request_completion && s == NULL)
 | ||
|     {
 | ||
|       g_printerr (_("Error: Signal name “%s” is invalid\n"), opt_emit_signal);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   signal_name = g_strdup (s + 1);
 | ||
|   interface_name = g_strndup (opt_emit_signal, s - opt_emit_signal);
 | ||
| 
 | ||
|   /* All done with completion now */
 | ||
|   if (request_completion)
 | ||
|     goto out;
 | ||
| 
 | ||
|   if (!g_dbus_is_interface_name (interface_name))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid interface name\n"), interface_name);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (!g_dbus_is_member_name (signal_name))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid member name\n"), signal_name);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   /* Read parameters */
 | ||
|   g_variant_builder_init_static (&builder, G_VARIANT_TYPE_TUPLE);
 | ||
|   skip_dashes = TRUE;
 | ||
|   parm = 0;
 | ||
|   for (n = 1; n < (guint) *argc; n++)
 | ||
|     {
 | ||
|       GVariant *value;
 | ||
| 
 | ||
|       /* Under certain conditions, g_option_context_parse returns the "--"
 | ||
|          itself (setting off unparsed arguments), too: */
 | ||
|       if (skip_dashes && g_strcmp0 ((*argv)[n], "--") == 0)
 | ||
|         {
 | ||
|           skip_dashes = FALSE;
 | ||
|           continue;
 | ||
|         }
 | ||
| 
 | ||
|       error = NULL;
 | ||
|       value = g_variant_parse (NULL,
 | ||
|                                (*argv)[n],
 | ||
|                                NULL,
 | ||
|                                NULL,
 | ||
|                                &error);
 | ||
|       if (value == NULL)
 | ||
|         {
 | ||
|           gchar *context;
 | ||
| 
 | ||
|           context = g_variant_parse_error_print_context (error, (*argv)[n]);
 | ||
|           g_error_free (error);
 | ||
|           error = NULL;
 | ||
|           value = _g_variant_parse_me_harder (NULL, (*argv)[n], &error);
 | ||
|           if (value == NULL)
 | ||
|             {
 | ||
|               /* Use the original non-"parse-me-harder" error */
 | ||
|               g_printerr (_("Error parsing parameter %d: %s\n"),
 | ||
|                           parm + 1,
 | ||
|                           context);
 | ||
|               g_error_free (error);
 | ||
|               g_free (context);
 | ||
|               g_variant_builder_clear (&builder);
 | ||
|               goto out;
 | ||
|             }
 | ||
|           g_free (context);
 | ||
|         }
 | ||
|       g_variant_builder_add_value (&builder, value);
 | ||
|       ++parm;
 | ||
|     }
 | ||
|   parameters = g_variant_builder_end (&builder);
 | ||
| 
 | ||
|   if (parameters != NULL)
 | ||
|     parameters = g_variant_ref_sink (parameters);
 | ||
|   if (!g_dbus_connection_emit_signal (c,
 | ||
|                                       opt_emit_dest,
 | ||
|                                       opt_emit_object_path,
 | ||
|                                       interface_name,
 | ||
|                                       signal_name,
 | ||
|                                       parameters,
 | ||
|                                       &error))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s\n"), error->message);
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (!g_dbus_connection_flush_sync (c, NULL, &error))
 | ||
|     {
 | ||
|       g_printerr (_("Error flushing connection: %s\n"), error->message);
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   ret = TRUE;
 | ||
| 
 | ||
|  out:
 | ||
|   if (c != NULL)
 | ||
|     g_object_unref (c);
 | ||
|   if (parameters != NULL)
 | ||
|     g_variant_unref (parameters);
 | ||
|   g_free (interface_name);
 | ||
|   g_free (signal_name);
 | ||
|   g_option_context_free (o);
 | ||
|   return ret;
 | ||
| }
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| static gchar *opt_call_dest = NULL;
 | ||
| static gchar *opt_call_object_path = NULL;
 | ||
| static gchar *opt_call_method = NULL;
 | ||
| static gint opt_call_timeout = -1;
 | ||
| static gboolean opt_call_interactive = FALSE;
 | ||
| 
 | ||
| static const GOptionEntry call_entries[] =
 | ||
| {
 | ||
|   { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_call_dest, N_("Destination name to invoke method on"), NULL},
 | ||
|   { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_call_object_path, N_("Object path to invoke method on"), NULL},
 | ||
|   { "method", 'm', 0, G_OPTION_ARG_STRING, &opt_call_method, N_("Method and interface name"), NULL},
 | ||
|   { "timeout", 't', 0, G_OPTION_ARG_INT, &opt_call_timeout, N_("Timeout in seconds"), NULL},
 | ||
|   { "interactive", 'i', 0, G_OPTION_ARG_NONE, &opt_call_interactive, N_("Allow interactive authorization"), NULL},
 | ||
|   G_OPTION_ENTRY_NULL
 | ||
| };
 | ||
| 
 | ||
| static gboolean
 | ||
| handle_call (gint        *argc,
 | ||
|              gchar      **argv[],
 | ||
|              gboolean     request_completion,
 | ||
|              const gchar *completion_cur,
 | ||
|              const gchar *completion_prev)
 | ||
| {
 | ||
|   gint ret;
 | ||
|   GOptionContext *o;
 | ||
|   gchar *s;
 | ||
|   GError *error;
 | ||
|   GDBusConnection *c;
 | ||
|   GVariant *parameters;
 | ||
|   gchar *interface_name;
 | ||
|   gchar *method_name;
 | ||
|   GVariant *result;
 | ||
|   GPtrArray *in_signature_types;
 | ||
| #ifdef G_OS_UNIX
 | ||
|   GUnixFDList *fd_list;
 | ||
| #endif
 | ||
|   gboolean complete_names;
 | ||
|   gboolean complete_paths;
 | ||
|   gboolean complete_methods;
 | ||
|   GVariantBuilder builder;
 | ||
|   gboolean skip_dashes;
 | ||
|   guint parm;
 | ||
|   guint n;
 | ||
|   GDBusCallFlags flags;
 | ||
| 
 | ||
|   ret = FALSE;
 | ||
|   c = NULL;
 | ||
|   parameters = NULL;
 | ||
|   interface_name = NULL;
 | ||
|   method_name = NULL;
 | ||
|   result = NULL;
 | ||
|   in_signature_types = NULL;
 | ||
| #ifdef G_OS_UNIX
 | ||
|   fd_list = g_unix_fd_list_new ();
 | ||
| #endif
 | ||
| 
 | ||
|   modify_argv0_for_command (argc, argv, "call");
 | ||
| 
 | ||
|   o = command_option_context_new (NULL, _("Invoke a method on a remote object."),
 | ||
|                                   call_entries, request_completion);
 | ||
|   g_option_context_add_group (o, connection_get_group ());
 | ||
| 
 | ||
|   complete_names = FALSE;
 | ||
|   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0)
 | ||
|     {
 | ||
|       complete_names = TRUE;
 | ||
|       remove_arg ((*argc) - 1, argc, argv);
 | ||
|     }
 | ||
| 
 | ||
|   complete_paths = FALSE;
 | ||
|   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0)
 | ||
|     {
 | ||
|       complete_paths = TRUE;
 | ||
|       remove_arg ((*argc) - 1, argc, argv);
 | ||
|     }
 | ||
| 
 | ||
|   complete_methods = FALSE;
 | ||
|   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--method") == 0)
 | ||
|     {
 | ||
|       complete_methods = TRUE;
 | ||
|       remove_arg ((*argc) - 1, argc, argv);
 | ||
|     }
 | ||
| 
 | ||
|   if (!g_option_context_parse (o, argc, argv, NULL))
 | ||
|     {
 | ||
|       if (!request_completion)
 | ||
|         {
 | ||
|           s = g_option_context_get_help (o, FALSE, NULL);
 | ||
|           g_printerr ("%s", s);
 | ||
|           g_free (s);
 | ||
|           goto out;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|   error = NULL;
 | ||
|   c = connection_get_dbus_connection (TRUE, &error);
 | ||
|   if (c == NULL)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         {
 | ||
|           if (g_strcmp0 (completion_prev, "--address") == 0)
 | ||
|             {
 | ||
|               g_print ("unix:\n"
 | ||
|                        "tcp:\n"
 | ||
|                        "nonce-tcp:\n");
 | ||
|             }
 | ||
|           else
 | ||
|             {
 | ||
|               g_print ("--system \n--session \n--address \n");
 | ||
|             }
 | ||
|         }
 | ||
|       else
 | ||
|         {
 | ||
|           g_printerr (_("Error connecting: %s\n"), error->message);
 | ||
|         }
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   /* validate and complete destination (bus name) */
 | ||
|   if (complete_names)
 | ||
|     {
 | ||
|       print_names (c, FALSE);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (opt_call_dest == NULL)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         g_print ("--dest \n");
 | ||
|       else
 | ||
|         g_printerr (_("Error: Destination is not specified\n"));
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0)
 | ||
|     {
 | ||
|       print_names (c, g_str_has_prefix (opt_call_dest, ":"));
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (!request_completion && !g_dbus_is_name (opt_call_dest))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid bus name\n"), opt_call_dest);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   /* validate and complete object path */
 | ||
|   if (complete_paths)
 | ||
|     {
 | ||
|       print_paths (c, opt_call_dest, "/");
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (opt_call_object_path == NULL)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         g_print ("--object-path \n");
 | ||
|       else
 | ||
|         g_printerr (_("Error: Object path is not specified\n"));
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (request_completion && g_strcmp0 ("--object-path", completion_prev) == 0)
 | ||
|     {
 | ||
|       gchar *p;
 | ||
|       s = g_strdup (opt_call_object_path);
 | ||
|       p = strrchr (s, '/');
 | ||
|       if (p != NULL)
 | ||
|         {
 | ||
|           if (p == s)
 | ||
|             p++;
 | ||
|           *p = '\0';
 | ||
|         }
 | ||
|       print_paths (c, opt_call_dest, s);
 | ||
|       g_free (s);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (!request_completion && !g_variant_is_object_path (opt_call_object_path))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid object path\n"), opt_call_object_path);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   /* validate and complete method (interface + method name) */
 | ||
|   if (complete_methods)
 | ||
|     {
 | ||
|       print_methods_and_signals (c, opt_call_dest, opt_call_object_path, TRUE, FALSE);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (opt_call_method == NULL)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         g_print ("--method \n");
 | ||
|       else
 | ||
|         g_printerr (_("Error: Method name is not specified\n"));
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (request_completion && g_strcmp0 ("--method", completion_prev) == 0)
 | ||
|     {
 | ||
|       print_methods_and_signals (c, opt_call_dest, opt_call_object_path, TRUE, FALSE);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   s = strrchr (opt_call_method, '.');
 | ||
|   if (!request_completion && s == NULL)
 | ||
|     {
 | ||
|       g_printerr (_("Error: Method name “%s” is invalid\n"), opt_call_method);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   method_name = g_strdup (s + 1);
 | ||
|   interface_name = g_strndup (opt_call_method, s - opt_call_method);
 | ||
| 
 | ||
|   /* All done with completion now */
 | ||
|   if (request_completion)
 | ||
|     goto out;
 | ||
| 
 | ||
|   /* Introspect, for easy conversion - it's not fatal if we can't do this */
 | ||
|   in_signature_types = call_helper_get_method_in_signature (c,
 | ||
|                                                             opt_call_dest,
 | ||
|                                                             opt_call_object_path,
 | ||
|                                                             interface_name,
 | ||
|                                                             method_name,
 | ||
|                                                             &error);
 | ||
|   if (in_signature_types == NULL)
 | ||
|     {
 | ||
|       //g_printerr ("Error getting introspection data: %s\n", error->message);
 | ||
|       g_error_free (error);
 | ||
|       error = NULL;
 | ||
|     }
 | ||
| 
 | ||
|   /* Read parameters */
 | ||
|   g_variant_builder_init_static (&builder, G_VARIANT_TYPE_TUPLE);
 | ||
|   skip_dashes = TRUE;
 | ||
|   parm = 0;
 | ||
|   for (n = 1; n < (guint) *argc; n++)
 | ||
|     {
 | ||
|       GVariant *value;
 | ||
|       GVariantType *type;
 | ||
| 
 | ||
|       /* Under certain conditions, g_option_context_parse returns the "--"
 | ||
|          itself (setting off unparsed arguments), too: */
 | ||
|       if (skip_dashes && g_strcmp0 ((*argv)[n], "--") == 0)
 | ||
|         {
 | ||
|           skip_dashes = FALSE;
 | ||
|           continue;
 | ||
|         }
 | ||
| 
 | ||
|       type = NULL;
 | ||
|       if (in_signature_types != NULL)
 | ||
|         {
 | ||
|           if (parm >= in_signature_types->len)
 | ||
|             {
 | ||
|               /* Only warn for the first param */
 | ||
|               if (parm == in_signature_types->len)
 | ||
|                 {
 | ||
|                   g_printerr ("Warning: Introspection data indicates %d parameters but more was passed\n",
 | ||
|                               in_signature_types->len);
 | ||
|                 }
 | ||
|             }
 | ||
|           else
 | ||
|             {
 | ||
|               type = in_signature_types->pdata[parm];
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|       error = NULL;
 | ||
|       value = g_variant_parse (type,
 | ||
|                                (*argv)[n],
 | ||
|                                NULL,
 | ||
|                                NULL,
 | ||
|                                &error);
 | ||
|       if (value == NULL)
 | ||
|         {
 | ||
|           gchar *context;
 | ||
| 
 | ||
|           context = g_variant_parse_error_print_context (error, (*argv)[n]);
 | ||
|           g_error_free (error);
 | ||
|           error = NULL;
 | ||
|           value = _g_variant_parse_me_harder (type, (*argv)[n], &error);
 | ||
|           if (value == NULL)
 | ||
|             {
 | ||
|               if (type != NULL)
 | ||
|                 {
 | ||
|                   s = g_variant_type_dup_string (type);
 | ||
|                   g_printerr (_("Error parsing parameter %d of type “%s”: %s\n"),
 | ||
|                               parm + 1,
 | ||
|                               s,
 | ||
|                               context);
 | ||
|                   g_free (s);
 | ||
|                 }
 | ||
|               else
 | ||
|                 {
 | ||
|                   g_printerr (_("Error parsing parameter %d: %s\n"),
 | ||
|                               parm + 1,
 | ||
|                               context);
 | ||
|                 }
 | ||
|               g_error_free (error);
 | ||
|               g_variant_builder_clear (&builder);
 | ||
|               g_free (context);
 | ||
|               goto out;
 | ||
|             }
 | ||
|           g_free (context);
 | ||
|         }
 | ||
| #ifdef G_OS_UNIX
 | ||
|       if (!walk_variant_for_handle (&builder, fd_list, value))
 | ||
|         {
 | ||
|           g_clear_pointer (&value, g_variant_unref);
 | ||
|           g_variant_builder_clear (&builder);
 | ||
|           goto out;
 | ||
|         }
 | ||
| #else
 | ||
|       g_variant_builder_add_value (&builder, value);
 | ||
| #endif
 | ||
|       g_clear_pointer (&value, g_variant_unref);
 | ||
|       ++parm;
 | ||
|     }
 | ||
|   parameters = g_variant_builder_end (&builder);
 | ||
| 
 | ||
|   if (parameters != NULL)
 | ||
|     parameters = g_variant_ref_sink (parameters);
 | ||
| 
 | ||
|   flags = G_DBUS_CALL_FLAGS_NONE;
 | ||
|   if (opt_call_interactive)
 | ||
|     flags |= G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION;
 | ||
| 
 | ||
| #ifdef G_OS_UNIX
 | ||
|   result = g_dbus_connection_call_with_unix_fd_list_sync (c,
 | ||
|                                                           opt_call_dest,
 | ||
|                                                           opt_call_object_path,
 | ||
|                                                           interface_name,
 | ||
|                                                           method_name,
 | ||
|                                                           parameters,
 | ||
|                                                           NULL,
 | ||
|                                                           flags,
 | ||
|                                                           opt_call_timeout > 0 ? opt_call_timeout * 1000 : opt_call_timeout,
 | ||
|                                                           fd_list,
 | ||
|                                                           NULL,
 | ||
|                                                           NULL,
 | ||
|                                                           &error);
 | ||
| #else
 | ||
|   result = g_dbus_connection_call_sync (c,
 | ||
| 		  			opt_call_dest,
 | ||
| 					opt_call_object_path,
 | ||
| 					interface_name,
 | ||
| 					method_name,
 | ||
| 					parameters,
 | ||
| 					NULL,
 | ||
| 					flags,
 | ||
| 					opt_call_timeout > 0 ? opt_call_timeout * 1000 : opt_call_timeout,
 | ||
| 					NULL,
 | ||
| 					&error);
 | ||
| #endif
 | ||
|   if (result == NULL)
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s\n"), error->message);
 | ||
| 
 | ||
|       if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS) && in_signature_types != NULL)
 | ||
|         {
 | ||
|           if (in_signature_types->len > 0)
 | ||
|             {
 | ||
|               GString *str;
 | ||
|               str = g_string_new (NULL);
 | ||
| 
 | ||
|               for (n = 0; n < in_signature_types->len; n++)
 | ||
|                 {
 | ||
|                   GVariantType *type = in_signature_types->pdata[n];
 | ||
|                   g_string_append_len (str,
 | ||
|                                        g_variant_type_peek_string (type),
 | ||
|                                        g_variant_type_get_string_length (type));
 | ||
|                 }
 | ||
| 
 | ||
|               g_printerr ("(According to introspection data, you need to pass '%s')\n", str->str);
 | ||
|               g_string_free (str, TRUE);
 | ||
|             }
 | ||
|           else
 | ||
|             g_printerr ("(According to introspection data, you need to pass no arguments)\n");
 | ||
|         }
 | ||
| 
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   s = g_variant_print (result, TRUE);
 | ||
|   g_print ("%s\n", s);
 | ||
|   g_free (s);
 | ||
| 
 | ||
|   ret = TRUE;
 | ||
| 
 | ||
|  out:
 | ||
|   if (in_signature_types != NULL)
 | ||
|     g_ptr_array_unref (in_signature_types);
 | ||
|   if (result != NULL)
 | ||
|     g_variant_unref (result);
 | ||
|   if (c != NULL)
 | ||
|     g_object_unref (c);
 | ||
|   if (parameters != NULL)
 | ||
|     g_variant_unref (parameters);
 | ||
|   g_free (interface_name);
 | ||
|   g_free (method_name);
 | ||
|   g_option_context_free (o);
 | ||
| #ifdef G_OS_UNIX
 | ||
|   g_clear_object (&fd_list);
 | ||
| #endif
 | ||
|   return ret;
 | ||
| }
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| static gchar *opt_introspect_dest = NULL;
 | ||
| static gchar *opt_introspect_object_path = NULL;
 | ||
| static gboolean opt_introspect_xml = FALSE;
 | ||
| static gboolean opt_introspect_recurse = FALSE;
 | ||
| static gboolean opt_introspect_only_properties = FALSE;
 | ||
| 
 | ||
| /* Introspect colors */
 | ||
| #define RESET_COLOR                 (use_colors? "\033[0m": "")
 | ||
| #define INTROSPECT_TITLE_COLOR      (use_colors? UNDERLINE: "")
 | ||
| #define INTROSPECT_NODE_COLOR       (use_colors? RESET_COLOR: "")
 | ||
| #define INTROSPECT_INTERFACE_COLOR  (use_colors? YELLOW: "")
 | ||
| #define INTROSPECT_METHOD_COLOR     (use_colors? BLUE: "")
 | ||
| #define INTROSPECT_SIGNAL_COLOR     (use_colors? BLUE: "")
 | ||
| #define INTROSPECT_PROPERTY_COLOR   (use_colors? MAGENTA: "")
 | ||
| #define INTROSPECT_INOUT_COLOR      (use_colors? RESET_COLOR: "")
 | ||
| #define INTROSPECT_TYPE_COLOR       (use_colors? GREEN: "")
 | ||
| #define INTROSPECT_ANNOTATION_COLOR (use_colors? RESET_COLOR: "")
 | ||
| 
 | ||
| static void
 | ||
| dump_annotation (const GDBusAnnotationInfo *o,
 | ||
|                  guint indent,
 | ||
|                  gboolean ignore_indent,
 | ||
|                  gboolean use_colors)
 | ||
| {
 | ||
|   guint n;
 | ||
|   g_print ("%*s%s@%s(\"%s\")%s\n",
 | ||
|            ignore_indent ? 0 : indent, "",
 | ||
|            INTROSPECT_ANNOTATION_COLOR, o->key, o->value, RESET_COLOR);
 | ||
|   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
 | ||
|     dump_annotation (o->annotations[n], indent + 2, FALSE, use_colors);
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| dump_arg (const GDBusArgInfo *o,
 | ||
|           guint indent,
 | ||
|           const gchar *direction,
 | ||
|           gboolean ignore_indent,
 | ||
|           gboolean include_newline,
 | ||
|           gboolean use_colors)
 | ||
| {
 | ||
|   guint n;
 | ||
| 
 | ||
|   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
 | ||
|     {
 | ||
|       dump_annotation (o->annotations[n], indent, ignore_indent, use_colors);
 | ||
|       ignore_indent = FALSE;
 | ||
|     }
 | ||
| 
 | ||
|   g_print ("%*s%s%s%s%s%s%s %s%s",
 | ||
|            ignore_indent ? 0 : indent, "",
 | ||
|            INTROSPECT_INOUT_COLOR, direction, RESET_COLOR,
 | ||
|            INTROSPECT_TYPE_COLOR, o->signature, RESET_COLOR,
 | ||
|            o->name,
 | ||
|            include_newline ? ",\n" : "");
 | ||
| }
 | ||
| 
 | ||
| static guint
 | ||
| count_args (GDBusArgInfo **args)
 | ||
| {
 | ||
|   guint n;
 | ||
|   n = 0;
 | ||
|   if (args == NULL)
 | ||
|     goto out;
 | ||
|   while (args[n] != NULL)
 | ||
|     n++;
 | ||
|  out:
 | ||
|   return n;
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| dump_method (const GDBusMethodInfo *o,
 | ||
|              guint                  indent,
 | ||
|              gboolean               use_colors)
 | ||
| {
 | ||
|   guint n;
 | ||
|   guint m;
 | ||
|   size_t name_len;
 | ||
|   guint total_num_args;
 | ||
| 
 | ||
|   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
 | ||
|     dump_annotation (o->annotations[n], indent, FALSE, use_colors);
 | ||
| 
 | ||
|   g_print ("%*s%s%s%s(",
 | ||
|            indent, "",
 | ||
|            INTROSPECT_METHOD_COLOR, o->name, RESET_COLOR);
 | ||
|   name_len = strlen (o->name);
 | ||
|   total_num_args = count_args (o->in_args) + count_args (o->out_args);
 | ||
|   for (n = 0, m = 0; o->in_args != NULL && o->in_args[n] != NULL; n++, m++)
 | ||
|     {
 | ||
|       gboolean ignore_indent = (m == 0);
 | ||
|       gboolean include_newline = (m != total_num_args - 1);
 | ||
| 
 | ||
|       dump_arg (o->in_args[n],
 | ||
|                 indent + name_len + 1,
 | ||
|                 "in  ",
 | ||
|                 ignore_indent,
 | ||
|                 include_newline,
 | ||
|                 use_colors);
 | ||
|     }
 | ||
|   for (n = 0; o->out_args != NULL && o->out_args[n] != NULL; n++, m++)
 | ||
|     {
 | ||
|       gboolean ignore_indent = (m == 0);
 | ||
|       gboolean include_newline = (m != total_num_args - 1);
 | ||
|       dump_arg (o->out_args[n],
 | ||
|                 indent + name_len + 1,
 | ||
|                 "out ",
 | ||
|                 ignore_indent,
 | ||
|                 include_newline,
 | ||
|                 use_colors);
 | ||
|     }
 | ||
|   g_print (");\n");
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| dump_signal (const GDBusSignalInfo *o,
 | ||
|              guint                  indent,
 | ||
|              gboolean               use_colors)
 | ||
| {
 | ||
|   guint n;
 | ||
|   guint name_len;
 | ||
|   guint total_num_args;
 | ||
| 
 | ||
|   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
 | ||
|     dump_annotation (o->annotations[n], indent, FALSE, use_colors);
 | ||
| 
 | ||
|   g_print ("%*s%s%s%s(",
 | ||
|            indent, "",
 | ||
|            INTROSPECT_SIGNAL_COLOR, o->name, RESET_COLOR);
 | ||
|   name_len = strlen (o->name);
 | ||
|   total_num_args = count_args (o->args);
 | ||
|   for (n = 0; o->args != NULL && o->args[n] != NULL; n++)
 | ||
|     {
 | ||
|       gboolean ignore_indent = (n == 0);
 | ||
|       gboolean include_newline = (n != total_num_args - 1);
 | ||
|       dump_arg (o->args[n],
 | ||
|                 indent + name_len + 1,
 | ||
|                 "",
 | ||
|                 ignore_indent,
 | ||
|                 include_newline,
 | ||
|                 use_colors);
 | ||
|     }
 | ||
|   g_print (");\n");
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| dump_property (const GDBusPropertyInfo *o,
 | ||
|                guint                    indent,
 | ||
|                gboolean                 use_colors,
 | ||
|                GVariant                *value)
 | ||
| {
 | ||
|   const gchar *access;
 | ||
|   guint n;
 | ||
| 
 | ||
|   if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_READABLE)
 | ||
|     access = "readonly";
 | ||
|   else if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)
 | ||
|     access = "writeonly";
 | ||
|   else if (o->flags == (G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE))
 | ||
|     access = "readwrite";
 | ||
|   else
 | ||
|     g_assert_not_reached ();
 | ||
| 
 | ||
|   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
 | ||
|     dump_annotation (o->annotations[n], indent, FALSE, use_colors);
 | ||
| 
 | ||
|   if (value != NULL)
 | ||
|     {
 | ||
|       gchar *s = g_variant_print (value, FALSE);
 | ||
|       g_print ("%*s%s %s%s%s %s%s%s = %s;\n", indent, "", access,
 | ||
|                INTROSPECT_TYPE_COLOR, o->signature, RESET_COLOR,
 | ||
|                INTROSPECT_PROPERTY_COLOR, o->name, RESET_COLOR,
 | ||
|                s);
 | ||
|       g_free (s);
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       g_print ("%*s%s %s %s;\n", indent, "", access, o->signature, o->name);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| dump_interface (GDBusConnection          *c,
 | ||
|                 const gchar              *name,
 | ||
|                 const GDBusInterfaceInfo *o,
 | ||
|                 guint                     indent,
 | ||
|                 gboolean                  use_colors,
 | ||
|                 const gchar              *object_path)
 | ||
| {
 | ||
|   guint n;
 | ||
|   GHashTable *properties;
 | ||
| 
 | ||
|   properties = g_hash_table_new_full (g_str_hash,
 | ||
|                                       g_str_equal,
 | ||
|                                       g_free,
 | ||
|                                       (GDestroyNotify) g_variant_unref);
 | ||
| 
 | ||
|   /* Try to get properties */
 | ||
|   if (c != NULL && name != NULL && object_path != NULL && o->properties != NULL)
 | ||
|     {
 | ||
|       GVariant *result;
 | ||
|       result = g_dbus_connection_call_sync (c,
 | ||
|                                             name,
 | ||
|                                             object_path,
 | ||
|                                             DBUS_INTERFACE_PROPERTIES,
 | ||
|                                             "GetAll",
 | ||
|                                             g_variant_new ("(s)", o->name),
 | ||
|                                             NULL,
 | ||
|                                             G_DBUS_CALL_FLAGS_NONE,
 | ||
|                                             3000,
 | ||
|                                             NULL,
 | ||
|                                             NULL);
 | ||
|       if (result != NULL)
 | ||
|         {
 | ||
|           if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(a{sv})")))
 | ||
|             {
 | ||
|               GVariantIter *iter;
 | ||
|               GVariant *item;
 | ||
|               g_variant_get (result,
 | ||
|                              "(a{sv})",
 | ||
|                              &iter);
 | ||
|               while ((item = g_variant_iter_next_value (iter)))
 | ||
|                 {
 | ||
|                   gchar *key;
 | ||
|                   GVariant *value;
 | ||
|                   g_variant_get (item,
 | ||
|                                  "{sv}",
 | ||
|                                  &key,
 | ||
|                                  &value);
 | ||
| 
 | ||
|                   g_hash_table_insert (properties, key, g_variant_ref (value));
 | ||
|                 }
 | ||
|             }
 | ||
|           g_variant_unref (result);
 | ||
|         }
 | ||
|       else
 | ||
|         {
 | ||
|           for (n = 0; o->properties != NULL && o->properties[n] != NULL; n++)
 | ||
|             {
 | ||
|               result = g_dbus_connection_call_sync (c,
 | ||
|                                                     name,
 | ||
|                                                     object_path,
 | ||
|                                                     DBUS_INTERFACE_PROPERTIES,
 | ||
|                                                     "Get",
 | ||
|                                                     g_variant_new ("(ss)", o->name, o->properties[n]->name),
 | ||
|                                                     G_VARIANT_TYPE ("(v)"),
 | ||
|                                                     G_DBUS_CALL_FLAGS_NONE,
 | ||
|                                                     3000,
 | ||
|                                                     NULL,
 | ||
|                                                     NULL);
 | ||
|               if (result != NULL)
 | ||
|                 {
 | ||
|                   GVariant *property_value;
 | ||
|                   g_variant_get (result,
 | ||
|                                  "(v)",
 | ||
|                                  &property_value);
 | ||
|                   g_hash_table_insert (properties,
 | ||
|                                        g_strdup (o->properties[n]->name),
 | ||
|                                        g_variant_ref (property_value));
 | ||
|                   g_variant_unref (result);
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
 | ||
|     dump_annotation (o->annotations[n], indent, FALSE, use_colors);
 | ||
| 
 | ||
|   g_print ("%*s%sinterface %s%s {\n",
 | ||
|            indent, "",
 | ||
|            INTROSPECT_INTERFACE_COLOR, o->name, RESET_COLOR);
 | ||
|   if (o->methods != NULL && !opt_introspect_only_properties)
 | ||
|     {
 | ||
|       g_print ("%*s  %smethods%s:\n",
 | ||
|                indent, "",
 | ||
|                INTROSPECT_TITLE_COLOR, RESET_COLOR);
 | ||
|       for (n = 0; o->methods[n] != NULL; n++)
 | ||
|         dump_method (o->methods[n], indent + 4, use_colors);
 | ||
|     }
 | ||
|   if (o->signals != NULL && !opt_introspect_only_properties)
 | ||
|     {
 | ||
|       g_print ("%*s  %ssignals%s:\n",
 | ||
|                indent, "",
 | ||
|                INTROSPECT_TITLE_COLOR, RESET_COLOR);
 | ||
|       for (n = 0; o->signals[n] != NULL; n++)
 | ||
|         dump_signal (o->signals[n], indent + 4, use_colors);
 | ||
|     }
 | ||
|   if (o->properties != NULL)
 | ||
|     {
 | ||
|       g_print ("%*s  %sproperties%s:\n",
 | ||
|                indent, "",
 | ||
|                INTROSPECT_TITLE_COLOR, RESET_COLOR);
 | ||
|       for (n = 0; o->properties[n] != NULL; n++)
 | ||
|         {
 | ||
|           dump_property (o->properties[n],
 | ||
|                          indent + 4,
 | ||
|                          use_colors,
 | ||
|                          g_hash_table_lookup (properties, (o->properties[n])->name));
 | ||
|         }
 | ||
|     }
 | ||
|   g_print ("%*s};\n",
 | ||
|            indent, "");
 | ||
| 
 | ||
|   g_hash_table_unref (properties);
 | ||
| }
 | ||
| 
 | ||
| static gboolean
 | ||
| introspect_do (GDBusConnection *c,
 | ||
|                const gchar     *object_path,
 | ||
|                guint            indent,
 | ||
|                gboolean         use_colors);
 | ||
| 
 | ||
| static void
 | ||
| dump_node (GDBusConnection      *c,
 | ||
|            const gchar          *name,
 | ||
|            const GDBusNodeInfo  *o,
 | ||
|            guint                 indent,
 | ||
|            gboolean              use_colors,
 | ||
|            const gchar          *object_path,
 | ||
|            gboolean              recurse)
 | ||
| {
 | ||
|   guint n;
 | ||
|   const gchar *object_path_to_print;
 | ||
| 
 | ||
|   object_path_to_print = object_path;
 | ||
|   if (o->path != NULL)
 | ||
|     object_path_to_print = o->path;
 | ||
| 
 | ||
|   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
 | ||
|     dump_annotation (o->annotations[n], indent, FALSE, use_colors);
 | ||
| 
 | ||
|   g_print ("%*s%snode %s%s",
 | ||
|            indent, "",
 | ||
|            INTROSPECT_NODE_COLOR,
 | ||
|            object_path_to_print != NULL ? object_path_to_print : "(not set)",
 | ||
|            RESET_COLOR);
 | ||
|   if (o->interfaces != NULL || o->nodes != NULL)
 | ||
|     {
 | ||
|       g_print (" {\n");
 | ||
|       for (n = 0; o->interfaces != NULL && o->interfaces[n] != NULL; n++)
 | ||
|         {
 | ||
|           if (opt_introspect_only_properties)
 | ||
|             {
 | ||
|               if (o->interfaces[n]->properties != NULL && o->interfaces[n]->properties[0] != NULL)
 | ||
|                 dump_interface (c, name, o->interfaces[n], indent + 2, use_colors, object_path);
 | ||
|             }
 | ||
|           else
 | ||
|             {
 | ||
|               dump_interface (c, name, o->interfaces[n], indent + 2, use_colors, object_path);
 | ||
|             }
 | ||
|         }
 | ||
|       for (n = 0; o->nodes != NULL && o->nodes[n] != NULL; n++)
 | ||
|         {
 | ||
|           if (recurse)
 | ||
|             {
 | ||
|               gchar *child_path;
 | ||
|               if (g_variant_is_object_path (o->nodes[n]->path))
 | ||
|                 {
 | ||
|                   child_path = g_strdup (o->nodes[n]->path);
 | ||
|                   /* avoid infinite loops */
 | ||
|                   if (g_str_has_prefix (child_path, object_path))
 | ||
|                     {
 | ||
|                       introspect_do (c, child_path, indent + 2, use_colors);
 | ||
|                     }
 | ||
|                   else
 | ||
|                     {
 | ||
|                       g_print ("Skipping path %s that is not enclosed by parent %s\n",
 | ||
|                                child_path, object_path);
 | ||
|                     }
 | ||
|                 }
 | ||
|               else
 | ||
|                 {
 | ||
|                   if (g_strcmp0 (object_path, "/") == 0)
 | ||
|                     child_path = g_strdup_printf ("/%s", o->nodes[n]->path);
 | ||
|                   else
 | ||
|                     child_path = g_strdup_printf ("%s/%s", object_path, o->nodes[n]->path);
 | ||
|                   introspect_do (c, child_path, indent + 2, use_colors);
 | ||
|                 }
 | ||
|               g_free (child_path);
 | ||
|             }
 | ||
|           else
 | ||
|             {
 | ||
|               dump_node (NULL, NULL, o->nodes[n], indent + 2, use_colors, NULL, recurse);
 | ||
|             }
 | ||
|         }
 | ||
|       g_print ("%*s};\n",
 | ||
|                indent, "");
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       g_print ("\n");
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| static const GOptionEntry introspect_entries[] =
 | ||
| {
 | ||
|   { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_introspect_dest, N_("Destination name to introspect"), NULL},
 | ||
|   { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_introspect_object_path, N_("Object path to introspect"), NULL},
 | ||
|   { "xml", 'x', 0, G_OPTION_ARG_NONE, &opt_introspect_xml, N_("Print XML"), NULL},
 | ||
|   { "recurse", 'r', 0, G_OPTION_ARG_NONE, &opt_introspect_recurse, N_("Introspect children"), NULL},
 | ||
|   { "only-properties", 'p', 0, G_OPTION_ARG_NONE, &opt_introspect_only_properties, N_("Only print properties"), NULL},
 | ||
|   G_OPTION_ENTRY_NULL
 | ||
| };
 | ||
| 
 | ||
| static gboolean
 | ||
| introspect_do (GDBusConnection *c,
 | ||
|                const gchar     *object_path,
 | ||
|                guint            indent,
 | ||
|                gboolean         use_colors)
 | ||
| {
 | ||
|   GError *error;
 | ||
|   GVariant *result;
 | ||
|   GDBusNodeInfo *node;
 | ||
|   gboolean ret;
 | ||
|   const gchar *xml_data;
 | ||
| 
 | ||
|   ret = FALSE;
 | ||
|   node = NULL;
 | ||
|   result = NULL;
 | ||
| 
 | ||
|   error = NULL;
 | ||
|   result = g_dbus_connection_call_sync (c,
 | ||
|                                         opt_introspect_dest,
 | ||
|                                         object_path,
 | ||
|                                         DBUS_INTERFACE_INTROSPECTABLE,
 | ||
|                                         "Introspect",
 | ||
|                                         NULL,
 | ||
|                                         G_VARIANT_TYPE ("(s)"),
 | ||
|                                         G_DBUS_CALL_FLAGS_NONE,
 | ||
|                                         3000, /* 3 sec */
 | ||
|                                         NULL,
 | ||
|                                         &error);
 | ||
|   if (result == NULL)
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s\n"), error->message);
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   g_variant_get (result, "(&s)", &xml_data);
 | ||
| 
 | ||
|   if (opt_introspect_xml)
 | ||
|     {
 | ||
|       g_print ("%s", xml_data);
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       error = NULL;
 | ||
|       node = g_dbus_node_info_new_for_xml (xml_data, &error);
 | ||
|       if (node == NULL)
 | ||
|         {
 | ||
|           g_printerr (_("Error parsing introspection XML: %s\n"), error->message);
 | ||
|           g_error_free (error);
 | ||
|           goto out;
 | ||
|         }
 | ||
| 
 | ||
|       dump_node (c, opt_introspect_dest, node, indent, use_colors, object_path, opt_introspect_recurse);
 | ||
|     }
 | ||
| 
 | ||
|   ret = TRUE;
 | ||
| 
 | ||
|  out:
 | ||
|   if (node != NULL)
 | ||
|     g_dbus_node_info_unref (node);
 | ||
|   if (result != NULL)
 | ||
|     g_variant_unref (result);
 | ||
|   return ret;
 | ||
| }
 | ||
| 
 | ||
| static gboolean
 | ||
| handle_introspect (gint        *argc,
 | ||
|                    gchar      **argv[],
 | ||
|                    gboolean     request_completion,
 | ||
|                    const gchar *completion_cur,
 | ||
|                    const gchar *completion_prev)
 | ||
| {
 | ||
|   gint ret;
 | ||
|   GOptionContext *o;
 | ||
|   gchar *s;
 | ||
|   GError *error;
 | ||
|   GDBusConnection *c;
 | ||
|   gboolean complete_names;
 | ||
|   gboolean complete_paths;
 | ||
|   gboolean color_support;
 | ||
| 
 | ||
|   ret = FALSE;
 | ||
|   c = NULL;
 | ||
| 
 | ||
|   modify_argv0_for_command (argc, argv, "introspect");
 | ||
| 
 | ||
|   o = command_option_context_new (NULL, _("Introspect a remote object."),
 | ||
|                                   introspect_entries, request_completion);
 | ||
|   g_option_context_add_group (o, connection_get_group ());
 | ||
| 
 | ||
|   complete_names = FALSE;
 | ||
|   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0)
 | ||
|     {
 | ||
|       complete_names = TRUE;
 | ||
|       remove_arg ((*argc) - 1, argc, argv);
 | ||
|     }
 | ||
| 
 | ||
|   complete_paths = FALSE;
 | ||
|   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0)
 | ||
|     {
 | ||
|       complete_paths = TRUE;
 | ||
|       remove_arg ((*argc) - 1, argc, argv);
 | ||
|     }
 | ||
| 
 | ||
|   if (!g_option_context_parse (o, argc, argv, NULL))
 | ||
|     {
 | ||
|       if (!request_completion)
 | ||
|         {
 | ||
|           s = g_option_context_get_help (o, FALSE, NULL);
 | ||
|           g_printerr ("%s", s);
 | ||
|           g_free (s);
 | ||
|           goto out;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|   error = NULL;
 | ||
|   c = connection_get_dbus_connection (TRUE, &error);
 | ||
|   if (c == NULL)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         {
 | ||
|           if (g_strcmp0 (completion_prev, "--address") == 0)
 | ||
|             {
 | ||
|               g_print ("unix:\n"
 | ||
|                        "tcp:\n"
 | ||
|                        "nonce-tcp:\n");
 | ||
|             }
 | ||
|           else
 | ||
|             {
 | ||
|               g_print ("--system \n--session \n--address \n");
 | ||
|             }
 | ||
|         }
 | ||
|       else
 | ||
|         {
 | ||
|           g_printerr (_("Error connecting: %s\n"), error->message);
 | ||
|         }
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (complete_names)
 | ||
|     {
 | ||
|       print_names (c, FALSE);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   /* this only makes sense on message bus connections */
 | ||
|   if (opt_introspect_dest == NULL)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         g_print ("--dest \n");
 | ||
|       else
 | ||
|         g_printerr (_("Error: Destination is not specified\n"));
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0)
 | ||
|     {
 | ||
|       print_names (c, g_str_has_prefix (opt_introspect_dest, ":"));
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (complete_paths)
 | ||
|     {
 | ||
|       print_paths (c, opt_introspect_dest, "/");
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (!request_completion && !g_dbus_is_name (opt_introspect_dest))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid bus name\n"), opt_introspect_dest);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (opt_introspect_object_path == NULL)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         g_print ("--object-path \n");
 | ||
|       else
 | ||
|         g_printerr (_("Error: Object path is not specified\n"));
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (request_completion && g_strcmp0 ("--object-path", completion_prev) == 0)
 | ||
|     {
 | ||
|       gchar *p;
 | ||
|       s = g_strdup (opt_introspect_object_path);
 | ||
|       p = strrchr (s, '/');
 | ||
|       if (p != NULL)
 | ||
|         {
 | ||
|           if (p == s)
 | ||
|             p++;
 | ||
|           *p = '\0';
 | ||
|         }
 | ||
|       print_paths (c, opt_introspect_dest, s);
 | ||
|       g_free (s);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (!request_completion && !g_variant_is_object_path (opt_introspect_object_path))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid object path\n"), opt_introspect_object_path);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (request_completion && opt_introspect_object_path != NULL && !opt_introspect_recurse)
 | ||
|     {
 | ||
|       g_print ("--recurse \n");
 | ||
|     }
 | ||
| 
 | ||
|   if (request_completion && opt_introspect_object_path != NULL && !opt_introspect_only_properties)
 | ||
|     {
 | ||
|       g_print ("--only-properties \n");
 | ||
|     }
 | ||
| 
 | ||
|   /* All done with completion now */
 | ||
|   if (request_completion)
 | ||
|     goto out;
 | ||
| 
 | ||
|   /* Before we start printing the actual info, check if we can do colors*/
 | ||
|   color_support = g_log_writer_supports_color (fileno (stdout));
 | ||
| 
 | ||
|   if (!introspect_do (c, opt_introspect_object_path, 0, color_support))
 | ||
|     goto out;
 | ||
| 
 | ||
|   ret = TRUE;
 | ||
| 
 | ||
|  out:
 | ||
|   if (c != NULL)
 | ||
|     g_object_unref (c);
 | ||
|   g_option_context_free (o);
 | ||
|   return ret;
 | ||
| }
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| static gchar *opt_monitor_dest = NULL;
 | ||
| static gchar *opt_monitor_object_path = NULL;
 | ||
| 
 | ||
| static guint monitor_filter_id = 0;
 | ||
| 
 | ||
| static void
 | ||
| monitor_signal_cb (GDBusConnection *connection,
 | ||
|                    const gchar     *sender_name,
 | ||
|                    const gchar     *object_path,
 | ||
|                    const gchar     *interface_name,
 | ||
|                    const gchar     *signal_name,
 | ||
|                    GVariant        *parameters,
 | ||
|                    gpointer         user_data)
 | ||
| {
 | ||
|   gchar *s;
 | ||
|   s = g_variant_print (parameters, TRUE);
 | ||
|   g_print ("%s: %s.%s %s\n",
 | ||
|            object_path,
 | ||
|            interface_name,
 | ||
|            signal_name,
 | ||
|            s);
 | ||
|   g_free (s);
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| monitor_on_name_appeared (GDBusConnection *connection,
 | ||
|                           const gchar *name,
 | ||
|                           const gchar *name_owner,
 | ||
|                           gpointer user_data)
 | ||
| {
 | ||
|   g_print ("The name %s is owned by %s\n", name, name_owner);
 | ||
|   g_assert (monitor_filter_id == 0);
 | ||
|   monitor_filter_id = g_dbus_connection_signal_subscribe (connection,
 | ||
|                                                           name_owner,
 | ||
|                                                           NULL,  /* any interface */
 | ||
|                                                           NULL,  /* any member */
 | ||
|                                                           opt_monitor_object_path,
 | ||
|                                                           NULL,  /* arg0 */
 | ||
|                                                           G_DBUS_SIGNAL_FLAGS_NONE,
 | ||
|                                                           monitor_signal_cb,
 | ||
|                                                           NULL,  /* user_data */
 | ||
|                                                           NULL); /* user_data destroy notify */
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| monitor_on_name_vanished (GDBusConnection *connection,
 | ||
|                           const gchar *name,
 | ||
|                           gpointer user_data)
 | ||
| {
 | ||
|   g_print ("The name %s does not have an owner\n", name);
 | ||
| 
 | ||
|   if (monitor_filter_id != 0)
 | ||
|     g_dbus_connection_signal_unsubscribe (connection, g_steal_handle_id (&monitor_filter_id));
 | ||
| }
 | ||
| 
 | ||
| static const GOptionEntry monitor_entries[] =
 | ||
| {
 | ||
|   { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_monitor_dest, N_("Destination name to monitor"), NULL},
 | ||
|   { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_monitor_object_path, N_("Object path to monitor"), NULL},
 | ||
|   G_OPTION_ENTRY_NULL
 | ||
| };
 | ||
| 
 | ||
| static gboolean
 | ||
| handle_monitor (gint        *argc,
 | ||
|                 gchar      **argv[],
 | ||
|                 gboolean     request_completion,
 | ||
|                 const gchar *completion_cur,
 | ||
|                 const gchar *completion_prev)
 | ||
| {
 | ||
|   gint ret;
 | ||
|   GOptionContext *o;
 | ||
|   gchar *s;
 | ||
|   GError *error;
 | ||
|   GDBusConnection *c;
 | ||
|   gboolean complete_names;
 | ||
|   gboolean complete_paths;
 | ||
|   GMainLoop *loop;
 | ||
| 
 | ||
|   ret = FALSE;
 | ||
|   c = NULL;
 | ||
| 
 | ||
|   modify_argv0_for_command (argc, argv, "monitor");
 | ||
| 
 | ||
|   o = command_option_context_new (NULL, _("Monitor a remote object."),
 | ||
|                                   monitor_entries, request_completion);
 | ||
|   g_option_context_add_group (o, connection_get_group ());
 | ||
| 
 | ||
|   complete_names = FALSE;
 | ||
|   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0)
 | ||
|     {
 | ||
|       complete_names = TRUE;
 | ||
|       remove_arg ((*argc) - 1, argc, argv);
 | ||
|     }
 | ||
| 
 | ||
|   complete_paths = FALSE;
 | ||
|   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0)
 | ||
|     {
 | ||
|       complete_paths = TRUE;
 | ||
|       remove_arg ((*argc) - 1, argc, argv);
 | ||
|     }
 | ||
| 
 | ||
|   if (!g_option_context_parse (o, argc, argv, NULL))
 | ||
|     {
 | ||
|       if (!request_completion)
 | ||
|         {
 | ||
|           s = g_option_context_get_help (o, FALSE, NULL);
 | ||
|           g_printerr ("%s", s);
 | ||
|           g_free (s);
 | ||
|           goto out;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|   error = NULL;
 | ||
|   c = connection_get_dbus_connection (TRUE, &error);
 | ||
|   if (c == NULL)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         {
 | ||
|           if (g_strcmp0 (completion_prev, "--address") == 0)
 | ||
|             {
 | ||
|               g_print ("unix:\n"
 | ||
|                        "tcp:\n"
 | ||
|                        "nonce-tcp:\n");
 | ||
|             }
 | ||
|           else
 | ||
|             {
 | ||
|               g_print ("--system \n--session \n--address \n");
 | ||
|             }
 | ||
|         }
 | ||
|       else
 | ||
|         {
 | ||
|           g_printerr (_("Error connecting: %s\n"), error->message);
 | ||
|         }
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   /* Monitoring doesn’t make sense on a non-message-bus connection. */
 | ||
|   if (g_dbus_connection_get_unique_name (c) == NULL)
 | ||
|     {
 | ||
|       if (!request_completion)
 | ||
|         g_printerr (_("Error: can’t monitor a non-message-bus connection\n"));
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (complete_names)
 | ||
|     {
 | ||
|       print_names (c, FALSE);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   /* this only makes sense on message bus connections */
 | ||
|   if (opt_monitor_dest == NULL)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         g_print ("--dest \n");
 | ||
|       else
 | ||
|         g_printerr (_("Error: Destination is not specified\n"));
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0)
 | ||
|     {
 | ||
|       print_names (c, g_str_has_prefix (opt_monitor_dest, ":"));
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (!request_completion && !g_dbus_is_name (opt_monitor_dest))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid bus name\n"), opt_monitor_dest);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (complete_paths)
 | ||
|     {
 | ||
|       print_paths (c, opt_monitor_dest, "/");
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (opt_monitor_object_path == NULL)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         {
 | ||
|           g_print ("--object-path \n");
 | ||
|           goto out;
 | ||
|         }
 | ||
|       /* it's fine to not have an object path */
 | ||
|     }
 | ||
|   if (request_completion && g_strcmp0 ("--object-path", completion_prev) == 0)
 | ||
|     {
 | ||
|       gchar *p;
 | ||
|       s = g_strdup (opt_monitor_object_path);
 | ||
|       p = strrchr (s, '/');
 | ||
|       if (p != NULL)
 | ||
|         {
 | ||
|           if (p == s)
 | ||
|             p++;
 | ||
|           *p = '\0';
 | ||
|         }
 | ||
|       print_paths (c, opt_monitor_dest, s);
 | ||
|       g_free (s);
 | ||
|       goto out;
 | ||
|     }
 | ||
|   if (!request_completion && (opt_monitor_object_path != NULL && !g_variant_is_object_path (opt_monitor_object_path)))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid object path\n"), opt_monitor_object_path);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   /* All done with completion now */
 | ||
|   if (request_completion)
 | ||
|     goto out;
 | ||
| 
 | ||
|   if (opt_monitor_object_path != NULL)
 | ||
|     g_print ("Monitoring signals on object %s owned by %s\n", opt_monitor_object_path, opt_monitor_dest);
 | ||
|   else
 | ||
|     g_print ("Monitoring signals from all objects owned by %s\n", opt_monitor_dest);
 | ||
| 
 | ||
|   loop = g_main_loop_new (NULL, FALSE);
 | ||
|   g_bus_watch_name_on_connection (c,
 | ||
|                                   opt_monitor_dest,
 | ||
|                                   G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
 | ||
|                                   monitor_on_name_appeared,
 | ||
|                                   monitor_on_name_vanished,
 | ||
|                                   NULL,
 | ||
|                                   NULL);
 | ||
| 
 | ||
|   g_main_loop_run (loop);
 | ||
|   g_main_loop_unref (loop);
 | ||
| 
 | ||
|   ret = TRUE;
 | ||
| 
 | ||
|  out:
 | ||
|   if (c != NULL)
 | ||
|     g_object_unref (c);
 | ||
|   g_option_context_free (o);
 | ||
|   return ret;
 | ||
| }
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| static gboolean opt_wait_activate_set = FALSE;
 | ||
| static gchar *opt_wait_activate_name = NULL;
 | ||
| static gint64 opt_wait_timeout_secs = 0;  /* no timeout */
 | ||
| 
 | ||
| typedef enum {
 | ||
|   WAIT_STATE_RUNNING,  /* waiting to see the service */
 | ||
|   WAIT_STATE_SUCCESS,  /* seen it successfully */
 | ||
|   WAIT_STATE_TIMEOUT,  /* timed out before seeing it */
 | ||
| } WaitState;
 | ||
| 
 | ||
| static gboolean
 | ||
| opt_wait_activate_cb (const gchar  *option_name,
 | ||
|                       const gchar  *value,
 | ||
|                       gpointer      data,
 | ||
|                       GError      **error)
 | ||
| {
 | ||
|   /* @value may be NULL */
 | ||
|   opt_wait_activate_set = TRUE;
 | ||
|   opt_wait_activate_name = g_strdup (value);
 | ||
| 
 | ||
|   return TRUE;
 | ||
| }
 | ||
| 
 | ||
| static const GOptionEntry wait_entries[] =
 | ||
| {
 | ||
|   { "activate", 'a', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
 | ||
|     opt_wait_activate_cb,
 | ||
|     N_("Service to activate before waiting for the other one (well-known name)"),
 | ||
|     "[NAME]" },
 | ||
|   { "timeout", 't', 0, G_OPTION_ARG_INT64, &opt_wait_timeout_secs,
 | ||
|     N_("Timeout to wait for before exiting with an error (seconds); 0 for "
 | ||
|        "no timeout (default)"), "SECS" },
 | ||
|   G_OPTION_ENTRY_NULL
 | ||
| };
 | ||
| 
 | ||
| static void
 | ||
| wait_name_appeared_cb (GDBusConnection *connection,
 | ||
|                        const gchar     *name,
 | ||
|                        const gchar     *name_owner,
 | ||
|                        gpointer         user_data)
 | ||
| {
 | ||
|   WaitState *wait_state = user_data;
 | ||
| 
 | ||
|   *wait_state = WAIT_STATE_SUCCESS;
 | ||
| }
 | ||
| 
 | ||
| static gboolean
 | ||
| wait_timeout_cb (gpointer user_data)
 | ||
| {
 | ||
|   WaitState *wait_state = user_data;
 | ||
| 
 | ||
|   *wait_state = WAIT_STATE_TIMEOUT;
 | ||
| 
 | ||
|   /* Removed in handle_wait(). */
 | ||
|   return G_SOURCE_CONTINUE;
 | ||
| }
 | ||
| 
 | ||
| static gboolean
 | ||
| handle_wait (gint        *argc,
 | ||
|              gchar      **argv[],
 | ||
|              gboolean     request_completion,
 | ||
|              const gchar *completion_cur,
 | ||
|              const gchar *completion_prev)
 | ||
| {
 | ||
|   gint ret;
 | ||
|   GOptionContext *o;
 | ||
|   gchar *s;
 | ||
|   GError *error;
 | ||
|   GDBusConnection *c;
 | ||
|   guint watch_id, timer_id = 0, activate_watch_id;
 | ||
|   const gchar *activate_service, *wait_service;
 | ||
|   WaitState wait_state = WAIT_STATE_RUNNING;
 | ||
| 
 | ||
|   ret = FALSE;
 | ||
|   c = NULL;
 | ||
| 
 | ||
|   modify_argv0_for_command (argc, argv, "wait");
 | ||
| 
 | ||
|   o = command_option_context_new (_("[OPTION…] BUS-NAME"),
 | ||
|                                   _("Wait for a bus name to appear."),
 | ||
|                                   wait_entries, request_completion);
 | ||
|   g_option_context_add_group (o, connection_get_group ());
 | ||
| 
 | ||
|   if (!g_option_context_parse (o, argc, argv, NULL))
 | ||
|     {
 | ||
|       if (!request_completion)
 | ||
|         {
 | ||
|           s = g_option_context_get_help (o, FALSE, NULL);
 | ||
|           g_printerr ("%s", s);
 | ||
|           g_free (s);
 | ||
|           goto out;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|   error = NULL;
 | ||
|   c = connection_get_dbus_connection (TRUE, &error);
 | ||
|   if (c == NULL)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         {
 | ||
|           if (g_strcmp0 (completion_prev, "--address") == 0)
 | ||
|             {
 | ||
|               g_print ("unix:\n"
 | ||
|                        "tcp:\n"
 | ||
|                        "nonce-tcp:\n");
 | ||
|             }
 | ||
|           else
 | ||
|             {
 | ||
|               g_print ("--system \n--session \n--address \n");
 | ||
|             }
 | ||
|         }
 | ||
|       else
 | ||
|         {
 | ||
|           g_printerr (_("Error connecting: %s\n"), error->message);
 | ||
|         }
 | ||
|       g_error_free (error);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   /* All done with completion now */
 | ||
|   if (request_completion)
 | ||
|     goto out;
 | ||
| 
 | ||
|   /*
 | ||
|    * Try and disentangle the command line arguments, with the aim of supporting:
 | ||
|    *    gdbus wait --session --activate ActivateName WaitName
 | ||
|    *    gdbus wait --session --activate ActivateAndWaitName
 | ||
|    *    gdbus wait --activate --session ActivateAndWaitName
 | ||
|    *    gdbus wait --session WaitName
 | ||
|    */
 | ||
|   if (*argc == 2 && opt_wait_activate_set && opt_wait_activate_name != NULL)
 | ||
|     {
 | ||
|       activate_service = opt_wait_activate_name;
 | ||
|       wait_service = (*argv)[1];
 | ||
|     }
 | ||
|   else if (*argc == 2 &&
 | ||
|            opt_wait_activate_set && opt_wait_activate_name == NULL)
 | ||
|     {
 | ||
|       activate_service = (*argv)[1];
 | ||
|       wait_service = (*argv)[1];
 | ||
|     }
 | ||
|   else if (*argc == 2 && !opt_wait_activate_set)
 | ||
|     {
 | ||
|       activate_service = NULL;  /* disabled */
 | ||
|       wait_service = (*argv)[1];
 | ||
|     }
 | ||
|   else if (*argc == 1 &&
 | ||
|            opt_wait_activate_set && opt_wait_activate_name != NULL)
 | ||
|     {
 | ||
|       activate_service = opt_wait_activate_name;
 | ||
|       wait_service = opt_wait_activate_name;
 | ||
|     }
 | ||
|   else if (*argc == 1 &&
 | ||
|            opt_wait_activate_set && opt_wait_activate_name == NULL)
 | ||
|     {
 | ||
|       g_printerr (_("Error: A service to activate for must be specified.\n"));
 | ||
|       goto out;
 | ||
|     }
 | ||
|   else if (*argc == 1 && !opt_wait_activate_set)
 | ||
|     {
 | ||
|       g_printerr (_("Error: A service to wait for must be specified.\n"));
 | ||
|       goto out;
 | ||
|     }
 | ||
|   else /* if (*argc > 2) */
 | ||
|     {
 | ||
|       g_printerr (_("Error: Too many arguments.\n"));
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (activate_service != NULL &&
 | ||
|       (!g_dbus_is_name (activate_service) ||
 | ||
|        g_dbus_is_unique_name (activate_service)))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid well-known bus name.\n"),
 | ||
|                   activate_service);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   if (!g_dbus_is_name (wait_service) || g_dbus_is_unique_name (wait_service))
 | ||
|     {
 | ||
|       g_printerr (_("Error: %s is not a valid well-known bus name.\n"),
 | ||
|                   wait_service);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   /* Start the prerequisite service if needed. */
 | ||
|   if (activate_service != NULL)
 | ||
|     {
 | ||
|       activate_watch_id = g_bus_watch_name_on_connection (c, activate_service,
 | ||
|                                                           G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
 | ||
|                                                           NULL, NULL,
 | ||
|                                                           NULL, NULL);
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       activate_watch_id = 0;
 | ||
|     }
 | ||
| 
 | ||
|   /* Wait for the expected name to appear. */
 | ||
|   watch_id = g_bus_watch_name_on_connection (c,
 | ||
|                                              wait_service,
 | ||
|                                              G_BUS_NAME_WATCHER_FLAGS_NONE,
 | ||
|                                              wait_name_appeared_cb,
 | ||
|                                              NULL, &wait_state, NULL);
 | ||
| 
 | ||
|   /* Safety timeout. */
 | ||
|   if (opt_wait_timeout_secs > 0)
 | ||
|     timer_id = g_timeout_add_seconds (opt_wait_timeout_secs, wait_timeout_cb, &wait_state);
 | ||
| 
 | ||
|   while (wait_state == WAIT_STATE_RUNNING)
 | ||
|     g_main_context_iteration (NULL, TRUE);
 | ||
| 
 | ||
|   g_bus_unwatch_name (watch_id);
 | ||
|   if (timer_id != 0)
 | ||
|       g_source_remove (timer_id);
 | ||
|   if (activate_watch_id != 0)
 | ||
|       g_bus_unwatch_name (activate_watch_id);
 | ||
| 
 | ||
|   ret = (wait_state == WAIT_STATE_SUCCESS);
 | ||
| 
 | ||
|  out:
 | ||
|   g_clear_object (&c);
 | ||
|   g_option_context_free (o);
 | ||
|   g_free (opt_wait_activate_name);
 | ||
|   opt_wait_activate_name = NULL;
 | ||
| 
 | ||
|   return ret;
 | ||
| }
 | ||
| 
 | ||
| /* ---------------------------------------------------------------------------------------------------- */
 | ||
| 
 | ||
| static gchar *
 | ||
| pick_word_at (const gchar  *s,
 | ||
|               gint          cursor,
 | ||
|               gint         *out_word_begins_at)
 | ||
| {
 | ||
|   gint begin;
 | ||
|   gint end;
 | ||
| 
 | ||
|   if (s[0] == '\0')
 | ||
|     {
 | ||
|       if (out_word_begins_at != NULL)
 | ||
|         *out_word_begins_at = -1;
 | ||
|       return NULL;
 | ||
|     }
 | ||
| 
 | ||
|   if (g_ascii_isspace (s[cursor]) && ((cursor > 0 && g_ascii_isspace(s[cursor-1])) || cursor == 0))
 | ||
|     {
 | ||
|       if (out_word_begins_at != NULL)
 | ||
|         *out_word_begins_at = cursor;
 | ||
|       return g_strdup ("");
 | ||
|     }
 | ||
| 
 | ||
|   while (!g_ascii_isspace (s[cursor - 1]) && cursor > 0)
 | ||
|     cursor--;
 | ||
|   begin = cursor;
 | ||
| 
 | ||
|   end = begin;
 | ||
|   while (!g_ascii_isspace (s[end]) && s[end] != '\0')
 | ||
|     end++;
 | ||
| 
 | ||
|   if (out_word_begins_at != NULL)
 | ||
|     *out_word_begins_at = begin;
 | ||
| 
 | ||
|   return g_strndup (s + begin, end - begin);
 | ||
| }
 | ||
| 
 | ||
| gint
 | ||
| main (gint argc, gchar *argv[])
 | ||
| {
 | ||
|   gint ret;
 | ||
|   const gchar *command;
 | ||
|   gboolean request_completion;
 | ||
|   gchar *completion_cur;
 | ||
|   gchar *completion_prev;
 | ||
| #ifdef G_OS_WIN32
 | ||
|   gchar *tmp;
 | ||
| #endif
 | ||
| 
 | ||
|   setlocale (LC_ALL, "");
 | ||
|   textdomain (GETTEXT_PACKAGE);
 | ||
| 
 | ||
| #ifdef G_OS_WIN32
 | ||
|   tmp = _glib_get_locale_dir ();
 | ||
|   bindtextdomain (GETTEXT_PACKAGE, tmp);
 | ||
|   g_free (tmp);
 | ||
| #else
 | ||
|   bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
 | ||
|   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 | ||
| #endif
 | ||
| 
 | ||
|   ret = 1;
 | ||
|   completion_cur = NULL;
 | ||
|   completion_prev = NULL;
 | ||
| 
 | ||
|   if (argc < 2)
 | ||
|     {
 | ||
|       usage (&argc, &argv, FALSE);
 | ||
|       goto out;
 | ||
|     }
 | ||
| 
 | ||
|   request_completion = FALSE;
 | ||
| 
 | ||
|   //completion_debug ("---- argc=%d --------------------------------------------------------", argc);
 | ||
| 
 | ||
|  again:
 | ||
|   command = argv[1];
 | ||
|   if (g_strcmp0 (command, "help") == 0)
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         {
 | ||
|           /* do nothing */
 | ||
|         }
 | ||
|       else
 | ||
|         {
 | ||
|           usage (&argc, &argv, TRUE);
 | ||
|           ret = 0;
 | ||
|         }
 | ||
|       goto out;
 | ||
|     }
 | ||
|   else if (g_strcmp0 (command, "emit") == 0)
 | ||
|     {
 | ||
|       if (handle_emit (&argc,
 | ||
|                        &argv,
 | ||
|                        request_completion,
 | ||
|                        completion_cur,
 | ||
|                        completion_prev))
 | ||
|         ret = 0;
 | ||
|       goto out;
 | ||
|     }
 | ||
|   else if (g_strcmp0 (command, "call") == 0)
 | ||
|     {
 | ||
|       if (handle_call (&argc,
 | ||
|                        &argv,
 | ||
|                        request_completion,
 | ||
|                        completion_cur,
 | ||
|                        completion_prev))
 | ||
|         ret = 0;
 | ||
|       goto out;
 | ||
|     }
 | ||
|   else if (g_strcmp0 (command, "introspect") == 0)
 | ||
|     {
 | ||
|       if (handle_introspect (&argc,
 | ||
|                              &argv,
 | ||
|                              request_completion,
 | ||
|                              completion_cur,
 | ||
|                              completion_prev))
 | ||
|         ret = 0;
 | ||
|       goto out;
 | ||
|     }
 | ||
|   else if (g_strcmp0 (command, "monitor") == 0)
 | ||
|     {
 | ||
|       if (handle_monitor (&argc,
 | ||
|                           &argv,
 | ||
|                           request_completion,
 | ||
|                           completion_cur,
 | ||
|                           completion_prev))
 | ||
|         ret = 0;
 | ||
|       goto out;
 | ||
|     }
 | ||
|   else if (g_strcmp0 (command, "wait") == 0)
 | ||
|     {
 | ||
|       if (handle_wait (&argc,
 | ||
|                        &argv,
 | ||
|                        request_completion,
 | ||
|                        completion_cur,
 | ||
|                        completion_prev))
 | ||
|         ret = 0;
 | ||
|       goto out;
 | ||
|     }
 | ||
| #ifdef G_OS_WIN32
 | ||
|   else if (g_strcmp0 (command, _GDBUS_ARG_WIN32_RUN_SESSION_BUS) == 0)
 | ||
|     {
 | ||
|       g_win32_run_session_bus (NULL, NULL, NULL, 0);
 | ||
|       ret = 0;
 | ||
|       goto out;
 | ||
|     }
 | ||
| #endif
 | ||
|   else if (g_strcmp0 (command, "complete") == 0 && argc == 4 && !request_completion)
 | ||
|     {
 | ||
|       const gchar *completion_line;
 | ||
|       gchar **completion_argv;
 | ||
|       gint completion_argc;
 | ||
|       gint completion_point;
 | ||
|       gchar *endp;
 | ||
|       gint cur_begin;
 | ||
| 
 | ||
|       request_completion = TRUE;
 | ||
| 
 | ||
|       completion_line = argv[2];
 | ||
|       completion_point = strtol (argv[3], &endp, 10);
 | ||
|       if (endp == argv[3] || *endp != '\0')
 | ||
|         goto out;
 | ||
| 
 | ||
| #if 0
 | ||
|       completion_debug ("completion_point=%d", completion_point);
 | ||
|       completion_debug ("----");
 | ||
|       completion_debug (" 0123456789012345678901234567890123456789012345678901234567890123456789");
 | ||
|       completion_debug ("'%s'", completion_line);
 | ||
|       completion_debug (" %*s^",
 | ||
|                          completion_point, "");
 | ||
|       completion_debug ("----");
 | ||
| #endif
 | ||
| 
 | ||
|       if (!g_shell_parse_argv (completion_line,
 | ||
|                                &completion_argc,
 | ||
|                                &completion_argv,
 | ||
|                                NULL))
 | ||
|         {
 | ||
|           /* it's very possible the command line can't be parsed (for
 | ||
|            * example, missing quotes etc) - in that case, we just
 | ||
|            * don't autocomplete at all
 | ||
|            */
 | ||
|           goto out;
 | ||
|         }
 | ||
| 
 | ||
|       /* compute cur and prev */
 | ||
|       completion_prev = NULL;
 | ||
|       completion_cur = pick_word_at (completion_line, completion_point, &cur_begin);
 | ||
|       if (cur_begin > 0)
 | ||
|         {
 | ||
|           gint prev_end;
 | ||
|           for (prev_end = cur_begin - 1; prev_end >= 0; prev_end--)
 | ||
|             {
 | ||
|               if (!g_ascii_isspace (completion_line[prev_end]))
 | ||
|                 {
 | ||
|                   completion_prev = pick_word_at (completion_line, prev_end, NULL);
 | ||
|                   break;
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
| #if 0
 | ||
|       completion_debug (" cur='%s'", completion_cur);
 | ||
|       completion_debug ("prev='%s'", completion_prev);
 | ||
| #endif
 | ||
| 
 | ||
|       argc = completion_argc;
 | ||
|       argv = completion_argv;
 | ||
| 
 | ||
|       ret = 0;
 | ||
| 
 | ||
|       goto again;
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       if (request_completion)
 | ||
|         {
 | ||
|           g_print ("help \nemit \ncall \nintrospect \nmonitor \nwait \n");
 | ||
|           ret = 0;
 | ||
|           goto out;
 | ||
|         }
 | ||
|       else
 | ||
|         {
 | ||
|           g_printerr ("Unknown command '%s'\n", command);
 | ||
|           usage (&argc, &argv, FALSE);
 | ||
|           goto out;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|  out:
 | ||
|   g_free (completion_cur);
 | ||
|   g_free (completion_prev);
 | ||
|   return ret;
 | ||
| }
 |