glib/gio/gdbus-tool.c

2642 lines
78 KiB
C
Raw Normal View History

/* GDBus - GLib D-Bus Library
*
2010-05-09 19:14:55 +02:00
* 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
2014-01-23 12:58:29 +01:00
* 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>
2010-05-06 22:34:23 +02:00
#include <string.h>
#include <stdio.h>
#include <locale.h>
#include <gio/gio.h>
#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
#endif
2010-05-06 22:34:23 +02:00
#include <gi18n.h>
#ifdef G_OS_WIN32
#include "glib/glib-private.h"
gdbus, win32: autolaunch bus with gdbus.exe instead of rundll32 This is a bit of breaking change: After this commit the apps relying of win32 dbus autolaunching, need to install gdbus.exe alongside with libgio-2.0-0.dll. A new command for gdbus tool is used for running server: gdbus.exe _win32_run_session_bus To implement it gdbus.exe uses the same exported function g_win32_run_session_bus that earlier was used by rundll. So (private) ABI was not changed. It runs the bus syncronously, exiting after inactivity timeout - all exactly like it was runed earlier with the help of rundll32. While private exported function may have _some_ version compatibility issues between gdbus.exe and libgio-2.0-0.dll compiling dbus server registration logic directly into gdbus.exe can lead to _more hidden and more complex_ compatibility issues since the names and behaviour of syncronization objects used to publish server address would be required compatible between gdbus.exe and libgio-2.0-0.dll. So using "private" exported function to call looks like more safe behaviour. gdbus.exe binary was selected for this task since it has corresponding name and at least for msys2 is shippied in same package with libgio-2.0-0.dll turn_off_the_starting_cursor function is also kept as is, however it is not obvious if it is still needed (by now I failed reproducing original issue). Explicit g_warnings added to help with possible problematic cases for absent or incompatible gdbus.exe Mainloop is created after successful daemon creation Before this change the function leaked mainloop on daemon creation fail
2019-02-28 21:12:47 +01:00
#include "gdbusprivate.h"
#endif
/* ---------------------------------------------------------------------------------------------------- */
/* 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 = fopen ("/tmp/gdbus-completion-debug.txt", "a+");
}
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,
"org.freedesktop.DBus.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,
"org.freedesktop.DBus.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,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.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,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.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,
"org.freedesktop.DBus.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;
}
/* ---------------------------------------------------------------------------------------------------- */
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 (&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;
gint fd_id;
#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 = NULL;
#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 (&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 (g_variant_is_of_type (value, G_VARIANT_TYPE_HANDLE))
{
if (!fd_list)
fd_list = g_unix_fd_list_new ();
if ((fd_id = g_unix_fd_list_append (fd_list, g_variant_get_handle (value), &error)) == -1)
{
g_printerr (_("Error adding handle %d: %s\n"),
g_variant_get_handle (value), error->message);
g_variant_builder_clear (&builder);
g_error_free (error);
goto out;
}
g_variant_unref (value);
value = g_variant_new_handle (fd_id);
}
#endif
g_variant_builder_add_value (&builder, value);
++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: "")
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
static void
dump_annotation (const GDBusAnnotationInfo *o,
guint indent,
gboolean ignore_indent,
gboolean use_colors)
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
{
guint n;
g_print ("%*s%s@%s(\"%s\")%s\n",
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
ignore_indent ? 0 : indent, "",
INTROSPECT_ANNOTATION_COLOR, o->key, o->value, RESET_COLOR);
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
dump_annotation (o->annotations[n], indent + 2, FALSE, use_colors);
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
}
static void
dump_arg (const GDBusArgInfo *o,
guint indent,
const gchar *direction,
gboolean ignore_indent,
gboolean include_newline,
gboolean use_colors)
{
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
guint n;
for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
{
dump_annotation (o->annotations[n], indent, ignore_indent, use_colors);
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
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;
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
dump_annotation (o->annotations[n], indent, FALSE, use_colors);
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
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);
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
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;
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
dump_annotation (o->annotations[n], indent, FALSE, use_colors);
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
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;
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
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 ();
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
dump_annotation (o->annotations[n], indent, FALSE, use_colors);
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
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,
"org.freedesktop.DBus.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,
"org.freedesktop.DBus.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);
}
}
}
}
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
dump_annotation (o->annotations[n], indent, FALSE, use_colors);
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
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;
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
dump_annotation (o->annotations[n], indent, FALSE, use_colors);
GDBus: Make gdbus(1) print annotations when introspecting data Also make the gdbus-example-server include some example annotations. The output looks like this: $ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject node /org/gtk/GDBus/TestObject { interface org.freedesktop.DBus.Properties { methods: Get(in s interface_name, in s property_name, out v value); GetAll(in s interface_name, out a{sv} properties); Set(in s interface_name, in s property_name, in v value); signals: PropertiesChanged(s interface_name, a{sv} changed_properties); }; interface org.freedesktop.DBus.Introspectable { methods: Introspect(out s xml_data); }; interface org.freedesktop.DBus.Peer { methods: Ping(); GetMachineId(out s machine_uuid); }; @org.gtk.GDBus.Annotation("OnInterface") @org.gtk.GDBus.Annotation("AlsoOnInterface") interface org.gtk.GDBus.TestInterface { methods: @org.gtk.GDBus.Annotation("OnMethod") HelloWorld(in s greeting, out s response); EmitSignal(@org.gtk.GDBus.Annotation.("OnArg") in d speed_in_mph); GimmeStdout(); signals: @org.gtk.GDBus.Annotation("Onsignal") VelocityChanged(d speed_in_mph, @org.gtk.GDBus.Annotation.("OnArg_NonFirst") s speed_as_string); properties: @org.gtk.GDBus.Annotation("OnProperty") @org.gtk.GDBus.Annotation("OnAnnotation_YesThisIsCrazy") readonly s FluxCapicitorName = 'DeLorean'; readwrite s Title = 'Back To C!'; readonly s ReadingAlwaysThrowsError; readwrite s WritingAlwaysThrowsError = "There's no home like home"; writeonly s OnlyWritable; readonly s Foo = 'Tick'; readonly s Bar = 'Tock'; }; };
2010-05-13 04:09:18 +02:00
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,
"org.freedesktop.DBus.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, monitor_filter_id);
monitor_filter_id = 0;
}
}
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 doesnt make sense on a non-message-bus connection. */
if (g_dbus_connection_get_unique_name (c) == NULL)
{
if (!request_completion)
g_printerr (_("Error: cant 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;
}
gdbus, win32: autolaunch bus with gdbus.exe instead of rundll32 This is a bit of breaking change: After this commit the apps relying of win32 dbus autolaunching, need to install gdbus.exe alongside with libgio-2.0-0.dll. A new command for gdbus tool is used for running server: gdbus.exe _win32_run_session_bus To implement it gdbus.exe uses the same exported function g_win32_run_session_bus that earlier was used by rundll. So (private) ABI was not changed. It runs the bus syncronously, exiting after inactivity timeout - all exactly like it was runed earlier with the help of rundll32. While private exported function may have _some_ version compatibility issues between gdbus.exe and libgio-2.0-0.dll compiling dbus server registration logic directly into gdbus.exe can lead to _more hidden and more complex_ compatibility issues since the names and behaviour of syncronization objects used to publish server address would be required compatible between gdbus.exe and libgio-2.0-0.dll. So using "private" exported function to call looks like more safe behaviour. gdbus.exe binary was selected for this task since it has corresponding name and at least for msys2 is shippied in same package with libgio-2.0-0.dll turn_off_the_starting_cursor function is also kept as is, however it is not obvious if it is still needed (by now I failed reproducing original issue). Explicit g_warnings added to help with possible problematic cases for absent or incompatible gdbus.exe Mainloop is created after successful daemon creation Before this change the function leaked mainloop on daemon creation fail
2019-02-28 21:12:47 +01:00
#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;
}