1/* GDBus - GLib D-Bus Library
2 *
3 * Copyright (C) 2008-2010 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: David Zeuthen <davidz@redhat.com>
19 */
20
21#include "config.h"
22
23#include <stdlib.h>
24#include <string.h>
25#include <stdio.h>
26#include <locale.h>
27
28#include <gio/gio.h>
29
30#ifdef G_OS_UNIX
31#include <gio/gunixfdlist.h>
32#endif
33
34#include <gi18n.h>
35
36#ifdef G_OS_WIN32
37#include "glib/glib-private.h"
38#include "gdbusprivate.h"
39#endif
40
41/* ---------------------------------------------------------------------------------------------------- */
42
43/* Escape values for console colors */
44#define UNDERLINE "\033[4m"
45#define BLUE "\033[34m"
46#define CYAN "\033[36m"
47#define GREEN "\033[32m"
48#define MAGENTA "\033[35m"
49#define RED "\033[31m"
50#define YELLOW "\033[33m"
51
52/* ---------------------------------------------------------------------------------------------------- */
53
54G_GNUC_UNUSED static void completion_debug (const gchar *format, ...);
55
56/* Uncomment to get debug traces in /tmp/gdbus-completion-debug.txt (nice
57 * to not have it interfere with stdout/stderr)
58 */
59#if 0
60G_GNUC_UNUSED static void
61completion_debug (const gchar *format, ...)
62{
63 va_list var_args;
64 gchar *s;
65 static FILE *f = NULL;
66
67 va_start (var_args, format);
68 s = g_strdup_vprintf (format, var_args);
69 if (f == NULL)
70 {
71 f = fopen ("/tmp/gdbus-completion-debug.txt", "a+");
72 }
73 fprintf (f, "%s\n", s);
74 g_free (s);
75}
76#else
77static void
78completion_debug (const gchar *format, ...)
79{
80}
81#endif
82
83/* ---------------------------------------------------------------------------------------------------- */
84
85
86static void
87remove_arg (gint num, gint *argc, gchar **argv[])
88{
89 gint n;
90
91 g_assert (num <= (*argc));
92
93 for (n = num; (*argv)[n] != NULL; n++)
94 (*argv)[n] = (*argv)[n+1];
95 (*argv)[n] = NULL;
96 (*argc) = (*argc) - 1;
97}
98
99static void
100usage (gint *argc, gchar **argv[], gboolean use_stdout)
101{
102 GOptionContext *o;
103 gchar *s;
104 gchar *program_name;
105
106 o = g_option_context_new (_("COMMAND"));
107 g_option_context_set_help_enabled (context: o, FALSE);
108 /* Ignore parsing result */
109 g_option_context_parse (context: o, argc, argv, NULL);
110 program_name = g_path_get_basename (file_name: (*argv)[0]);
111 s = g_strdup_printf (_("Commands:\n"
112 " help Shows this information\n"
113 " introspect Introspect a remote object\n"
114 " monitor Monitor a remote object\n"
115 " call Invoke a method on a remote object\n"
116 " emit Emit a signal\n"
117 " wait Wait for a bus name to appear\n"
118 "\n"
119 "Use “%s COMMAND --help” to get help on each command.\n"),
120 program_name);
121 g_free (mem: program_name);
122 g_option_context_set_description (context: o, description: s);
123 g_free (mem: s);
124 s = g_option_context_get_help (context: o, FALSE, NULL);
125 if (use_stdout)
126 g_print (format: "%s", s);
127 else
128 g_printerr (format: "%s", s);
129 g_free (mem: s);
130 g_option_context_free (context: o);
131}
132
133static void
134modify_argv0_for_command (gint *argc, gchar **argv[], const gchar *command)
135{
136 gchar *s;
137 gchar *program_name;
138
139 /* TODO:
140 * 1. get a g_set_prgname() ?; or
141 * 2. save old argv[0] and restore later
142 */
143
144 g_assert (g_strcmp0 ((*argv)[1], command) == 0);
145 remove_arg (num: 1, argc, argv);
146
147 program_name = g_path_get_basename (file_name: (*argv)[0]);
148 s = g_strdup_printf (format: "%s %s", (*argv)[0], command);
149 (*argv)[0] = s;
150 g_free (mem: program_name);
151}
152
153static GOptionContext *
154command_option_context_new (const gchar *parameter_string,
155 const gchar *summary,
156 const GOptionEntry *entries,
157 gboolean request_completion)
158{
159 GOptionContext *o = NULL;
160
161 o = g_option_context_new (parameter_string);
162 if (request_completion)
163 g_option_context_set_ignore_unknown_options (context: o, TRUE);
164 g_option_context_set_help_enabled (context: o, FALSE);
165 g_option_context_set_summary (context: o, summary);
166 g_option_context_add_main_entries (context: o, entries, GETTEXT_PACKAGE);
167
168 return g_steal_pointer (&o);
169}
170
171/* ---------------------------------------------------------------------------------------------------- */
172
173static void
174print_methods_and_signals (GDBusConnection *c,
175 const gchar *name,
176 const gchar *path,
177 gboolean print_methods,
178 gboolean print_signals)
179{
180 GVariant *result;
181 GError *error;
182 const gchar *xml_data;
183 GDBusNodeInfo *node;
184 guint n;
185 guint m;
186
187 error = NULL;
188 result = g_dbus_connection_call_sync (connection: c,
189 bus_name: name,
190 object_path: path,
191 interface_name: "org.freedesktop.DBus.Introspectable",
192 method_name: "Introspect",
193 NULL,
194 G_VARIANT_TYPE ("(s)"),
195 flags: G_DBUS_CALL_FLAGS_NONE,
196 timeout_msec: 3000, /* 3 secs */
197 NULL,
198 error: &error);
199 if (result == NULL)
200 {
201 g_printerr (_("Error: %s\n"), error->message);
202 g_error_free (error);
203 goto out;
204 }
205 g_variant_get (value: result, format_string: "(&s)", &xml_data);
206
207 error = NULL;
208 node = g_dbus_node_info_new_for_xml (xml_data, error: &error);
209 g_variant_unref (value: result);
210 if (node == NULL)
211 {
212 g_printerr (_("Error parsing introspection XML: %s\n"), error->message);
213 g_error_free (error);
214 goto out;
215 }
216
217 for (n = 0; node->interfaces != NULL && node->interfaces[n] != NULL; n++)
218 {
219 const GDBusInterfaceInfo *iface = node->interfaces[n];
220 for (m = 0; print_methods && iface->methods != NULL && iface->methods[m] != NULL; m++)
221 {
222 const GDBusMethodInfo *method = iface->methods[m];
223 g_print (format: "%s.%s \n", iface->name, method->name);
224 }
225 for (m = 0; print_signals && iface->signals != NULL && iface->signals[m] != NULL; m++)
226 {
227 const GDBusSignalInfo *signal = iface->signals[m];
228 g_print (format: "%s.%s \n", iface->name, signal->name);
229 }
230 }
231 g_dbus_node_info_unref (info: node);
232
233 out:
234 ;
235}
236
237static void
238print_paths (GDBusConnection *c,
239 const gchar *name,
240 const gchar *path)
241{
242 GVariant *result;
243 GError *error;
244 const gchar *xml_data;
245 GDBusNodeInfo *node;
246 guint n;
247
248 if (!g_dbus_is_name (string: name))
249 {
250 g_printerr (_("Error: %s is not a valid name\n"), name);
251 goto out;
252 }
253 if (!g_variant_is_object_path (string: path))
254 {
255 g_printerr (_("Error: %s is not a valid object path\n"), path);
256 goto out;
257 }
258
259 error = NULL;
260 result = g_dbus_connection_call_sync (connection: c,
261 bus_name: name,
262 object_path: path,
263 interface_name: "org.freedesktop.DBus.Introspectable",
264 method_name: "Introspect",
265 NULL,
266 G_VARIANT_TYPE ("(s)"),
267 flags: G_DBUS_CALL_FLAGS_NONE,
268 timeout_msec: 3000, /* 3 secs */
269 NULL,
270 error: &error);
271 if (result == NULL)
272 {
273 g_printerr (_("Error: %s\n"), error->message);
274 g_error_free (error);
275 goto out;
276 }
277 g_variant_get (value: result, format_string: "(&s)", &xml_data);
278
279 //g_printerr ("xml='%s'", xml_data);
280
281 error = NULL;
282 node = g_dbus_node_info_new_for_xml (xml_data, error: &error);
283 g_variant_unref (value: result);
284 if (node == NULL)
285 {
286 g_printerr (_("Error parsing introspection XML: %s\n"), error->message);
287 g_error_free (error);
288 goto out;
289 }
290
291 //g_printerr ("bar '%s'\n", path);
292
293 if (node->interfaces != NULL)
294 g_print (format: "%s \n", path);
295
296 for (n = 0; node->nodes != NULL && node->nodes[n] != NULL; n++)
297 {
298 gchar *s;
299
300 //g_printerr ("foo '%s'\n", node->nodes[n].path);
301
302 if (g_strcmp0 (str1: path, str2: "/") == 0)
303 s = g_strdup_printf (format: "/%s", node->nodes[n]->path);
304 else
305 s = g_strdup_printf (format: "%s/%s", path, node->nodes[n]->path);
306
307 print_paths (c, name, path: s);
308
309 g_free (mem: s);
310 }
311 g_dbus_node_info_unref (info: node);
312
313 out:
314 ;
315}
316
317static void
318print_names (GDBusConnection *c,
319 gboolean include_unique_names)
320{
321 GVariant *result;
322 GError *error;
323 GVariantIter *iter;
324 gchar *str;
325 GHashTable *name_set;
326 GList *keys;
327 GList *l;
328
329 name_set = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, NULL);
330
331 error = NULL;
332 result = g_dbus_connection_call_sync (connection: c,
333 bus_name: "org.freedesktop.DBus",
334 object_path: "/org/freedesktop/DBus",
335 interface_name: "org.freedesktop.DBus",
336 method_name: "ListNames",
337 NULL,
338 G_VARIANT_TYPE ("(as)"),
339 flags: G_DBUS_CALL_FLAGS_NONE,
340 timeout_msec: 3000, /* 3 secs */
341 NULL,
342 error: &error);
343 if (result == NULL)
344 {
345 g_printerr (_("Error: %s\n"), error->message);
346 g_error_free (error);
347 goto out;
348 }
349 g_variant_get (value: result, format_string: "(as)", &iter);
350 while (g_variant_iter_loop (iter, format_string: "s", &str))
351 g_hash_table_add (hash_table: name_set, key: g_strdup (str));
352 g_variant_iter_free (iter);
353 g_variant_unref (value: result);
354
355 error = NULL;
356 result = g_dbus_connection_call_sync (connection: c,
357 bus_name: "org.freedesktop.DBus",
358 object_path: "/org/freedesktop/DBus",
359 interface_name: "org.freedesktop.DBus",
360 method_name: "ListActivatableNames",
361 NULL,
362 G_VARIANT_TYPE ("(as)"),
363 flags: G_DBUS_CALL_FLAGS_NONE,
364 timeout_msec: 3000, /* 3 secs */
365 NULL,
366 error: &error);
367 if (result == NULL)
368 {
369 g_printerr (_("Error: %s\n"), error->message);
370 g_error_free (error);
371 goto out;
372 }
373 g_variant_get (value: result, format_string: "(as)", &iter);
374 while (g_variant_iter_loop (iter, format_string: "s", &str))
375 g_hash_table_add (hash_table: name_set, key: g_strdup (str));
376 g_variant_iter_free (iter);
377 g_variant_unref (value: result);
378
379 keys = g_hash_table_get_keys (hash_table: name_set);
380 keys = g_list_sort (list: keys, compare_func: (GCompareFunc) g_strcmp0);
381 for (l = keys; l != NULL; l = l->next)
382 {
383 const gchar *name = l->data;
384 if (!include_unique_names && g_str_has_prefix (str: name, prefix: ":"))
385 continue;
386
387 g_print (format: "%s \n", name);
388 }
389 g_list_free (list: keys);
390
391 out:
392 g_hash_table_unref (hash_table: name_set);
393}
394
395/* ---------------------------------------------------------------------------------------------------- */
396
397static gboolean opt_connection_system = FALSE;
398static gboolean opt_connection_session = FALSE;
399static gchar *opt_connection_address = NULL;
400
401static const GOptionEntry connection_entries[] =
402{
403 { "system", 'y', 0, G_OPTION_ARG_NONE, &opt_connection_system, N_("Connect to the system bus"), NULL},
404 { "session", 'e', 0, G_OPTION_ARG_NONE, &opt_connection_session, N_("Connect to the session bus"), NULL},
405 { "address", 'a', 0, G_OPTION_ARG_STRING, &opt_connection_address, N_("Connect to given D-Bus address"), NULL},
406 { NULL }
407};
408
409static GOptionGroup *
410connection_get_group (void)
411{
412 static GOptionGroup *g;
413
414 g = g_option_group_new (name: "connection",
415 N_("Connection Endpoint Options:"),
416 N_("Options specifying the connection endpoint"),
417 NULL,
418 NULL);
419 g_option_group_set_translation_domain (group: g, GETTEXT_PACKAGE);
420 g_option_group_add_entries (group: g, entries: connection_entries);
421
422 return g;
423}
424
425static GDBusConnection *
426connection_get_dbus_connection (gboolean require_message_bus,
427 GError **error)
428{
429 GDBusConnection *c;
430
431 c = NULL;
432
433 /* First, ensure we have exactly one connect */
434 if (!opt_connection_system && !opt_connection_session && opt_connection_address == NULL)
435 {
436 g_set_error (err: error,
437 G_IO_ERROR,
438 code: G_IO_ERROR_FAILED,
439 _("No connection endpoint specified"));
440 goto out;
441 }
442 else if ((opt_connection_system && (opt_connection_session || opt_connection_address != NULL)) ||
443 (opt_connection_session && (opt_connection_system || opt_connection_address != NULL)) ||
444 (opt_connection_address != NULL && (opt_connection_system || opt_connection_session)))
445 {
446 g_set_error (err: error,
447 G_IO_ERROR,
448 code: G_IO_ERROR_FAILED,
449 _("Multiple connection endpoints specified"));
450 goto out;
451 }
452
453 if (opt_connection_system)
454 {
455 c = g_bus_get_sync (bus_type: G_BUS_TYPE_SYSTEM, NULL, error);
456 }
457 else if (opt_connection_session)
458 {
459 c = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, error);
460 }
461 else if (opt_connection_address != NULL)
462 {
463 GDBusConnectionFlags flags = G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT;
464 if (require_message_bus)
465 flags |= G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION;
466 c = g_dbus_connection_new_for_address_sync (address: opt_connection_address,
467 flags,
468 NULL, /* GDBusAuthObserver */
469 NULL, /* GCancellable */
470 error);
471 }
472
473 out:
474 return c;
475}
476
477/* ---------------------------------------------------------------------------------------------------- */
478
479static GPtrArray *
480call_helper_get_method_in_signature (GDBusConnection *c,
481 const gchar *dest,
482 const gchar *path,
483 const gchar *interface_name,
484 const gchar *method_name,
485 GError **error)
486{
487 GPtrArray *ret;
488 GVariant *result;
489 GDBusNodeInfo *node_info;
490 const gchar *xml_data;
491 GDBusInterfaceInfo *interface_info;
492 GDBusMethodInfo *method_info;
493 guint n;
494
495 ret = NULL;
496 result = NULL;
497 node_info = NULL;
498
499 result = g_dbus_connection_call_sync (connection: c,
500 bus_name: dest,
501 object_path: path,
502 interface_name: "org.freedesktop.DBus.Introspectable",
503 method_name: "Introspect",
504 NULL,
505 G_VARIANT_TYPE ("(s)"),
506 flags: G_DBUS_CALL_FLAGS_NONE,
507 timeout_msec: 3000, /* 3 secs */
508 NULL,
509 error);
510 if (result == NULL)
511 goto out;
512
513 g_variant_get (value: result, format_string: "(&s)", &xml_data);
514 node_info = g_dbus_node_info_new_for_xml (xml_data, error);
515 if (node_info == NULL)
516 goto out;
517
518 interface_info = g_dbus_node_info_lookup_interface (info: node_info, name: interface_name);
519 if (interface_info == NULL)
520 {
521 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED,
522 _("Warning: According to introspection data, interface “%s” does not exist\n"),
523 interface_name);
524 goto out;
525 }
526
527 method_info = g_dbus_interface_info_lookup_method (info: interface_info, name: method_name);
528 if (method_info == NULL)
529 {
530 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED,
531 _("Warning: According to introspection data, method “%s” does not exist on interface “%s”\n"),
532 method_name,
533 interface_name);
534 goto out;
535 }
536
537 ret = g_ptr_array_new_with_free_func (element_free_func: (GDestroyNotify) g_variant_type_free);
538 for (n = 0; method_info->in_args != NULL && method_info->in_args[n] != NULL; n++)
539 {
540 g_ptr_array_add (array: ret, data: g_variant_type_new (type_string: method_info->in_args[n]->signature));
541 }
542
543 out:
544 if (node_info != NULL)
545 g_dbus_node_info_unref (info: node_info);
546 if (result != NULL)
547 g_variant_unref (value: result);
548
549 return ret;
550}
551
552/* ---------------------------------------------------------------------------------------------------- */
553
554static GVariant *
555_g_variant_parse_me_harder (GVariantType *type,
556 const gchar *given_str,
557 GError **error)
558{
559 GVariant *value;
560 gchar *s;
561 guint n;
562 GString *str;
563
564 str = g_string_new (init: "\"");
565 for (n = 0; given_str[n] != '\0'; n++)
566 {
567 if (G_UNLIKELY (given_str[n] == '\"'))
568 g_string_append (string: str, val: "\\\"");
569 else
570 g_string_append_c (str, given_str[n]);
571 }
572 g_string_append_c (str, '"');
573 s = g_string_free (string: str, FALSE);
574
575 value = g_variant_parse (type,
576 text: s,
577 NULL,
578 NULL,
579 error);
580 g_free (mem: s);
581
582 return value;
583}
584
585/* ---------------------------------------------------------------------------------------------------- */
586
587static gchar *opt_emit_dest = NULL;
588static gchar *opt_emit_object_path = NULL;
589static gchar *opt_emit_signal = NULL;
590
591static const GOptionEntry emit_entries[] =
592{
593 { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_emit_dest, N_("Optional destination for signal (unique name)"), NULL},
594 { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_emit_object_path, N_("Object path to emit signal on"), NULL},
595 { "signal", 's', 0, G_OPTION_ARG_STRING, &opt_emit_signal, N_("Signal and interface name"), NULL},
596 { NULL }
597};
598
599static gboolean
600handle_emit (gint *argc,
601 gchar **argv[],
602 gboolean request_completion,
603 const gchar *completion_cur,
604 const gchar *completion_prev)
605{
606 gint ret;
607 GOptionContext *o;
608 gchar *s;
609 GError *error;
610 GDBusConnection *c;
611 GVariant *parameters;
612 gchar *interface_name;
613 gchar *signal_name;
614 GVariantBuilder builder;
615 gboolean skip_dashes;
616 guint parm;
617 guint n;
618 gboolean complete_names, complete_paths, complete_signals;
619
620 ret = FALSE;
621 c = NULL;
622 parameters = NULL;
623 interface_name = NULL;
624 signal_name = NULL;
625
626 modify_argv0_for_command (argc, argv, command: "emit");
627
628 o = command_option_context_new (NULL, _("Emit a signal."),
629 entries: emit_entries, request_completion);
630 g_option_context_add_group (context: o, group: connection_get_group ());
631
632 complete_names = FALSE;
633 if (request_completion && *argc > 1 && g_strcmp0 (str1: (*argv)[(*argc)-1], str2: "--dest") == 0)
634 {
635 complete_names = TRUE;
636 remove_arg (num: (*argc) - 1, argc, argv);
637 }
638
639 complete_paths = FALSE;
640 if (request_completion && *argc > 1 && g_strcmp0 (str1: (*argv)[(*argc)-1], str2: "--object-path") == 0)
641 {
642 complete_paths = TRUE;
643 remove_arg (num: (*argc) - 1, argc, argv);
644 }
645
646 complete_signals = FALSE;
647 if (request_completion && *argc > 1 && g_strcmp0 (str1: (*argv)[(*argc)-1], str2: "--signal") == 0)
648 {
649 complete_signals = TRUE;
650 remove_arg (num: (*argc) - 1, argc, argv);
651 }
652
653 if (!g_option_context_parse (context: o, argc, argv, NULL))
654 {
655 if (!request_completion)
656 {
657 s = g_option_context_get_help (context: o, FALSE, NULL);
658 g_printerr (format: "%s", s);
659 g_free (mem: s);
660 goto out;
661 }
662 }
663
664 error = NULL;
665 c = connection_get_dbus_connection (require_message_bus: (opt_emit_dest != NULL), error: &error);
666 if (c == NULL)
667 {
668 if (request_completion)
669 {
670 if (g_strcmp0 (str1: completion_prev, str2: "--address") == 0)
671 {
672 g_print (format: "unix:\n"
673 "tcp:\n"
674 "nonce-tcp:\n");
675 }
676 else
677 {
678 g_print (format: "--system \n--session \n--address \n");
679 }
680 }
681 else
682 {
683 g_printerr (_("Error connecting: %s\n"), error->message);
684 }
685 g_error_free (error);
686 goto out;
687 }
688
689 /* validate and complete destination (bus name) */
690 if (complete_names)
691 {
692 print_names (c, FALSE);
693 goto out;
694 }
695 if (request_completion && opt_emit_dest != NULL && g_strcmp0 (str1: "--dest", str2: completion_prev) == 0)
696 {
697 print_names (c, include_unique_names: g_str_has_prefix (str: opt_emit_dest, prefix: ":"));
698 goto out;
699 }
700
701 if (!request_completion && opt_emit_dest != NULL && !g_dbus_is_unique_name (string: opt_emit_dest))
702 {
703 g_printerr (_("Error: %s is not a valid unique bus name.\n"), opt_emit_dest);
704 goto out;
705 }
706
707 if (opt_emit_dest == NULL && opt_emit_object_path == NULL && request_completion)
708 {
709 g_print (format: "--dest \n");
710 }
711 /* validate and complete object path */
712 if (opt_emit_dest != NULL && complete_paths)
713 {
714 print_paths (c, name: opt_emit_dest, path: "/");
715 goto out;
716 }
717 if (opt_emit_object_path == NULL)
718 {
719 if (request_completion)
720 g_print (format: "--object-path \n");
721 else
722 g_printerr (_("Error: Object path is not specified\n"));
723 goto out;
724 }
725 if (request_completion && g_strcmp0 (str1: "--object-path", str2: completion_prev) == 0)
726 {
727 if (opt_emit_dest != NULL)
728 {
729 gchar *p;
730 s = g_strdup (str: opt_emit_object_path);
731 p = strrchr (s: s, c: '/');
732 if (p != NULL)
733 {
734 if (p == s)
735 p++;
736 *p = '\0';
737 }
738 print_paths (c, name: opt_emit_dest, path: s);
739 g_free (mem: s);
740 }
741 goto out;
742 }
743 if (!request_completion && !g_variant_is_object_path (string: opt_emit_object_path))
744 {
745 g_printerr (_("Error: %s is not a valid object path\n"), opt_emit_object_path);
746 goto out;
747 }
748
749 /* validate and complete signal (interface + signal name) */
750 if (opt_emit_dest != NULL && opt_emit_object_path != NULL && complete_signals)
751 {
752 print_methods_and_signals (c, name: opt_emit_dest, path: opt_emit_object_path, FALSE, TRUE);
753 goto out;
754 }
755 if (opt_emit_signal == NULL)
756 {
757 /* don't keep repeatedly completing --signal */
758 if (request_completion)
759 {
760 if (g_strcmp0 (str1: "--signal", str2: completion_prev) != 0)
761 g_print (format: "--signal \n");
762 }
763 else
764 {
765 g_printerr (_("Error: Signal name is not specified\n"));
766 }
767
768 goto out;
769 }
770 if (request_completion && opt_emit_dest != NULL && opt_emit_object_path != NULL &&
771 g_strcmp0 (str1: "--signal", str2: completion_prev) == 0)
772 {
773 print_methods_and_signals (c, name: opt_emit_dest, path: opt_emit_object_path, FALSE, TRUE);
774 goto out;
775 }
776 s = strrchr (s: opt_emit_signal, c: '.');
777 if (!request_completion && s == NULL)
778 {
779 g_printerr (_("Error: Signal name “%s” is invalid\n"), opt_emit_signal);
780 goto out;
781 }
782 signal_name = g_strdup (str: s + 1);
783 interface_name = g_strndup (str: opt_emit_signal, n: s - opt_emit_signal);
784
785 /* All done with completion now */
786 if (request_completion)
787 goto out;
788
789 if (!g_dbus_is_interface_name (string: interface_name))
790 {
791 g_printerr (_("Error: %s is not a valid interface name\n"), interface_name);
792 goto out;
793 }
794
795 if (!g_dbus_is_member_name (string: signal_name))
796 {
797 g_printerr (_("Error: %s is not a valid member name\n"), signal_name);
798 goto out;
799 }
800
801 /* Read parameters */
802 g_variant_builder_init (builder: &builder, G_VARIANT_TYPE_TUPLE);
803 skip_dashes = TRUE;
804 parm = 0;
805 for (n = 1; n < (guint) *argc; n++)
806 {
807 GVariant *value;
808
809 /* Under certain conditions, g_option_context_parse returns the "--"
810 itself (setting off unparsed arguments), too: */
811 if (skip_dashes && g_strcmp0 (str1: (*argv)[n], str2: "--") == 0)
812 {
813 skip_dashes = FALSE;
814 continue;
815 }
816
817 error = NULL;
818 value = g_variant_parse (NULL,
819 text: (*argv)[n],
820 NULL,
821 NULL,
822 error: &error);
823 if (value == NULL)
824 {
825 gchar *context;
826
827 context = g_variant_parse_error_print_context (error, source_str: (*argv)[n]);
828 g_error_free (error);
829 error = NULL;
830 value = _g_variant_parse_me_harder (NULL, given_str: (*argv)[n], error: &error);
831 if (value == NULL)
832 {
833 /* Use the original non-"parse-me-harder" error */
834 g_printerr (_("Error parsing parameter %d: %s\n"),
835 parm + 1,
836 context);
837 g_error_free (error);
838 g_free (mem: context);
839 g_variant_builder_clear (builder: &builder);
840 goto out;
841 }
842 g_free (mem: context);
843 }
844 g_variant_builder_add_value (builder: &builder, value);
845 ++parm;
846 }
847 parameters = g_variant_builder_end (builder: &builder);
848
849 if (parameters != NULL)
850 parameters = g_variant_ref_sink (value: parameters);
851 if (!g_dbus_connection_emit_signal (connection: c,
852 destination_bus_name: opt_emit_dest,
853 object_path: opt_emit_object_path,
854 interface_name,
855 signal_name,
856 parameters,
857 error: &error))
858 {
859 g_printerr (_("Error: %s\n"), error->message);
860 g_error_free (error);
861 goto out;
862 }
863
864 if (!g_dbus_connection_flush_sync (connection: c, NULL, error: &error))
865 {
866 g_printerr (_("Error flushing connection: %s\n"), error->message);
867 g_error_free (error);
868 goto out;
869 }
870
871 ret = TRUE;
872
873 out:
874 if (c != NULL)
875 g_object_unref (object: c);
876 if (parameters != NULL)
877 g_variant_unref (value: parameters);
878 g_free (mem: interface_name);
879 g_free (mem: signal_name);
880 g_option_context_free (context: o);
881 return ret;
882}
883
884/* ---------------------------------------------------------------------------------------------------- */
885
886static gchar *opt_call_dest = NULL;
887static gchar *opt_call_object_path = NULL;
888static gchar *opt_call_method = NULL;
889static gint opt_call_timeout = -1;
890
891static const GOptionEntry call_entries[] =
892{
893 { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_call_dest, N_("Destination name to invoke method on"), NULL},
894 { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_call_object_path, N_("Object path to invoke method on"), NULL},
895 { "method", 'm', 0, G_OPTION_ARG_STRING, &opt_call_method, N_("Method and interface name"), NULL},
896 { "timeout", 't', 0, G_OPTION_ARG_INT, &opt_call_timeout, N_("Timeout in seconds"), NULL},
897 { NULL }
898};
899
900static gboolean
901handle_call (gint *argc,
902 gchar **argv[],
903 gboolean request_completion,
904 const gchar *completion_cur,
905 const gchar *completion_prev)
906{
907 gint ret;
908 GOptionContext *o;
909 gchar *s;
910 GError *error;
911 GDBusConnection *c;
912 GVariant *parameters;
913 gchar *interface_name;
914 gchar *method_name;
915 GVariant *result;
916 GPtrArray *in_signature_types;
917#ifdef G_OS_UNIX
918 GUnixFDList *fd_list;
919 gint fd_id;
920#endif
921 gboolean complete_names;
922 gboolean complete_paths;
923 gboolean complete_methods;
924 GVariantBuilder builder;
925 gboolean skip_dashes;
926 guint parm;
927 guint n;
928
929 ret = FALSE;
930 c = NULL;
931 parameters = NULL;
932 interface_name = NULL;
933 method_name = NULL;
934 result = NULL;
935 in_signature_types = NULL;
936#ifdef G_OS_UNIX
937 fd_list = NULL;
938#endif
939
940 modify_argv0_for_command (argc, argv, command: "call");
941
942 o = command_option_context_new (NULL, _("Invoke a method on a remote object."),
943 entries: call_entries, request_completion);
944 g_option_context_add_group (context: o, group: connection_get_group ());
945
946 complete_names = FALSE;
947 if (request_completion && *argc > 1 && g_strcmp0 (str1: (*argv)[(*argc)-1], str2: "--dest") == 0)
948 {
949 complete_names = TRUE;
950 remove_arg (num: (*argc) - 1, argc, argv);
951 }
952
953 complete_paths = FALSE;
954 if (request_completion && *argc > 1 && g_strcmp0 (str1: (*argv)[(*argc)-1], str2: "--object-path") == 0)
955 {
956 complete_paths = TRUE;
957 remove_arg (num: (*argc) - 1, argc, argv);
958 }
959
960 complete_methods = FALSE;
961 if (request_completion && *argc > 1 && g_strcmp0 (str1: (*argv)[(*argc)-1], str2: "--method") == 0)
962 {
963 complete_methods = TRUE;
964 remove_arg (num: (*argc) - 1, argc, argv);
965 }
966
967 if (!g_option_context_parse (context: o, argc, argv, NULL))
968 {
969 if (!request_completion)
970 {
971 s = g_option_context_get_help (context: o, FALSE, NULL);
972 g_printerr (format: "%s", s);
973 g_free (mem: s);
974 goto out;
975 }
976 }
977
978 error = NULL;
979 c = connection_get_dbus_connection (TRUE, error: &error);
980 if (c == NULL)
981 {
982 if (request_completion)
983 {
984 if (g_strcmp0 (str1: completion_prev, str2: "--address") == 0)
985 {
986 g_print (format: "unix:\n"
987 "tcp:\n"
988 "nonce-tcp:\n");
989 }
990 else
991 {
992 g_print (format: "--system \n--session \n--address \n");
993 }
994 }
995 else
996 {
997 g_printerr (_("Error connecting: %s\n"), error->message);
998 }
999 g_error_free (error);
1000 goto out;
1001 }
1002
1003 /* validate and complete destination (bus name) */
1004 if (complete_names)
1005 {
1006 print_names (c, FALSE);
1007 goto out;
1008 }
1009 if (opt_call_dest == NULL)
1010 {
1011 if (request_completion)
1012 g_print (format: "--dest \n");
1013 else
1014 g_printerr (_("Error: Destination is not specified\n"));
1015 goto out;
1016 }
1017 if (request_completion && g_strcmp0 (str1: "--dest", str2: completion_prev) == 0)
1018 {
1019 print_names (c, include_unique_names: g_str_has_prefix (str: opt_call_dest, prefix: ":"));
1020 goto out;
1021 }
1022
1023 if (!request_completion && !g_dbus_is_name (string: opt_call_dest))
1024 {
1025 g_printerr (_("Error: %s is not a valid bus name\n"), opt_call_dest);
1026 goto out;
1027 }
1028
1029 /* validate and complete object path */
1030 if (complete_paths)
1031 {
1032 print_paths (c, name: opt_call_dest, path: "/");
1033 goto out;
1034 }
1035 if (opt_call_object_path == NULL)
1036 {
1037 if (request_completion)
1038 g_print (format: "--object-path \n");
1039 else
1040 g_printerr (_("Error: Object path is not specified\n"));
1041 goto out;
1042 }
1043 if (request_completion && g_strcmp0 (str1: "--object-path", str2: completion_prev) == 0)
1044 {
1045 gchar *p;
1046 s = g_strdup (str: opt_call_object_path);
1047 p = strrchr (s: s, c: '/');
1048 if (p != NULL)
1049 {
1050 if (p == s)
1051 p++;
1052 *p = '\0';
1053 }
1054 print_paths (c, name: opt_call_dest, path: s);
1055 g_free (mem: s);
1056 goto out;
1057 }
1058 if (!request_completion && !g_variant_is_object_path (string: opt_call_object_path))
1059 {
1060 g_printerr (_("Error: %s is not a valid object path\n"), opt_call_object_path);
1061 goto out;
1062 }
1063
1064 /* validate and complete method (interface + method name) */
1065 if (complete_methods)
1066 {
1067 print_methods_and_signals (c, name: opt_call_dest, path: opt_call_object_path, TRUE, FALSE);
1068 goto out;
1069 }
1070 if (opt_call_method == NULL)
1071 {
1072 if (request_completion)
1073 g_print (format: "--method \n");
1074 else
1075 g_printerr (_("Error: Method name is not specified\n"));
1076 goto out;
1077 }
1078 if (request_completion && g_strcmp0 (str1: "--method", str2: completion_prev) == 0)
1079 {
1080 print_methods_and_signals (c, name: opt_call_dest, path: opt_call_object_path, TRUE, FALSE);
1081 goto out;
1082 }
1083 s = strrchr (s: opt_call_method, c: '.');
1084 if (!request_completion && s == NULL)
1085 {
1086 g_printerr (_("Error: Method name “%s” is invalid\n"), opt_call_method);
1087 goto out;
1088 }
1089 method_name = g_strdup (str: s + 1);
1090 interface_name = g_strndup (str: opt_call_method, n: s - opt_call_method);
1091
1092 /* All done with completion now */
1093 if (request_completion)
1094 goto out;
1095
1096 /* Introspect, for easy conversion - it's not fatal if we can't do this */
1097 in_signature_types = call_helper_get_method_in_signature (c,
1098 dest: opt_call_dest,
1099 path: opt_call_object_path,
1100 interface_name,
1101 method_name,
1102 error: &error);
1103 if (in_signature_types == NULL)
1104 {
1105 //g_printerr ("Error getting introspection data: %s\n", error->message);
1106 g_error_free (error);
1107 error = NULL;
1108 }
1109
1110 /* Read parameters */
1111 g_variant_builder_init (builder: &builder, G_VARIANT_TYPE_TUPLE);
1112 skip_dashes = TRUE;
1113 parm = 0;
1114 for (n = 1; n < (guint) *argc; n++)
1115 {
1116 GVariant *value;
1117 GVariantType *type;
1118
1119 /* Under certain conditions, g_option_context_parse returns the "--"
1120 itself (setting off unparsed arguments), too: */
1121 if (skip_dashes && g_strcmp0 (str1: (*argv)[n], str2: "--") == 0)
1122 {
1123 skip_dashes = FALSE;
1124 continue;
1125 }
1126
1127 type = NULL;
1128 if (in_signature_types != NULL)
1129 {
1130 if (parm >= in_signature_types->len)
1131 {
1132 /* Only warn for the first param */
1133 if (parm == in_signature_types->len)
1134 {
1135 g_printerr (format: "Warning: Introspection data indicates %d parameters but more was passed\n",
1136 in_signature_types->len);
1137 }
1138 }
1139 else
1140 {
1141 type = in_signature_types->pdata[parm];
1142 }
1143 }
1144
1145 error = NULL;
1146 value = g_variant_parse (type,
1147 text: (*argv)[n],
1148 NULL,
1149 NULL,
1150 error: &error);
1151 if (value == NULL)
1152 {
1153 gchar *context;
1154
1155 context = g_variant_parse_error_print_context (error, source_str: (*argv)[n]);
1156 g_error_free (error);
1157 error = NULL;
1158 value = _g_variant_parse_me_harder (type, given_str: (*argv)[n], error: &error);
1159 if (value == NULL)
1160 {
1161 if (type != NULL)
1162 {
1163 s = g_variant_type_dup_string (type);
1164 g_printerr (_("Error parsing parameter %d of type “%s”: %s\n"),
1165 parm + 1,
1166 s,
1167 context);
1168 g_free (mem: s);
1169 }
1170 else
1171 {
1172 g_printerr (_("Error parsing parameter %d: %s\n"),
1173 parm + 1,
1174 context);
1175 }
1176 g_error_free (error);
1177 g_variant_builder_clear (builder: &builder);
1178 g_free (mem: context);
1179 goto out;
1180 }
1181 g_free (mem: context);
1182 }
1183#ifdef G_OS_UNIX
1184 if (g_variant_is_of_type (value, G_VARIANT_TYPE_HANDLE))
1185 {
1186 if (!fd_list)
1187 fd_list = g_unix_fd_list_new ();
1188 if ((fd_id = g_unix_fd_list_append (list: fd_list, fd: g_variant_get_handle (value), error: &error)) == -1)
1189 {
1190 g_printerr (_("Error adding handle %d: %s\n"),
1191 g_variant_get_handle (value), error->message);
1192 g_variant_builder_clear (builder: &builder);
1193 g_error_free (error);
1194 goto out;
1195 }
1196 g_variant_unref (value);
1197 value = g_variant_new_handle (value: fd_id);
1198 }
1199#endif
1200 g_variant_builder_add_value (builder: &builder, value);
1201 ++parm;
1202 }
1203 parameters = g_variant_builder_end (builder: &builder);
1204
1205 if (parameters != NULL)
1206 parameters = g_variant_ref_sink (value: parameters);
1207#ifdef G_OS_UNIX
1208 result = g_dbus_connection_call_with_unix_fd_list_sync (connection: c,
1209 bus_name: opt_call_dest,
1210 object_path: opt_call_object_path,
1211 interface_name,
1212 method_name,
1213 parameters,
1214 NULL,
1215 flags: G_DBUS_CALL_FLAGS_NONE,
1216 timeout_msec: opt_call_timeout > 0 ? opt_call_timeout * 1000 : opt_call_timeout,
1217 fd_list,
1218 NULL,
1219 NULL,
1220 error: &error);
1221#else
1222 result = g_dbus_connection_call_sync (c,
1223 opt_call_dest,
1224 opt_call_object_path,
1225 interface_name,
1226 method_name,
1227 parameters,
1228 NULL,
1229 G_DBUS_CALL_FLAGS_NONE,
1230 opt_call_timeout > 0 ? opt_call_timeout * 1000 : opt_call_timeout,
1231 NULL,
1232 &error);
1233#endif
1234 if (result == NULL)
1235 {
1236 g_printerr (_("Error: %s\n"), error->message);
1237
1238 if (g_error_matches (error, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS) && in_signature_types != NULL)
1239 {
1240 if (in_signature_types->len > 0)
1241 {
1242 GString *s;
1243 s = g_string_new (NULL);
1244
1245 for (n = 0; n < in_signature_types->len; n++)
1246 {
1247 GVariantType *type = in_signature_types->pdata[n];
1248 g_string_append_len (string: s,
1249 val: g_variant_type_peek_string (type),
1250 len: g_variant_type_get_string_length (type));
1251 }
1252
1253 g_printerr (format: "(According to introspection data, you need to pass '%s')\n", s->str);
1254 g_string_free (string: s, TRUE);
1255 }
1256 else
1257 g_printerr (format: "(According to introspection data, you need to pass no arguments)\n");
1258 }
1259
1260 g_error_free (error);
1261 goto out;
1262 }
1263
1264 s = g_variant_print (value: result, TRUE);
1265 g_print (format: "%s\n", s);
1266 g_free (mem: s);
1267
1268 ret = TRUE;
1269
1270 out:
1271 if (in_signature_types != NULL)
1272 g_ptr_array_unref (array: in_signature_types);
1273 if (result != NULL)
1274 g_variant_unref (value: result);
1275 if (c != NULL)
1276 g_object_unref (object: c);
1277 if (parameters != NULL)
1278 g_variant_unref (value: parameters);
1279 g_free (mem: interface_name);
1280 g_free (mem: method_name);
1281 g_option_context_free (context: o);
1282#ifdef G_OS_UNIX
1283 g_clear_object (&fd_list);
1284#endif
1285 return ret;
1286}
1287
1288/* ---------------------------------------------------------------------------------------------------- */
1289
1290static gchar *opt_introspect_dest = NULL;
1291static gchar *opt_introspect_object_path = NULL;
1292static gboolean opt_introspect_xml = FALSE;
1293static gboolean opt_introspect_recurse = FALSE;
1294static gboolean opt_introspect_only_properties = FALSE;
1295
1296/* Introspect colors */
1297#define RESET_COLOR (use_colors? "\033[0m": "")
1298#define INTROSPECT_TITLE_COLOR (use_colors? UNDERLINE: "")
1299#define INTROSPECT_NODE_COLOR (use_colors? RESET_COLOR: "")
1300#define INTROSPECT_INTERFACE_COLOR (use_colors? YELLOW: "")
1301#define INTROSPECT_METHOD_COLOR (use_colors? BLUE: "")
1302#define INTROSPECT_SIGNAL_COLOR (use_colors? BLUE: "")
1303#define INTROSPECT_PROPERTY_COLOR (use_colors? MAGENTA: "")
1304#define INTROSPECT_INOUT_COLOR (use_colors? RESET_COLOR: "")
1305#define INTROSPECT_TYPE_COLOR (use_colors? GREEN: "")
1306#define INTROSPECT_ANNOTATION_COLOR (use_colors? RESET_COLOR: "")
1307
1308static void
1309dump_annotation (const GDBusAnnotationInfo *o,
1310 guint indent,
1311 gboolean ignore_indent,
1312 gboolean use_colors)
1313{
1314 guint n;
1315 g_print (format: "%*s%s@%s(\"%s\")%s\n",
1316 ignore_indent ? 0 : indent, "",
1317 INTROSPECT_ANNOTATION_COLOR, o->key, o->value, RESET_COLOR);
1318 for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
1319 dump_annotation (o: o->annotations[n], indent: indent + 2, FALSE, use_colors);
1320}
1321
1322static void
1323dump_arg (const GDBusArgInfo *o,
1324 guint indent,
1325 const gchar *direction,
1326 gboolean ignore_indent,
1327 gboolean include_newline,
1328 gboolean use_colors)
1329{
1330 guint n;
1331
1332 for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
1333 {
1334 dump_annotation (o: o->annotations[n], indent, ignore_indent, use_colors);
1335 ignore_indent = FALSE;
1336 }
1337
1338 g_print (format: "%*s%s%s%s%s%s%s %s%s",
1339 ignore_indent ? 0 : indent, "",
1340 INTROSPECT_INOUT_COLOR, direction, RESET_COLOR,
1341 INTROSPECT_TYPE_COLOR, o->signature, RESET_COLOR,
1342 o->name,
1343 include_newline ? ",\n" : "");
1344}
1345
1346static guint
1347count_args (GDBusArgInfo **args)
1348{
1349 guint n;
1350 n = 0;
1351 if (args == NULL)
1352 goto out;
1353 while (args[n] != NULL)
1354 n++;
1355 out:
1356 return n;
1357}
1358
1359static void
1360dump_method (const GDBusMethodInfo *o,
1361 guint indent,
1362 gboolean use_colors)
1363{
1364 guint n;
1365 guint m;
1366 guint name_len;
1367 guint total_num_args;
1368
1369 for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
1370 dump_annotation (o: o->annotations[n], indent, FALSE, use_colors);
1371
1372 g_print (format: "%*s%s%s%s(",
1373 indent, "",
1374 INTROSPECT_METHOD_COLOR, o->name, RESET_COLOR);
1375 name_len = strlen (s: o->name);
1376 total_num_args = count_args (args: o->in_args) + count_args (args: o->out_args);
1377 for (n = 0, m = 0; o->in_args != NULL && o->in_args[n] != NULL; n++, m++)
1378 {
1379 gboolean ignore_indent = (m == 0);
1380 gboolean include_newline = (m != total_num_args - 1);
1381
1382 dump_arg (o: o->in_args[n],
1383 indent: indent + name_len + 1,
1384 direction: "in ",
1385 ignore_indent,
1386 include_newline,
1387 use_colors);
1388 }
1389 for (n = 0; o->out_args != NULL && o->out_args[n] != NULL; n++, m++)
1390 {
1391 gboolean ignore_indent = (m == 0);
1392 gboolean include_newline = (m != total_num_args - 1);
1393 dump_arg (o: o->out_args[n],
1394 indent: indent + name_len + 1,
1395 direction: "out ",
1396 ignore_indent,
1397 include_newline,
1398 use_colors);
1399 }
1400 g_print (format: ");\n");
1401}
1402
1403static void
1404dump_signal (const GDBusSignalInfo *o,
1405 guint indent,
1406 gboolean use_colors)
1407{
1408 guint n;
1409 guint name_len;
1410 guint total_num_args;
1411
1412 for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
1413 dump_annotation (o: o->annotations[n], indent, FALSE, use_colors);
1414
1415 g_print (format: "%*s%s%s%s(",
1416 indent, "",
1417 INTROSPECT_SIGNAL_COLOR, o->name, RESET_COLOR);
1418 name_len = strlen (s: o->name);
1419 total_num_args = count_args (args: o->args);
1420 for (n = 0; o->args != NULL && o->args[n] != NULL; n++)
1421 {
1422 gboolean ignore_indent = (n == 0);
1423 gboolean include_newline = (n != total_num_args - 1);
1424 dump_arg (o: o->args[n],
1425 indent: indent + name_len + 1,
1426 direction: "",
1427 ignore_indent,
1428 include_newline,
1429 use_colors);
1430 }
1431 g_print (format: ");\n");
1432}
1433
1434static void
1435dump_property (const GDBusPropertyInfo *o,
1436 guint indent,
1437 gboolean use_colors,
1438 GVariant *value)
1439{
1440 const gchar *access;
1441 guint n;
1442
1443 if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_READABLE)
1444 access = "readonly";
1445 else if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)
1446 access = "writeonly";
1447 else if (o->flags == (G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE))
1448 access = "readwrite";
1449 else
1450 g_assert_not_reached ();
1451
1452 for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
1453 dump_annotation (o: o->annotations[n], indent, FALSE, use_colors);
1454
1455 if (value != NULL)
1456 {
1457 gchar *s = g_variant_print (value, FALSE);
1458 g_print (format: "%*s%s %s%s%s %s%s%s = %s;\n", indent, "", access,
1459 INTROSPECT_TYPE_COLOR, o->signature, RESET_COLOR,
1460 INTROSPECT_PROPERTY_COLOR, o->name, RESET_COLOR,
1461 s);
1462 g_free (mem: s);
1463 }
1464 else
1465 {
1466 g_print (format: "%*s%s %s %s;\n", indent, "", access, o->signature, o->name);
1467 }
1468}
1469
1470static void
1471dump_interface (GDBusConnection *c,
1472 const gchar *name,
1473 const GDBusInterfaceInfo *o,
1474 guint indent,
1475 gboolean use_colors,
1476 const gchar *object_path)
1477{
1478 guint n;
1479 GHashTable *properties;
1480
1481 properties = g_hash_table_new_full (hash_func: g_str_hash,
1482 key_equal_func: g_str_equal,
1483 key_destroy_func: g_free,
1484 value_destroy_func: (GDestroyNotify) g_variant_unref);
1485
1486 /* Try to get properties */
1487 if (c != NULL && name != NULL && object_path != NULL && o->properties != NULL)
1488 {
1489 GVariant *result;
1490 result = g_dbus_connection_call_sync (connection: c,
1491 bus_name: name,
1492 object_path,
1493 interface_name: "org.freedesktop.DBus.Properties",
1494 method_name: "GetAll",
1495 parameters: g_variant_new (format_string: "(s)", o->name),
1496 NULL,
1497 flags: G_DBUS_CALL_FLAGS_NONE,
1498 timeout_msec: 3000,
1499 NULL,
1500 NULL);
1501 if (result != NULL)
1502 {
1503 if (g_variant_is_of_type (value: result, G_VARIANT_TYPE ("(a{sv})")))
1504 {
1505 GVariantIter *iter;
1506 GVariant *item;
1507 g_variant_get (value: result,
1508 format_string: "(a{sv})",
1509 &iter);
1510 while ((item = g_variant_iter_next_value (iter)))
1511 {
1512 gchar *key;
1513 GVariant *value;
1514 g_variant_get (value: item,
1515 format_string: "{sv}",
1516 &key,
1517 &value);
1518
1519 g_hash_table_insert (hash_table: properties, key, value: g_variant_ref (value));
1520 }
1521 }
1522 g_variant_unref (value: result);
1523 }
1524 else
1525 {
1526 guint n;
1527 for (n = 0; o->properties != NULL && o->properties[n] != NULL; n++)
1528 {
1529 result = g_dbus_connection_call_sync (connection: c,
1530 bus_name: name,
1531 object_path,
1532 interface_name: "org.freedesktop.DBus.Properties",
1533 method_name: "Get",
1534 parameters: g_variant_new (format_string: "(ss)", o->name, o->properties[n]->name),
1535 G_VARIANT_TYPE ("(v)"),
1536 flags: G_DBUS_CALL_FLAGS_NONE,
1537 timeout_msec: 3000,
1538 NULL,
1539 NULL);
1540 if (result != NULL)
1541 {
1542 GVariant *property_value;
1543 g_variant_get (value: result,
1544 format_string: "(v)",
1545 &property_value);
1546 g_hash_table_insert (hash_table: properties,
1547 key: g_strdup (str: o->properties[n]->name),
1548 value: g_variant_ref (value: property_value));
1549 g_variant_unref (value: result);
1550 }
1551 }
1552 }
1553 }
1554
1555 for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
1556 dump_annotation (o: o->annotations[n], indent, FALSE, use_colors);
1557
1558 g_print (format: "%*s%sinterface %s%s {\n",
1559 indent, "",
1560 INTROSPECT_INTERFACE_COLOR, o->name, RESET_COLOR);
1561 if (o->methods != NULL && !opt_introspect_only_properties)
1562 {
1563 g_print (format: "%*s %smethods%s:\n",
1564 indent, "",
1565 INTROSPECT_TITLE_COLOR, RESET_COLOR);
1566 for (n = 0; o->methods[n] != NULL; n++)
1567 dump_method (o: o->methods[n], indent: indent + 4, use_colors);
1568 }
1569 if (o->signals != NULL && !opt_introspect_only_properties)
1570 {
1571 g_print (format: "%*s %ssignals%s:\n",
1572 indent, "",
1573 INTROSPECT_TITLE_COLOR, RESET_COLOR);
1574 for (n = 0; o->signals[n] != NULL; n++)
1575 dump_signal (o: o->signals[n], indent: indent + 4, use_colors);
1576 }
1577 if (o->properties != NULL)
1578 {
1579 g_print (format: "%*s %sproperties%s:\n",
1580 indent, "",
1581 INTROSPECT_TITLE_COLOR, RESET_COLOR);
1582 for (n = 0; o->properties[n] != NULL; n++)
1583 {
1584 dump_property (o: o->properties[n],
1585 indent: indent + 4,
1586 use_colors,
1587 value: g_hash_table_lookup (hash_table: properties, key: (o->properties[n])->name));
1588 }
1589 }
1590 g_print (format: "%*s};\n",
1591 indent, "");
1592
1593 g_hash_table_unref (hash_table: properties);
1594}
1595
1596static gboolean
1597introspect_do (GDBusConnection *c,
1598 const gchar *object_path,
1599 guint indent,
1600 gboolean use_colors);
1601
1602static void
1603dump_node (GDBusConnection *c,
1604 const gchar *name,
1605 const GDBusNodeInfo *o,
1606 guint indent,
1607 gboolean use_colors,
1608 const gchar *object_path,
1609 gboolean recurse)
1610{
1611 guint n;
1612 const gchar *object_path_to_print;
1613
1614 object_path_to_print = object_path;
1615 if (o->path != NULL)
1616 object_path_to_print = o->path;
1617
1618 for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
1619 dump_annotation (o: o->annotations[n], indent, FALSE, use_colors);
1620
1621 g_print (format: "%*s%snode %s%s",
1622 indent, "",
1623 INTROSPECT_NODE_COLOR,
1624 object_path_to_print != NULL ? object_path_to_print : "(not set)",
1625 RESET_COLOR);
1626 if (o->interfaces != NULL || o->nodes != NULL)
1627 {
1628 g_print (format: " {\n");
1629 for (n = 0; o->interfaces != NULL && o->interfaces[n] != NULL; n++)
1630 {
1631 if (opt_introspect_only_properties)
1632 {
1633 if (o->interfaces[n]->properties != NULL && o->interfaces[n]->properties[0] != NULL)
1634 dump_interface (c, name, o: o->interfaces[n], indent: indent + 2, use_colors, object_path);
1635 }
1636 else
1637 {
1638 dump_interface (c, name, o: o->interfaces[n], indent: indent + 2, use_colors, object_path);
1639 }
1640 }
1641 for (n = 0; o->nodes != NULL && o->nodes[n] != NULL; n++)
1642 {
1643 if (recurse)
1644 {
1645 gchar *child_path;
1646 if (g_variant_is_object_path (string: o->nodes[n]->path))
1647 {
1648 child_path = g_strdup (str: o->nodes[n]->path);
1649 /* avoid infinite loops */
1650 if (g_str_has_prefix (str: child_path, prefix: object_path))
1651 {
1652 introspect_do (c, object_path: child_path, indent: indent + 2, use_colors);
1653 }
1654 else
1655 {
1656 g_print (format: "Skipping path %s that is not enclosed by parent %s\n",
1657 child_path, object_path);
1658 }
1659 }
1660 else
1661 {
1662 if (g_strcmp0 (str1: object_path, str2: "/") == 0)
1663 child_path = g_strdup_printf (format: "/%s", o->nodes[n]->path);
1664 else
1665 child_path = g_strdup_printf (format: "%s/%s", object_path, o->nodes[n]->path);
1666 introspect_do (c, object_path: child_path, indent: indent + 2, use_colors);
1667 }
1668 g_free (mem: child_path);
1669 }
1670 else
1671 {
1672 dump_node (NULL, NULL, o: o->nodes[n], indent: indent + 2, use_colors, NULL, recurse);
1673 }
1674 }
1675 g_print (format: "%*s};\n",
1676 indent, "");
1677 }
1678 else
1679 {
1680 g_print (format: "\n");
1681 }
1682}
1683
1684static const GOptionEntry introspect_entries[] =
1685{
1686 { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_introspect_dest, N_("Destination name to introspect"), NULL},
1687 { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_introspect_object_path, N_("Object path to introspect"), NULL},
1688 { "xml", 'x', 0, G_OPTION_ARG_NONE, &opt_introspect_xml, N_("Print XML"), NULL},
1689 { "recurse", 'r', 0, G_OPTION_ARG_NONE, &opt_introspect_recurse, N_("Introspect children"), NULL},
1690 { "only-properties", 'p', 0, G_OPTION_ARG_NONE, &opt_introspect_only_properties, N_("Only print properties"), NULL},
1691 { NULL }
1692};
1693
1694static gboolean
1695introspect_do (GDBusConnection *c,
1696 const gchar *object_path,
1697 guint indent,
1698 gboolean use_colors)
1699{
1700 GError *error;
1701 GVariant *result;
1702 GDBusNodeInfo *node;
1703 gboolean ret;
1704 const gchar *xml_data;
1705
1706 ret = FALSE;
1707 node = NULL;
1708 result = NULL;
1709
1710 error = NULL;
1711 result = g_dbus_connection_call_sync (connection: c,
1712 bus_name: opt_introspect_dest,
1713 object_path,
1714 interface_name: "org.freedesktop.DBus.Introspectable",
1715 method_name: "Introspect",
1716 NULL,
1717 G_VARIANT_TYPE ("(s)"),
1718 flags: G_DBUS_CALL_FLAGS_NONE,
1719 timeout_msec: 3000, /* 3 sec */
1720 NULL,
1721 error: &error);
1722 if (result == NULL)
1723 {
1724 g_printerr (_("Error: %s\n"), error->message);
1725 g_error_free (error);
1726 goto out;
1727 }
1728 g_variant_get (value: result, format_string: "(&s)", &xml_data);
1729
1730 if (opt_introspect_xml)
1731 {
1732 g_print (format: "%s", xml_data);
1733 }
1734 else
1735 {
1736 error = NULL;
1737 node = g_dbus_node_info_new_for_xml (xml_data, error: &error);
1738 if (node == NULL)
1739 {
1740 g_printerr (_("Error parsing introspection XML: %s\n"), error->message);
1741 g_error_free (error);
1742 goto out;
1743 }
1744
1745 dump_node (c, name: opt_introspect_dest, o: node, indent, use_colors, object_path, recurse: opt_introspect_recurse);
1746 }
1747
1748 ret = TRUE;
1749
1750 out:
1751 if (node != NULL)
1752 g_dbus_node_info_unref (info: node);
1753 if (result != NULL)
1754 g_variant_unref (value: result);
1755 return ret;
1756}
1757
1758static gboolean
1759handle_introspect (gint *argc,
1760 gchar **argv[],
1761 gboolean request_completion,
1762 const gchar *completion_cur,
1763 const gchar *completion_prev)
1764{
1765 gint ret;
1766 GOptionContext *o;
1767 gchar *s;
1768 GError *error;
1769 GDBusConnection *c;
1770 gboolean complete_names;
1771 gboolean complete_paths;
1772 gboolean color_support;
1773
1774 ret = FALSE;
1775 c = NULL;
1776
1777 modify_argv0_for_command (argc, argv, command: "introspect");
1778
1779 o = command_option_context_new (NULL, _("Introspect a remote object."),
1780 entries: introspect_entries, request_completion);
1781 g_option_context_add_group (context: o, group: connection_get_group ());
1782
1783 complete_names = FALSE;
1784 if (request_completion && *argc > 1 && g_strcmp0 (str1: (*argv)[(*argc)-1], str2: "--dest") == 0)
1785 {
1786 complete_names = TRUE;
1787 remove_arg (num: (*argc) - 1, argc, argv);
1788 }
1789
1790 complete_paths = FALSE;
1791 if (request_completion && *argc > 1 && g_strcmp0 (str1: (*argv)[(*argc)-1], str2: "--object-path") == 0)
1792 {
1793 complete_paths = TRUE;
1794 remove_arg (num: (*argc) - 1, argc, argv);
1795 }
1796
1797 if (!g_option_context_parse (context: o, argc, argv, NULL))
1798 {
1799 if (!request_completion)
1800 {
1801 s = g_option_context_get_help (context: o, FALSE, NULL);
1802 g_printerr (format: "%s", s);
1803 g_free (mem: s);
1804 goto out;
1805 }
1806 }
1807
1808 error = NULL;
1809 c = connection_get_dbus_connection (TRUE, error: &error);
1810 if (c == NULL)
1811 {
1812 if (request_completion)
1813 {
1814 if (g_strcmp0 (str1: completion_prev, str2: "--address") == 0)
1815 {
1816 g_print (format: "unix:\n"
1817 "tcp:\n"
1818 "nonce-tcp:\n");
1819 }
1820 else
1821 {
1822 g_print (format: "--system \n--session \n--address \n");
1823 }
1824 }
1825 else
1826 {
1827 g_printerr (_("Error connecting: %s\n"), error->message);
1828 }
1829 g_error_free (error);
1830 goto out;
1831 }
1832
1833 if (complete_names)
1834 {
1835 print_names (c, FALSE);
1836 goto out;
1837 }
1838 /* this only makes sense on message bus connections */
1839 if (opt_introspect_dest == NULL)
1840 {
1841 if (request_completion)
1842 g_print (format: "--dest \n");
1843 else
1844 g_printerr (_("Error: Destination is not specified\n"));
1845 goto out;
1846 }
1847 if (request_completion && g_strcmp0 (str1: "--dest", str2: completion_prev) == 0)
1848 {
1849 print_names (c, include_unique_names: g_str_has_prefix (str: opt_introspect_dest, prefix: ":"));
1850 goto out;
1851 }
1852
1853 if (complete_paths)
1854 {
1855 print_paths (c, name: opt_introspect_dest, path: "/");
1856 goto out;
1857 }
1858
1859 if (!request_completion && !g_dbus_is_name (string: opt_introspect_dest))
1860 {
1861 g_printerr (_("Error: %s is not a valid bus name\n"), opt_introspect_dest);
1862 goto out;
1863 }
1864
1865 if (opt_introspect_object_path == NULL)
1866 {
1867 if (request_completion)
1868 g_print (format: "--object-path \n");
1869 else
1870 g_printerr (_("Error: Object path is not specified\n"));
1871 goto out;
1872 }
1873 if (request_completion && g_strcmp0 (str1: "--object-path", str2: completion_prev) == 0)
1874 {
1875 gchar *p;
1876 s = g_strdup (str: opt_introspect_object_path);
1877 p = strrchr (s: s, c: '/');
1878 if (p != NULL)
1879 {
1880 if (p == s)
1881 p++;
1882 *p = '\0';
1883 }
1884 print_paths (c, name: opt_introspect_dest, path: s);
1885 g_free (mem: s);
1886 goto out;
1887 }
1888 if (!request_completion && !g_variant_is_object_path (string: opt_introspect_object_path))
1889 {
1890 g_printerr (_("Error: %s is not a valid object path\n"), opt_introspect_object_path);
1891 goto out;
1892 }
1893
1894 if (request_completion && opt_introspect_object_path != NULL && !opt_introspect_recurse)
1895 {
1896 g_print (format: "--recurse \n");
1897 }
1898
1899 if (request_completion && opt_introspect_object_path != NULL && !opt_introspect_only_properties)
1900 {
1901 g_print (format: "--only-properties \n");
1902 }
1903
1904 /* All done with completion now */
1905 if (request_completion)
1906 goto out;
1907
1908 /* Before we start printing the actual info, check if we can do colors*/
1909 color_support = g_log_writer_supports_color (output_fd: fileno (stdout));
1910
1911 if (!introspect_do (c, object_path: opt_introspect_object_path, indent: 0, use_colors: color_support))
1912 goto out;
1913
1914 ret = TRUE;
1915
1916 out:
1917 if (c != NULL)
1918 g_object_unref (object: c);
1919 g_option_context_free (context: o);
1920 return ret;
1921}
1922
1923/* ---------------------------------------------------------------------------------------------------- */
1924
1925static gchar *opt_monitor_dest = NULL;
1926static gchar *opt_monitor_object_path = NULL;
1927
1928static guint monitor_filter_id = 0;
1929
1930static void
1931monitor_signal_cb (GDBusConnection *connection,
1932 const gchar *sender_name,
1933 const gchar *object_path,
1934 const gchar *interface_name,
1935 const gchar *signal_name,
1936 GVariant *parameters,
1937 gpointer user_data)
1938{
1939 gchar *s;
1940 s = g_variant_print (value: parameters, TRUE);
1941 g_print (format: "%s: %s.%s %s\n",
1942 object_path,
1943 interface_name,
1944 signal_name,
1945 s);
1946 g_free (mem: s);
1947}
1948
1949static void
1950monitor_on_name_appeared (GDBusConnection *connection,
1951 const gchar *name,
1952 const gchar *name_owner,
1953 gpointer user_data)
1954{
1955 g_print (format: "The name %s is owned by %s\n", name, name_owner);
1956 g_assert (monitor_filter_id == 0);
1957 monitor_filter_id = g_dbus_connection_signal_subscribe (connection,
1958 sender: name_owner,
1959 NULL, /* any interface */
1960 NULL, /* any member */
1961 object_path: opt_monitor_object_path,
1962 NULL, /* arg0 */
1963 flags: G_DBUS_SIGNAL_FLAGS_NONE,
1964 callback: monitor_signal_cb,
1965 NULL, /* user_data */
1966 NULL); /* user_data destroy notify */
1967}
1968
1969static void
1970monitor_on_name_vanished (GDBusConnection *connection,
1971 const gchar *name,
1972 gpointer user_data)
1973{
1974 g_print (format: "The name %s does not have an owner\n", name);
1975
1976 if (monitor_filter_id != 0)
1977 {
1978 g_dbus_connection_signal_unsubscribe (connection, subscription_id: monitor_filter_id);
1979 monitor_filter_id = 0;
1980 }
1981}
1982
1983static const GOptionEntry monitor_entries[] =
1984{
1985 { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_monitor_dest, N_("Destination name to monitor"), NULL},
1986 { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_monitor_object_path, N_("Object path to monitor"), NULL},
1987 { NULL }
1988};
1989
1990static gboolean
1991handle_monitor (gint *argc,
1992 gchar **argv[],
1993 gboolean request_completion,
1994 const gchar *completion_cur,
1995 const gchar *completion_prev)
1996{
1997 gint ret;
1998 GOptionContext *o;
1999 gchar *s;
2000 GError *error;
2001 GDBusConnection *c;
2002 gboolean complete_names;
2003 gboolean complete_paths;
2004 GMainLoop *loop;
2005
2006 ret = FALSE;
2007 c = NULL;
2008
2009 modify_argv0_for_command (argc, argv, command: "monitor");
2010
2011 o = command_option_context_new (NULL, _("Monitor a remote object."),
2012 entries: monitor_entries, request_completion);
2013 g_option_context_add_group (context: o, group: connection_get_group ());
2014
2015 complete_names = FALSE;
2016 if (request_completion && *argc > 1 && g_strcmp0 (str1: (*argv)[(*argc)-1], str2: "--dest") == 0)
2017 {
2018 complete_names = TRUE;
2019 remove_arg (num: (*argc) - 1, argc, argv);
2020 }
2021
2022 complete_paths = FALSE;
2023 if (request_completion && *argc > 1 && g_strcmp0 (str1: (*argv)[(*argc)-1], str2: "--object-path") == 0)
2024 {
2025 complete_paths = TRUE;
2026 remove_arg (num: (*argc) - 1, argc, argv);
2027 }
2028
2029 if (!g_option_context_parse (context: o, argc, argv, NULL))
2030 {
2031 if (!request_completion)
2032 {
2033 s = g_option_context_get_help (context: o, FALSE, NULL);
2034 g_printerr (format: "%s", s);
2035 g_free (mem: s);
2036 goto out;
2037 }
2038 }
2039
2040 error = NULL;
2041 c = connection_get_dbus_connection (TRUE, error: &error);
2042 if (c == NULL)
2043 {
2044 if (request_completion)
2045 {
2046 if (g_strcmp0 (str1: completion_prev, str2: "--address") == 0)
2047 {
2048 g_print (format: "unix:\n"
2049 "tcp:\n"
2050 "nonce-tcp:\n");
2051 }
2052 else
2053 {
2054 g_print (format: "--system \n--session \n--address \n");
2055 }
2056 }
2057 else
2058 {
2059 g_printerr (_("Error connecting: %s\n"), error->message);
2060 }
2061 g_error_free (error);
2062 goto out;
2063 }
2064
2065 /* Monitoring doesn’t make sense on a non-message-bus connection. */
2066 if (g_dbus_connection_get_unique_name (connection: c) == NULL)
2067 {
2068 if (!request_completion)
2069 g_printerr (_("Error: can’t monitor a non-message-bus connection\n"));
2070 goto out;
2071 }
2072
2073 if (complete_names)
2074 {
2075 print_names (c, FALSE);
2076 goto out;
2077 }
2078 /* this only makes sense on message bus connections */
2079 if (opt_monitor_dest == NULL)
2080 {
2081 if (request_completion)
2082 g_print (format: "--dest \n");
2083 else
2084 g_printerr (_("Error: Destination is not specified\n"));
2085 goto out;
2086 }
2087 if (request_completion && g_strcmp0 (str1: "--dest", str2: completion_prev) == 0)
2088 {
2089 print_names (c, include_unique_names: g_str_has_prefix (str: opt_monitor_dest, prefix: ":"));
2090 goto out;
2091 }
2092
2093 if (!request_completion && !g_dbus_is_name (string: opt_monitor_dest))
2094 {
2095 g_printerr (_("Error: %s is not a valid bus name\n"), opt_monitor_dest);
2096 goto out;
2097 }
2098
2099 if (complete_paths)
2100 {
2101 print_paths (c, name: opt_monitor_dest, path: "/");
2102 goto out;
2103 }
2104 if (opt_monitor_object_path == NULL)
2105 {
2106 if (request_completion)
2107 {
2108 g_print (format: "--object-path \n");
2109 goto out;
2110 }
2111 /* it's fine to not have an object path */
2112 }
2113 if (request_completion && g_strcmp0 (str1: "--object-path", str2: completion_prev) == 0)
2114 {
2115 gchar *p;
2116 s = g_strdup (str: opt_monitor_object_path);
2117 p = strrchr (s: s, c: '/');
2118 if (p != NULL)
2119 {
2120 if (p == s)
2121 p++;
2122 *p = '\0';
2123 }
2124 print_paths (c, name: opt_monitor_dest, path: s);
2125 g_free (mem: s);
2126 goto out;
2127 }
2128 if (!request_completion && (opt_monitor_object_path != NULL && !g_variant_is_object_path (string: opt_monitor_object_path)))
2129 {
2130 g_printerr (_("Error: %s is not a valid object path\n"), opt_monitor_object_path);
2131 goto out;
2132 }
2133
2134 /* All done with completion now */
2135 if (request_completion)
2136 goto out;
2137
2138 if (opt_monitor_object_path != NULL)
2139 g_print (format: "Monitoring signals on object %s owned by %s\n", opt_monitor_object_path, opt_monitor_dest);
2140 else
2141 g_print (format: "Monitoring signals from all objects owned by %s\n", opt_monitor_dest);
2142
2143 loop = g_main_loop_new (NULL, FALSE);
2144 g_bus_watch_name_on_connection (connection: c,
2145 name: opt_monitor_dest,
2146 flags: G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
2147 name_appeared_handler: monitor_on_name_appeared,
2148 name_vanished_handler: monitor_on_name_vanished,
2149 NULL,
2150 NULL);
2151
2152 g_main_loop_run (loop);
2153 g_main_loop_unref (loop);
2154
2155 ret = TRUE;
2156
2157 out:
2158 if (c != NULL)
2159 g_object_unref (object: c);
2160 g_option_context_free (context: o);
2161 return ret;
2162}
2163
2164/* ---------------------------------------------------------------------------------------------------- */
2165
2166static gboolean opt_wait_activate_set = FALSE;
2167static gchar *opt_wait_activate_name = NULL;
2168static gint64 opt_wait_timeout_secs = 0; /* no timeout */
2169
2170typedef enum {
2171 WAIT_STATE_RUNNING, /* waiting to see the service */
2172 WAIT_STATE_SUCCESS, /* seen it successfully */
2173 WAIT_STATE_TIMEOUT, /* timed out before seeing it */
2174} WaitState;
2175
2176static gboolean
2177opt_wait_activate_cb (const gchar *option_name,
2178 const gchar *value,
2179 gpointer data,
2180 GError **error)
2181{
2182 /* @value may be NULL */
2183 opt_wait_activate_set = TRUE;
2184 opt_wait_activate_name = g_strdup (str: value);
2185
2186 return TRUE;
2187}
2188
2189static const GOptionEntry wait_entries[] =
2190{
2191 { "activate", 'a', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
2192 opt_wait_activate_cb,
2193 N_("Service to activate before waiting for the other one (well-known name)"),
2194 "[NAME]" },
2195 { "timeout", 't', 0, G_OPTION_ARG_INT64, &opt_wait_timeout_secs,
2196 N_("Timeout to wait for before exiting with an error (seconds); 0 for "
2197 "no timeout (default)"), "SECS" },
2198 { NULL }
2199};
2200
2201static void
2202wait_name_appeared_cb (GDBusConnection *connection,
2203 const gchar *name,
2204 const gchar *name_owner,
2205 gpointer user_data)
2206{
2207 WaitState *wait_state = user_data;
2208
2209 *wait_state = WAIT_STATE_SUCCESS;
2210}
2211
2212static gboolean
2213wait_timeout_cb (gpointer user_data)
2214{
2215 WaitState *wait_state = user_data;
2216
2217 *wait_state = WAIT_STATE_TIMEOUT;
2218
2219 /* Removed in handle_wait(). */
2220 return G_SOURCE_CONTINUE;
2221}
2222
2223static gboolean
2224handle_wait (gint *argc,
2225 gchar **argv[],
2226 gboolean request_completion,
2227 const gchar *completion_cur,
2228 const gchar *completion_prev)
2229{
2230 gint ret;
2231 GOptionContext *o;
2232 gchar *s;
2233 GError *error;
2234 GDBusConnection *c;
2235 guint watch_id, timer_id = 0, activate_watch_id;
2236 const gchar *activate_service, *wait_service;
2237 WaitState wait_state = WAIT_STATE_RUNNING;
2238
2239 ret = FALSE;
2240 c = NULL;
2241
2242 modify_argv0_for_command (argc, argv, command: "wait");
2243
2244 o = command_option_context_new (_("[OPTION…] BUS-NAME"),
2245 _("Wait for a bus name to appear."),
2246 entries: wait_entries, request_completion);
2247 g_option_context_add_group (context: o, group: connection_get_group ());
2248
2249 if (!g_option_context_parse (context: o, argc, argv, NULL))
2250 {
2251 if (!request_completion)
2252 {
2253 s = g_option_context_get_help (context: o, FALSE, NULL);
2254 g_printerr (format: "%s", s);
2255 g_free (mem: s);
2256 goto out;
2257 }
2258 }
2259
2260 error = NULL;
2261 c = connection_get_dbus_connection (TRUE, error: &error);
2262 if (c == NULL)
2263 {
2264 if (request_completion)
2265 {
2266 if (g_strcmp0 (str1: completion_prev, str2: "--address") == 0)
2267 {
2268 g_print (format: "unix:\n"
2269 "tcp:\n"
2270 "nonce-tcp:\n");
2271 }
2272 else
2273 {
2274 g_print (format: "--system \n--session \n--address \n");
2275 }
2276 }
2277 else
2278 {
2279 g_printerr (_("Error connecting: %s\n"), error->message);
2280 }
2281 g_error_free (error);
2282 goto out;
2283 }
2284
2285 /* All done with completion now */
2286 if (request_completion)
2287 goto out;
2288
2289 /*
2290 * Try and disentangle the command line arguments, with the aim of supporting:
2291 * gdbus wait --session --activate ActivateName WaitName
2292 * gdbus wait --session --activate ActivateAndWaitName
2293 * gdbus wait --activate --session ActivateAndWaitName
2294 * gdbus wait --session WaitName
2295 */
2296 if (*argc == 2 && opt_wait_activate_set && opt_wait_activate_name != NULL)
2297 {
2298 activate_service = opt_wait_activate_name;
2299 wait_service = (*argv)[1];
2300 }
2301 else if (*argc == 2 &&
2302 opt_wait_activate_set && opt_wait_activate_name == NULL)
2303 {
2304 activate_service = (*argv)[1];
2305 wait_service = (*argv)[1];
2306 }
2307 else if (*argc == 2 && !opt_wait_activate_set)
2308 {
2309 activate_service = NULL; /* disabled */
2310 wait_service = (*argv)[1];
2311 }
2312 else if (*argc == 1 &&
2313 opt_wait_activate_set && opt_wait_activate_name != NULL)
2314 {
2315 activate_service = opt_wait_activate_name;
2316 wait_service = opt_wait_activate_name;
2317 }
2318 else if (*argc == 1 &&
2319 opt_wait_activate_set && opt_wait_activate_name == NULL)
2320 {
2321 g_printerr (_("Error: A service to activate for must be specified.\n"));
2322 goto out;
2323 }
2324 else if (*argc == 1 && !opt_wait_activate_set)
2325 {
2326 g_printerr (_("Error: A service to wait for must be specified.\n"));
2327 goto out;
2328 }
2329 else /* if (*argc > 2) */
2330 {
2331 g_printerr (_("Error: Too many arguments.\n"));
2332 goto out;
2333 }
2334
2335 if (activate_service != NULL &&
2336 (!g_dbus_is_name (string: activate_service) ||
2337 g_dbus_is_unique_name (string: activate_service)))
2338 {
2339 g_printerr (_("Error: %s is not a valid well-known bus name.\n"),
2340 activate_service);
2341 goto out;
2342 }
2343
2344 if (!g_dbus_is_name (string: wait_service) || g_dbus_is_unique_name (string: wait_service))
2345 {
2346 g_printerr (_("Error: %s is not a valid well-known bus name.\n"),
2347 wait_service);
2348 goto out;
2349 }
2350
2351 /* Start the prerequisite service if needed. */
2352 if (activate_service != NULL)
2353 {
2354 activate_watch_id = g_bus_watch_name_on_connection (connection: c, name: activate_service,
2355 flags: G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
2356 NULL, NULL,
2357 NULL, NULL);
2358 }
2359 else
2360 {
2361 activate_watch_id = 0;
2362 }
2363
2364 /* Wait for the expected name to appear. */
2365 watch_id = g_bus_watch_name_on_connection (connection: c,
2366 name: wait_service,
2367 flags: G_BUS_NAME_WATCHER_FLAGS_NONE,
2368 name_appeared_handler: wait_name_appeared_cb,
2369 NULL, user_data: &wait_state, NULL);
2370
2371 /* Safety timeout. */
2372 if (opt_wait_timeout_secs > 0)
2373 timer_id = g_timeout_add_seconds (interval: opt_wait_timeout_secs, function: wait_timeout_cb, data: &wait_state);
2374
2375 while (wait_state == WAIT_STATE_RUNNING)
2376 g_main_context_iteration (NULL, TRUE);
2377
2378 g_bus_unwatch_name (watcher_id: watch_id);
2379 if (timer_id != 0)
2380 g_source_remove (tag: timer_id);
2381 if (activate_watch_id != 0)
2382 g_bus_unwatch_name (watcher_id: activate_watch_id);
2383
2384 ret = (wait_state == WAIT_STATE_SUCCESS);
2385
2386 out:
2387 g_clear_object (&c);
2388 g_option_context_free (context: o);
2389 g_free (mem: opt_wait_activate_name);
2390 opt_wait_activate_name = NULL;
2391
2392 return ret;
2393}
2394
2395/* ---------------------------------------------------------------------------------------------------- */
2396
2397static gchar *
2398pick_word_at (const gchar *s,
2399 gint cursor,
2400 gint *out_word_begins_at)
2401{
2402 gint begin;
2403 gint end;
2404
2405 if (s[0] == '\0')
2406 {
2407 if (out_word_begins_at != NULL)
2408 *out_word_begins_at = -1;
2409 return NULL;
2410 }
2411
2412 if (g_ascii_isspace (s[cursor]) && ((cursor > 0 && g_ascii_isspace(s[cursor-1])) || cursor == 0))
2413 {
2414 if (out_word_begins_at != NULL)
2415 *out_word_begins_at = cursor;
2416 return g_strdup (str: "");
2417 }
2418
2419 while (!g_ascii_isspace (s[cursor - 1]) && cursor > 0)
2420 cursor--;
2421 begin = cursor;
2422
2423 end = begin;
2424 while (!g_ascii_isspace (s[end]) && s[end] != '\0')
2425 end++;
2426
2427 if (out_word_begins_at != NULL)
2428 *out_word_begins_at = begin;
2429
2430 return g_strndup (str: s + begin, n: end - begin);
2431}
2432
2433gint
2434main (gint argc, gchar *argv[])
2435{
2436 gint ret;
2437 const gchar *command;
2438 gboolean request_completion;
2439 gchar *completion_cur;
2440 gchar *completion_prev;
2441#ifdef G_OS_WIN32
2442 gchar *tmp;
2443#endif
2444
2445 setlocale (LC_ALL, locale: "");
2446 textdomain (GETTEXT_PACKAGE);
2447
2448#ifdef G_OS_WIN32
2449 tmp = _glib_get_locale_dir ();
2450 bindtextdomain (GETTEXT_PACKAGE, tmp);
2451 g_free (tmp);
2452#else
2453 bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
2454#endif
2455
2456#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
2457 bind_textdomain_codeset (GETTEXT_PACKAGE, codeset: "UTF-8");
2458#endif
2459
2460 ret = 1;
2461 completion_cur = NULL;
2462 completion_prev = NULL;
2463
2464 if (argc < 2)
2465 {
2466 usage (argc: &argc, argv: &argv, FALSE);
2467 goto out;
2468 }
2469
2470 request_completion = FALSE;
2471
2472 //completion_debug ("---- argc=%d --------------------------------------------------------", argc);
2473
2474 again:
2475 command = argv[1];
2476 if (g_strcmp0 (str1: command, str2: "help") == 0)
2477 {
2478 if (request_completion)
2479 {
2480 /* do nothing */
2481 }
2482 else
2483 {
2484 usage (argc: &argc, argv: &argv, TRUE);
2485 ret = 0;
2486 }
2487 goto out;
2488 }
2489 else if (g_strcmp0 (str1: command, str2: "emit") == 0)
2490 {
2491 if (handle_emit (argc: &argc,
2492 argv: &argv,
2493 request_completion,
2494 completion_cur,
2495 completion_prev))
2496 ret = 0;
2497 goto out;
2498 }
2499 else if (g_strcmp0 (str1: command, str2: "call") == 0)
2500 {
2501 if (handle_call (argc: &argc,
2502 argv: &argv,
2503 request_completion,
2504 completion_cur,
2505 completion_prev))
2506 ret = 0;
2507 goto out;
2508 }
2509 else if (g_strcmp0 (str1: command, str2: "introspect") == 0)
2510 {
2511 if (handle_introspect (argc: &argc,
2512 argv: &argv,
2513 request_completion,
2514 completion_cur,
2515 completion_prev))
2516 ret = 0;
2517 goto out;
2518 }
2519 else if (g_strcmp0 (str1: command, str2: "monitor") == 0)
2520 {
2521 if (handle_monitor (argc: &argc,
2522 argv: &argv,
2523 request_completion,
2524 completion_cur,
2525 completion_prev))
2526 ret = 0;
2527 goto out;
2528 }
2529 else if (g_strcmp0 (str1: command, str2: "wait") == 0)
2530 {
2531 if (handle_wait (argc: &argc,
2532 argv: &argv,
2533 request_completion,
2534 completion_cur,
2535 completion_prev))
2536 ret = 0;
2537 goto out;
2538 }
2539#ifdef G_OS_WIN32
2540 else if (g_strcmp0 (command, _GDBUS_ARG_WIN32_RUN_SESSION_BUS) == 0)
2541 {
2542 g_win32_run_session_bus (NULL, NULL, NULL, 0);
2543 ret = 0;
2544 goto out;
2545 }
2546#endif
2547 else if (g_strcmp0 (str1: command, str2: "complete") == 0 && argc == 4 && !request_completion)
2548 {
2549 const gchar *completion_line;
2550 gchar **completion_argv;
2551 gint completion_argc;
2552 gint completion_point;
2553 gchar *endp;
2554 gint cur_begin;
2555
2556 request_completion = TRUE;
2557
2558 completion_line = argv[2];
2559 completion_point = strtol (nptr: argv[3], endptr: &endp, base: 10);
2560 if (endp == argv[3] || *endp != '\0')
2561 goto out;
2562
2563#if 0
2564 completion_debug ("completion_point=%d", completion_point);
2565 completion_debug ("----");
2566 completion_debug (" 0123456789012345678901234567890123456789012345678901234567890123456789");
2567 completion_debug ("'%s'", completion_line);
2568 completion_debug (" %*s^",
2569 completion_point, "");
2570 completion_debug ("----");
2571#endif
2572
2573 if (!g_shell_parse_argv (command_line: completion_line,
2574 argcp: &completion_argc,
2575 argvp: &completion_argv,
2576 NULL))
2577 {
2578 /* it's very possible the command line can't be parsed (for
2579 * example, missing quotes etc) - in that case, we just
2580 * don't autocomplete at all
2581 */
2582 goto out;
2583 }
2584
2585 /* compute cur and prev */
2586 completion_prev = NULL;
2587 completion_cur = pick_word_at (s: completion_line, cursor: completion_point, out_word_begins_at: &cur_begin);
2588 if (cur_begin > 0)
2589 {
2590 gint prev_end;
2591 for (prev_end = cur_begin - 1; prev_end >= 0; prev_end--)
2592 {
2593 if (!g_ascii_isspace (completion_line[prev_end]))
2594 {
2595 completion_prev = pick_word_at (s: completion_line, cursor: prev_end, NULL);
2596 break;
2597 }
2598 }
2599 }
2600#if 0
2601 completion_debug (" cur='%s'", completion_cur);
2602 completion_debug ("prev='%s'", completion_prev);
2603#endif
2604
2605 argc = completion_argc;
2606 argv = completion_argv;
2607
2608 ret = 0;
2609
2610 goto again;
2611 }
2612 else
2613 {
2614 if (request_completion)
2615 {
2616 g_print (format: "help \nemit \ncall \nintrospect \nmonitor \nwait \n");
2617 ret = 0;
2618 goto out;
2619 }
2620 else
2621 {
2622 g_printerr (format: "Unknown command '%s'\n", command);
2623 usage (argc: &argc, argv: &argv, FALSE);
2624 goto out;
2625 }
2626 }
2627
2628 out:
2629 g_free (mem: completion_cur);
2630 g_free (mem: completion_prev);
2631 return ret;
2632}
2633

source code of gtk/subprojects/glib/gio/gdbus-tool.c