• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
54 G_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
60 G_GNUC_UNUSED static void
61 completion_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
77 static void
completion_debug(const gchar * format,...)78 completion_debug (const gchar *format, ...)
79 {
80 }
81 #endif
82 
83 /* ---------------------------------------------------------------------------------------------------- */
84 
85 
86 static void
remove_arg(gint num,gint * argc,gchar ** argv[])87 remove_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 
99 static void
usage(gint * argc,gchar ** argv[],gboolean use_stdout)100 usage (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 (o, FALSE);
108   /* Ignore parsing result */
109   g_option_context_parse (o, argc, argv, NULL);
110   program_name = g_path_get_basename ((*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 (program_name);
122   g_option_context_set_description (o, s);
123   g_free (s);
124   s = g_option_context_get_help (o, FALSE, NULL);
125   if (use_stdout)
126     g_print ("%s", s);
127   else
128     g_printerr ("%s", s);
129   g_free (s);
130   g_option_context_free (o);
131 }
132 
133 static void
modify_argv0_for_command(gint * argc,gchar ** argv[],const gchar * command)134 modify_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 (1, argc, argv);
146 
147   program_name = g_path_get_basename ((*argv)[0]);
148   s = g_strdup_printf ("%s %s", (*argv)[0], command);
149   (*argv)[0] = s;
150   g_free (program_name);
151 }
152 
153 static GOptionContext *
command_option_context_new(const gchar * parameter_string,const gchar * summary,const GOptionEntry * entries,gboolean request_completion)154 command_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 (o, TRUE);
164   g_option_context_set_help_enabled (o, FALSE);
165   g_option_context_set_summary (o, summary);
166   g_option_context_add_main_entries (o, entries, GETTEXT_PACKAGE);
167 
168   return g_steal_pointer (&o);
169 }
170 
171 /* ---------------------------------------------------------------------------------------------------- */
172 
173 static void
print_methods_and_signals(GDBusConnection * c,const gchar * name,const gchar * path,gboolean print_methods,gboolean print_signals)174 print_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 (c,
189                                         name,
190                                         path,
191                                         "org.freedesktop.DBus.Introspectable",
192                                         "Introspect",
193                                         NULL,
194                                         G_VARIANT_TYPE ("(s)"),
195                                         G_DBUS_CALL_FLAGS_NONE,
196                                         3000, /* 3 secs */
197                                         NULL,
198                                         &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 (result, "(&s)", &xml_data);
206 
207   error = NULL;
208   node = g_dbus_node_info_new_for_xml (xml_data, &error);
209   g_variant_unref (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 ("%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 ("%s.%s \n", iface->name, signal->name);
229         }
230     }
231   g_dbus_node_info_unref (node);
232 
233  out:
234   ;
235 }
236 
237 static void
print_paths(GDBusConnection * c,const gchar * name,const gchar * path)238 print_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 (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 (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 (c,
261                                         name,
262                                         path,
263                                         "org.freedesktop.DBus.Introspectable",
264                                         "Introspect",
265                                         NULL,
266                                         G_VARIANT_TYPE ("(s)"),
267                                         G_DBUS_CALL_FLAGS_NONE,
268                                         3000, /* 3 secs */
269                                         NULL,
270                                         &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 (result, "(&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);
283   g_variant_unref (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 ("%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 (path, "/") == 0)
303         s = g_strdup_printf ("/%s", node->nodes[n]->path);
304       else
305         s = g_strdup_printf ("%s/%s", path, node->nodes[n]->path);
306 
307       print_paths (c, name, s);
308 
309       g_free (s);
310     }
311   g_dbus_node_info_unref (node);
312 
313  out:
314   ;
315 }
316 
317 static void
print_names(GDBusConnection * c,gboolean include_unique_names)318 print_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 (g_str_hash, g_str_equal, g_free, NULL);
330 
331   error = NULL;
332   result = g_dbus_connection_call_sync (c,
333                                         "org.freedesktop.DBus",
334                                         "/org/freedesktop/DBus",
335                                         "org.freedesktop.DBus",
336                                         "ListNames",
337                                         NULL,
338                                         G_VARIANT_TYPE ("(as)"),
339                                         G_DBUS_CALL_FLAGS_NONE,
340                                         3000, /* 3 secs */
341                                         NULL,
342                                         &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 (result, "(as)", &iter);
350   while (g_variant_iter_loop (iter, "s", &str))
351     g_hash_table_add (name_set, g_strdup (str));
352   g_variant_iter_free (iter);
353   g_variant_unref (result);
354 
355   error = NULL;
356   result = g_dbus_connection_call_sync (c,
357                                         "org.freedesktop.DBus",
358                                         "/org/freedesktop/DBus",
359                                         "org.freedesktop.DBus",
360                                         "ListActivatableNames",
361                                         NULL,
362                                         G_VARIANT_TYPE ("(as)"),
363                                         G_DBUS_CALL_FLAGS_NONE,
364                                         3000, /* 3 secs */
365                                         NULL,
366                                         &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 (result, "(as)", &iter);
374   while (g_variant_iter_loop (iter, "s", &str))
375     g_hash_table_add (name_set, g_strdup (str));
376   g_variant_iter_free (iter);
377   g_variant_unref (result);
378 
379   keys = g_hash_table_get_keys (name_set);
380   keys = g_list_sort (keys, (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 (name, ":"))
385         continue;
386 
387       g_print ("%s \n", name);
388     }
389   g_list_free (keys);
390 
391  out:
392   g_hash_table_unref (name_set);
393 }
394 
395 /* ---------------------------------------------------------------------------------------------------- */
396 
397 static gboolean  opt_connection_system  = FALSE;
398 static gboolean  opt_connection_session = FALSE;
399 static gchar    *opt_connection_address = NULL;
400 
401 static 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 
409 static GOptionGroup *
connection_get_group(void)410 connection_get_group (void)
411 {
412   static GOptionGroup *g;
413 
414   g = g_option_group_new ("connection",
415                           N_("Connection Endpoint Options:"),
416                           N_("Options specifying the connection endpoint"),
417                           NULL,
418                           NULL);
419   g_option_group_set_translation_domain (g, GETTEXT_PACKAGE);
420   g_option_group_add_entries (g, connection_entries);
421 
422   return g;
423 }
424 
425 static GDBusConnection *
connection_get_dbus_connection(gboolean require_message_bus,GError ** error)426 connection_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 (error,
437                    G_IO_ERROR,
438                    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 (error,
447                    G_IO_ERROR,
448                    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 (G_BUS_TYPE_SYSTEM, NULL, error);
456     }
457   else if (opt_connection_session)
458     {
459       c = g_bus_get_sync (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 (opt_connection_address,
467                                                   flags,
468                                                   NULL, /* GDBusAuthObserver */
469                                                   NULL, /* GCancellable */
470                                                   error);
471     }
472 
473  out:
474   return c;
475 }
476 
477 /* ---------------------------------------------------------------------------------------------------- */
478 
479 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)480 call_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 (c,
500                                         dest,
501                                         path,
502                                         "org.freedesktop.DBus.Introspectable",
503                                         "Introspect",
504                                         NULL,
505                                         G_VARIANT_TYPE ("(s)"),
506                                         G_DBUS_CALL_FLAGS_NONE,
507                                         3000, /* 3 secs */
508                                         NULL,
509                                         error);
510   if (result == NULL)
511     goto out;
512 
513   g_variant_get (result, "(&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 (node_info, interface_name);
519   if (interface_info == NULL)
520     {
521       g_set_error (error, G_IO_ERROR, 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 (interface_info, method_name);
528   if (method_info == NULL)
529     {
530       g_set_error (error, G_IO_ERROR, 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 ((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 (ret, g_variant_type_new (method_info->in_args[n]->signature));
541     }
542 
543  out:
544   if (node_info != NULL)
545     g_dbus_node_info_unref (node_info);
546   if (result != NULL)
547     g_variant_unref (result);
548 
549   return ret;
550 }
551 
552 /* ---------------------------------------------------------------------------------------------------- */
553 
554 static GVariant *
_g_variant_parse_me_harder(GVariantType * type,const gchar * given_str,GError ** error)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 ("\"");
565   for (n = 0; given_str[n] != '\0'; n++)
566     {
567       if (G_UNLIKELY (given_str[n] == '\"'))
568         g_string_append (str, "\\\"");
569       else
570         g_string_append_c (str, given_str[n]);
571     }
572   g_string_append_c (str, '"');
573   s = g_string_free (str, FALSE);
574 
575   value = g_variant_parse (type,
576                            s,
577                            NULL,
578                            NULL,
579                            error);
580   g_free (s);
581 
582   return value;
583 }
584 
585 /* ---------------------------------------------------------------------------------------------------- */
586 
587 static gchar *opt_emit_dest = NULL;
588 static gchar *opt_emit_object_path = NULL;
589 static gchar *opt_emit_signal = NULL;
590 
591 static 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 
599 static gboolean
handle_emit(gint * argc,gchar ** argv[],gboolean request_completion,const gchar * completion_cur,const gchar * completion_prev)600 handle_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, "emit");
627 
628   o = command_option_context_new (NULL, _("Emit a signal."),
629                                   emit_entries, request_completion);
630   g_option_context_add_group (o, connection_get_group ());
631 
632   complete_names = FALSE;
633   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0)
634     {
635       complete_names = TRUE;
636       remove_arg ((*argc) - 1, argc, argv);
637     }
638 
639   complete_paths = FALSE;
640   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0)
641     {
642       complete_paths = TRUE;
643       remove_arg ((*argc) - 1, argc, argv);
644     }
645 
646   complete_signals = FALSE;
647   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--signal") == 0)
648     {
649       complete_signals = TRUE;
650       remove_arg ((*argc) - 1, argc, argv);
651     }
652 
653   if (!g_option_context_parse (o, argc, argv, NULL))
654     {
655       if (!request_completion)
656         {
657           s = g_option_context_get_help (o, FALSE, NULL);
658           g_printerr ("%s", s);
659           g_free (s);
660           goto out;
661         }
662     }
663 
664   error = NULL;
665   c = connection_get_dbus_connection ((opt_emit_dest != NULL), &error);
666   if (c == NULL)
667     {
668       if (request_completion)
669         {
670           if (g_strcmp0 (completion_prev, "--address") == 0)
671             {
672               g_print ("unix:\n"
673                        "tcp:\n"
674                        "nonce-tcp:\n");
675             }
676           else
677             {
678               g_print ("--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 ("--dest", completion_prev) == 0)
696     {
697       print_names (c, g_str_has_prefix (opt_emit_dest, ":"));
698       goto out;
699     }
700 
701   if (!request_completion && opt_emit_dest != NULL && !g_dbus_is_unique_name (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 ("--dest \n");
710     }
711   /* validate and complete object path */
712   if (opt_emit_dest != NULL && complete_paths)
713     {
714       print_paths (c, opt_emit_dest, "/");
715       goto out;
716     }
717   if (opt_emit_object_path == NULL)
718     {
719       if (request_completion)
720         g_print ("--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 ("--object-path", completion_prev) == 0)
726     {
727       if (opt_emit_dest != NULL)
728         {
729           gchar *p;
730           s = g_strdup (opt_emit_object_path);
731           p = strrchr (s, '/');
732           if (p != NULL)
733             {
734               if (p == s)
735                 p++;
736               *p = '\0';
737             }
738           print_paths (c, opt_emit_dest, s);
739           g_free (s);
740         }
741       goto out;
742     }
743   if (!request_completion && !g_variant_is_object_path (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, opt_emit_dest, 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 ("--signal", completion_prev) != 0)
761             g_print ("--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 ("--signal", completion_prev) == 0)
772     {
773       print_methods_and_signals (c, opt_emit_dest, opt_emit_object_path, FALSE, TRUE);
774       goto out;
775     }
776   s = strrchr (opt_emit_signal, '.');
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 (s + 1);
783   interface_name = g_strndup (opt_emit_signal, 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 (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 (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, 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 ((*argv)[n], "--") == 0)
812         {
813           skip_dashes = FALSE;
814           continue;
815         }
816 
817       error = NULL;
818       value = g_variant_parse (NULL,
819                                (*argv)[n],
820                                NULL,
821                                NULL,
822                                &error);
823       if (value == NULL)
824         {
825           gchar *context;
826 
827           context = g_variant_parse_error_print_context (error, (*argv)[n]);
828           g_error_free (error);
829           error = NULL;
830           value = _g_variant_parse_me_harder (NULL, (*argv)[n], &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 (context);
839               g_variant_builder_clear (&builder);
840               goto out;
841             }
842           g_free (context);
843         }
844       g_variant_builder_add_value (&builder, value);
845       ++parm;
846     }
847   parameters = g_variant_builder_end (&builder);
848 
849   if (parameters != NULL)
850     parameters = g_variant_ref_sink (parameters);
851   if (!g_dbus_connection_emit_signal (c,
852                                       opt_emit_dest,
853                                       opt_emit_object_path,
854                                       interface_name,
855                                       signal_name,
856                                       parameters,
857                                       &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 (c, NULL, &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 (c);
876   if (parameters != NULL)
877     g_variant_unref (parameters);
878   g_free (interface_name);
879   g_free (signal_name);
880   g_option_context_free (o);
881   return ret;
882 }
883 
884 /* ---------------------------------------------------------------------------------------------------- */
885 
886 static gchar *opt_call_dest = NULL;
887 static gchar *opt_call_object_path = NULL;
888 static gchar *opt_call_method = NULL;
889 static gint opt_call_timeout = -1;
890 
891 static 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 
900 static gboolean
handle_call(gint * argc,gchar ** argv[],gboolean request_completion,const gchar * completion_cur,const gchar * completion_prev)901 handle_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, "call");
941 
942   o = command_option_context_new (NULL, _("Invoke a method on a remote object."),
943                                   call_entries, request_completion);
944   g_option_context_add_group (o, connection_get_group ());
945 
946   complete_names = FALSE;
947   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0)
948     {
949       complete_names = TRUE;
950       remove_arg ((*argc) - 1, argc, argv);
951     }
952 
953   complete_paths = FALSE;
954   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0)
955     {
956       complete_paths = TRUE;
957       remove_arg ((*argc) - 1, argc, argv);
958     }
959 
960   complete_methods = FALSE;
961   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--method") == 0)
962     {
963       complete_methods = TRUE;
964       remove_arg ((*argc) - 1, argc, argv);
965     }
966 
967   if (!g_option_context_parse (o, argc, argv, NULL))
968     {
969       if (!request_completion)
970         {
971           s = g_option_context_get_help (o, FALSE, NULL);
972           g_printerr ("%s", s);
973           g_free (s);
974           goto out;
975         }
976     }
977 
978   error = NULL;
979   c = connection_get_dbus_connection (TRUE, &error);
980   if (c == NULL)
981     {
982       if (request_completion)
983         {
984           if (g_strcmp0 (completion_prev, "--address") == 0)
985             {
986               g_print ("unix:\n"
987                        "tcp:\n"
988                        "nonce-tcp:\n");
989             }
990           else
991             {
992               g_print ("--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 ("--dest \n");
1013       else
1014         g_printerr (_("Error: Destination is not specified\n"));
1015       goto out;
1016     }
1017   if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0)
1018     {
1019       print_names (c, g_str_has_prefix (opt_call_dest, ":"));
1020       goto out;
1021     }
1022 
1023   if (!request_completion && !g_dbus_is_name (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, opt_call_dest, "/");
1033       goto out;
1034     }
1035   if (opt_call_object_path == NULL)
1036     {
1037       if (request_completion)
1038         g_print ("--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 ("--object-path", completion_prev) == 0)
1044     {
1045       gchar *p;
1046       s = g_strdup (opt_call_object_path);
1047       p = strrchr (s, '/');
1048       if (p != NULL)
1049         {
1050           if (p == s)
1051             p++;
1052           *p = '\0';
1053         }
1054       print_paths (c, opt_call_dest, s);
1055       g_free (s);
1056       goto out;
1057     }
1058   if (!request_completion && !g_variant_is_object_path (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, opt_call_dest, opt_call_object_path, TRUE, FALSE);
1068       goto out;
1069     }
1070   if (opt_call_method == NULL)
1071     {
1072       if (request_completion)
1073         g_print ("--method \n");
1074       else
1075         g_printerr (_("Error: Method name is not specified\n"));
1076       goto out;
1077     }
1078   if (request_completion && g_strcmp0 ("--method", completion_prev) == 0)
1079     {
1080       print_methods_and_signals (c, opt_call_dest, opt_call_object_path, TRUE, FALSE);
1081       goto out;
1082     }
1083   s = strrchr (opt_call_method, '.');
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 (s + 1);
1090   interface_name = g_strndup (opt_call_method, 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                                                             opt_call_dest,
1099                                                             opt_call_object_path,
1100                                                             interface_name,
1101                                                             method_name,
1102                                                             &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, 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 ((*argv)[n], "--") == 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 ("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                                (*argv)[n],
1148                                NULL,
1149                                NULL,
1150                                &error);
1151       if (value == NULL)
1152         {
1153           gchar *context;
1154 
1155           context = g_variant_parse_error_print_context (error, (*argv)[n]);
1156           g_error_free (error);
1157           error = NULL;
1158           value = _g_variant_parse_me_harder (type, (*argv)[n], &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 (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);
1178               g_free (context);
1179               goto out;
1180             }
1181           g_free (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 (fd_list, g_variant_get_handle (value), &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);
1193               g_error_free (error);
1194               goto out;
1195             }
1196 	  g_variant_unref (value);
1197           value = g_variant_new_handle (fd_id);
1198       	}
1199 #endif
1200       g_variant_builder_add_value (&builder, value);
1201       ++parm;
1202     }
1203   parameters = g_variant_builder_end (&builder);
1204 
1205   if (parameters != NULL)
1206     parameters = g_variant_ref_sink (parameters);
1207 #ifdef G_OS_UNIX
1208   result = g_dbus_connection_call_with_unix_fd_list_sync (c,
1209                                                           opt_call_dest,
1210                                                           opt_call_object_path,
1211                                                           interface_name,
1212                                                           method_name,
1213                                                           parameters,
1214                                                           NULL,
1215                                                           G_DBUS_CALL_FLAGS_NONE,
1216                                                           opt_call_timeout > 0 ? opt_call_timeout * 1000 : opt_call_timeout,
1217                                                           fd_list,
1218                                                           NULL,
1219                                                           NULL,
1220                                                           &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, 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 (s,
1249                                        g_variant_type_peek_string (type),
1250                                        g_variant_type_get_string_length (type));
1251                 }
1252 
1253               g_printerr ("(According to introspection data, you need to pass '%s')\n", s->str);
1254               g_string_free (s, TRUE);
1255             }
1256           else
1257             g_printerr ("(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 (result, TRUE);
1265   g_print ("%s\n", s);
1266   g_free (s);
1267 
1268   ret = TRUE;
1269 
1270  out:
1271   if (in_signature_types != NULL)
1272     g_ptr_array_unref (in_signature_types);
1273   if (result != NULL)
1274     g_variant_unref (result);
1275   if (c != NULL)
1276     g_object_unref (c);
1277   if (parameters != NULL)
1278     g_variant_unref (parameters);
1279   g_free (interface_name);
1280   g_free (method_name);
1281   g_option_context_free (o);
1282 #ifdef G_OS_UNIX
1283   g_clear_object (&fd_list);
1284 #endif
1285   return ret;
1286 }
1287 
1288 /* ---------------------------------------------------------------------------------------------------- */
1289 
1290 static gchar *opt_introspect_dest = NULL;
1291 static gchar *opt_introspect_object_path = NULL;
1292 static gboolean opt_introspect_xml = FALSE;
1293 static gboolean opt_introspect_recurse = FALSE;
1294 static 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 
1308 static void
dump_annotation(const GDBusAnnotationInfo * o,guint indent,gboolean ignore_indent,gboolean use_colors)1309 dump_annotation (const GDBusAnnotationInfo *o,
1310                  guint indent,
1311                  gboolean ignore_indent,
1312                  gboolean use_colors)
1313 {
1314   guint n;
1315   g_print ("%*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->annotations[n], indent + 2, FALSE, use_colors);
1320 }
1321 
1322 static void
dump_arg(const GDBusArgInfo * o,guint indent,const gchar * direction,gboolean ignore_indent,gboolean include_newline,gboolean use_colors)1323 dump_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->annotations[n], indent, ignore_indent, use_colors);
1335       ignore_indent = FALSE;
1336     }
1337 
1338   g_print ("%*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 
1346 static guint
count_args(GDBusArgInfo ** args)1347 count_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 
1359 static void
dump_method(const GDBusMethodInfo * o,guint indent,gboolean use_colors)1360 dump_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->annotations[n], indent, FALSE, use_colors);
1371 
1372   g_print ("%*s%s%s%s(",
1373            indent, "",
1374            INTROSPECT_METHOD_COLOR, o->name, RESET_COLOR);
1375   name_len = strlen (o->name);
1376   total_num_args = count_args (o->in_args) + count_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->in_args[n],
1383                 indent + name_len + 1,
1384                 "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->out_args[n],
1394                 indent + name_len + 1,
1395                 "out ",
1396                 ignore_indent,
1397                 include_newline,
1398                 use_colors);
1399     }
1400   g_print (");\n");
1401 }
1402 
1403 static void
dump_signal(const GDBusSignalInfo * o,guint indent,gboolean use_colors)1404 dump_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->annotations[n], indent, FALSE, use_colors);
1414 
1415   g_print ("%*s%s%s%s(",
1416            indent, "",
1417            INTROSPECT_SIGNAL_COLOR, o->name, RESET_COLOR);
1418   name_len = strlen (o->name);
1419   total_num_args = count_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->args[n],
1425                 indent + name_len + 1,
1426                 "",
1427                 ignore_indent,
1428                 include_newline,
1429                 use_colors);
1430     }
1431   g_print (");\n");
1432 }
1433 
1434 static void
dump_property(const GDBusPropertyInfo * o,guint indent,gboolean use_colors,GVariant * value)1435 dump_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->annotations[n], indent, FALSE, use_colors);
1454 
1455   if (value != NULL)
1456     {
1457       gchar *s = g_variant_print (value, FALSE);
1458       g_print ("%*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 (s);
1463     }
1464   else
1465     {
1466       g_print ("%*s%s %s %s;\n", indent, "", access, o->signature, o->name);
1467     }
1468 }
1469 
1470 static void
dump_interface(GDBusConnection * c,const gchar * name,const GDBusInterfaceInfo * o,guint indent,gboolean use_colors,const gchar * object_path)1471 dump_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 (g_str_hash,
1482                                       g_str_equal,
1483                                       g_free,
1484                                       (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 (c,
1491                                             name,
1492                                             object_path,
1493                                             "org.freedesktop.DBus.Properties",
1494                                             "GetAll",
1495                                             g_variant_new ("(s)", o->name),
1496                                             NULL,
1497                                             G_DBUS_CALL_FLAGS_NONE,
1498                                             3000,
1499                                             NULL,
1500                                             NULL);
1501       if (result != NULL)
1502         {
1503           if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(a{sv})")))
1504             {
1505               GVariantIter *iter;
1506               GVariant *item;
1507               g_variant_get (result,
1508                              "(a{sv})",
1509                              &iter);
1510               while ((item = g_variant_iter_next_value (iter)))
1511                 {
1512                   gchar *key;
1513                   GVariant *value;
1514                   g_variant_get (item,
1515                                  "{sv}",
1516                                  &key,
1517                                  &value);
1518 
1519                   g_hash_table_insert (properties, key, g_variant_ref (value));
1520                 }
1521             }
1522           g_variant_unref (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 (c,
1530                                                     name,
1531                                                     object_path,
1532                                                     "org.freedesktop.DBus.Properties",
1533                                                     "Get",
1534                                                     g_variant_new ("(ss)", o->name, o->properties[n]->name),
1535                                                     G_VARIANT_TYPE ("(v)"),
1536                                                     G_DBUS_CALL_FLAGS_NONE,
1537                                                     3000,
1538                                                     NULL,
1539                                                     NULL);
1540               if (result != NULL)
1541                 {
1542                   GVariant *property_value;
1543                   g_variant_get (result,
1544                                  "(v)",
1545                                  &property_value);
1546                   g_hash_table_insert (properties,
1547                                        g_strdup (o->properties[n]->name),
1548                                        g_variant_ref (property_value));
1549                   g_variant_unref (result);
1550                 }
1551             }
1552         }
1553     }
1554 
1555   for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
1556     dump_annotation (o->annotations[n], indent, FALSE, use_colors);
1557 
1558   g_print ("%*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 ("%*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->methods[n], indent + 4, use_colors);
1568     }
1569   if (o->signals != NULL && !opt_introspect_only_properties)
1570     {
1571       g_print ("%*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->signals[n], indent + 4, use_colors);
1576     }
1577   if (o->properties != NULL)
1578     {
1579       g_print ("%*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->properties[n],
1585                          indent + 4,
1586                          use_colors,
1587                          g_hash_table_lookup (properties, (o->properties[n])->name));
1588         }
1589     }
1590   g_print ("%*s};\n",
1591            indent, "");
1592 
1593   g_hash_table_unref (properties);
1594 }
1595 
1596 static gboolean
1597 introspect_do (GDBusConnection *c,
1598                const gchar     *object_path,
1599                guint            indent,
1600                gboolean         use_colors);
1601 
1602 static void
dump_node(GDBusConnection * c,const gchar * name,const GDBusNodeInfo * o,guint indent,gboolean use_colors,const gchar * object_path,gboolean recurse)1603 dump_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->annotations[n], indent, FALSE, use_colors);
1620 
1621   g_print ("%*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 (" {\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->interfaces[n], indent + 2, use_colors, object_path);
1635             }
1636           else
1637             {
1638               dump_interface (c, name, o->interfaces[n], 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 (o->nodes[n]->path))
1647                 {
1648                   child_path = g_strdup (o->nodes[n]->path);
1649                   /* avoid infinite loops */
1650                   if (g_str_has_prefix (child_path, object_path))
1651                     {
1652                       introspect_do (c, child_path, indent + 2, use_colors);
1653                     }
1654                   else
1655                     {
1656                       g_print ("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 (object_path, "/") == 0)
1663                     child_path = g_strdup_printf ("/%s", o->nodes[n]->path);
1664                   else
1665                     child_path = g_strdup_printf ("%s/%s", object_path, o->nodes[n]->path);
1666                   introspect_do (c, child_path, indent + 2, use_colors);
1667                 }
1668               g_free (child_path);
1669             }
1670           else
1671             {
1672               dump_node (NULL, NULL, o->nodes[n], indent + 2, use_colors, NULL, recurse);
1673             }
1674         }
1675       g_print ("%*s};\n",
1676                indent, "");
1677     }
1678   else
1679     {
1680       g_print ("\n");
1681     }
1682 }
1683 
1684 static 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 
1694 static gboolean
introspect_do(GDBusConnection * c,const gchar * object_path,guint indent,gboolean use_colors)1695 introspect_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 (c,
1712                                         opt_introspect_dest,
1713                                         object_path,
1714                                         "org.freedesktop.DBus.Introspectable",
1715                                         "Introspect",
1716                                         NULL,
1717                                         G_VARIANT_TYPE ("(s)"),
1718                                         G_DBUS_CALL_FLAGS_NONE,
1719                                         3000, /* 3 sec */
1720                                         NULL,
1721                                         &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 (result, "(&s)", &xml_data);
1729 
1730   if (opt_introspect_xml)
1731     {
1732       g_print ("%s", xml_data);
1733     }
1734   else
1735     {
1736       error = NULL;
1737       node = g_dbus_node_info_new_for_xml (xml_data, &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, opt_introspect_dest, node, indent, use_colors, object_path, opt_introspect_recurse);
1746     }
1747 
1748   ret = TRUE;
1749 
1750  out:
1751   if (node != NULL)
1752     g_dbus_node_info_unref (node);
1753   if (result != NULL)
1754     g_variant_unref (result);
1755   return ret;
1756 }
1757 
1758 static gboolean
handle_introspect(gint * argc,gchar ** argv[],gboolean request_completion,const gchar * completion_cur,const gchar * completion_prev)1759 handle_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, "introspect");
1778 
1779   o = command_option_context_new (NULL, _("Introspect a remote object."),
1780                                   introspect_entries, request_completion);
1781   g_option_context_add_group (o, connection_get_group ());
1782 
1783   complete_names = FALSE;
1784   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0)
1785     {
1786       complete_names = TRUE;
1787       remove_arg ((*argc) - 1, argc, argv);
1788     }
1789 
1790   complete_paths = FALSE;
1791   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0)
1792     {
1793       complete_paths = TRUE;
1794       remove_arg ((*argc) - 1, argc, argv);
1795     }
1796 
1797   if (!g_option_context_parse (o, argc, argv, NULL))
1798     {
1799       if (!request_completion)
1800         {
1801           s = g_option_context_get_help (o, FALSE, NULL);
1802           g_printerr ("%s", s);
1803           g_free (s);
1804           goto out;
1805         }
1806     }
1807 
1808   error = NULL;
1809   c = connection_get_dbus_connection (TRUE, &error);
1810   if (c == NULL)
1811     {
1812       if (request_completion)
1813         {
1814           if (g_strcmp0 (completion_prev, "--address") == 0)
1815             {
1816               g_print ("unix:\n"
1817                        "tcp:\n"
1818                        "nonce-tcp:\n");
1819             }
1820           else
1821             {
1822               g_print ("--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 ("--dest \n");
1843       else
1844         g_printerr (_("Error: Destination is not specified\n"));
1845       goto out;
1846     }
1847   if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0)
1848     {
1849       print_names (c, g_str_has_prefix (opt_introspect_dest, ":"));
1850       goto out;
1851     }
1852 
1853   if (complete_paths)
1854     {
1855       print_paths (c, opt_introspect_dest, "/");
1856       goto out;
1857     }
1858 
1859   if (!request_completion && !g_dbus_is_name (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 ("--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 ("--object-path", completion_prev) == 0)
1874     {
1875       gchar *p;
1876       s = g_strdup (opt_introspect_object_path);
1877       p = strrchr (s, '/');
1878       if (p != NULL)
1879         {
1880           if (p == s)
1881             p++;
1882           *p = '\0';
1883         }
1884       print_paths (c, opt_introspect_dest, s);
1885       g_free (s);
1886       goto out;
1887     }
1888   if (!request_completion && !g_variant_is_object_path (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 ("--recurse \n");
1897     }
1898 
1899   if (request_completion && opt_introspect_object_path != NULL && !opt_introspect_only_properties)
1900     {
1901       g_print ("--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 (fileno (stdout));
1910 
1911   if (!introspect_do (c, opt_introspect_object_path, 0, color_support))
1912     goto out;
1913 
1914   ret = TRUE;
1915 
1916  out:
1917   if (c != NULL)
1918     g_object_unref (c);
1919   g_option_context_free (o);
1920   return ret;
1921 }
1922 
1923 /* ---------------------------------------------------------------------------------------------------- */
1924 
1925 static gchar *opt_monitor_dest = NULL;
1926 static gchar *opt_monitor_object_path = NULL;
1927 
1928 static guint monitor_filter_id = 0;
1929 
1930 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)1931 monitor_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 (parameters, TRUE);
1941   g_print ("%s: %s.%s %s\n",
1942            object_path,
1943            interface_name,
1944            signal_name,
1945            s);
1946   g_free (s);
1947 }
1948 
1949 static void
monitor_on_name_appeared(GDBusConnection * connection,const gchar * name,const gchar * name_owner,gpointer user_data)1950 monitor_on_name_appeared (GDBusConnection *connection,
1951                           const gchar *name,
1952                           const gchar *name_owner,
1953                           gpointer user_data)
1954 {
1955   g_print ("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                                                           name_owner,
1959                                                           NULL,  /* any interface */
1960                                                           NULL,  /* any member */
1961                                                           opt_monitor_object_path,
1962                                                           NULL,  /* arg0 */
1963                                                           G_DBUS_SIGNAL_FLAGS_NONE,
1964                                                           monitor_signal_cb,
1965                                                           NULL,  /* user_data */
1966                                                           NULL); /* user_data destroy notify */
1967 }
1968 
1969 static void
monitor_on_name_vanished(GDBusConnection * connection,const gchar * name,gpointer user_data)1970 monitor_on_name_vanished (GDBusConnection *connection,
1971                           const gchar *name,
1972                           gpointer user_data)
1973 {
1974   g_print ("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, monitor_filter_id);
1979       monitor_filter_id = 0;
1980     }
1981 }
1982 
1983 static 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 
1990 static gboolean
handle_monitor(gint * argc,gchar ** argv[],gboolean request_completion,const gchar * completion_cur,const gchar * completion_prev)1991 handle_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, "monitor");
2010 
2011   o = command_option_context_new (NULL, _("Monitor a remote object."),
2012                                   monitor_entries, request_completion);
2013   g_option_context_add_group (o, connection_get_group ());
2014 
2015   complete_names = FALSE;
2016   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0)
2017     {
2018       complete_names = TRUE;
2019       remove_arg ((*argc) - 1, argc, argv);
2020     }
2021 
2022   complete_paths = FALSE;
2023   if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0)
2024     {
2025       complete_paths = TRUE;
2026       remove_arg ((*argc) - 1, argc, argv);
2027     }
2028 
2029   if (!g_option_context_parse (o, argc, argv, NULL))
2030     {
2031       if (!request_completion)
2032         {
2033           s = g_option_context_get_help (o, FALSE, NULL);
2034           g_printerr ("%s", s);
2035           g_free (s);
2036           goto out;
2037         }
2038     }
2039 
2040   error = NULL;
2041   c = connection_get_dbus_connection (TRUE, &error);
2042   if (c == NULL)
2043     {
2044       if (request_completion)
2045         {
2046           if (g_strcmp0 (completion_prev, "--address") == 0)
2047             {
2048               g_print ("unix:\n"
2049                        "tcp:\n"
2050                        "nonce-tcp:\n");
2051             }
2052           else
2053             {
2054               g_print ("--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 (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 ("--dest \n");
2083       else
2084         g_printerr (_("Error: Destination is not specified\n"));
2085       goto out;
2086     }
2087   if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0)
2088     {
2089       print_names (c, g_str_has_prefix (opt_monitor_dest, ":"));
2090       goto out;
2091     }
2092 
2093   if (!request_completion && !g_dbus_is_name (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, opt_monitor_dest, "/");
2102       goto out;
2103     }
2104   if (opt_monitor_object_path == NULL)
2105     {
2106       if (request_completion)
2107         {
2108           g_print ("--object-path \n");
2109           goto out;
2110         }
2111       /* it's fine to not have an object path */
2112     }
2113   if (request_completion && g_strcmp0 ("--object-path", completion_prev) == 0)
2114     {
2115       gchar *p;
2116       s = g_strdup (opt_monitor_object_path);
2117       p = strrchr (s, '/');
2118       if (p != NULL)
2119         {
2120           if (p == s)
2121             p++;
2122           *p = '\0';
2123         }
2124       print_paths (c, opt_monitor_dest, s);
2125       g_free (s);
2126       goto out;
2127     }
2128   if (!request_completion && (opt_monitor_object_path != NULL && !g_variant_is_object_path (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 ("Monitoring signals on object %s owned by %s\n", opt_monitor_object_path, opt_monitor_dest);
2140   else
2141     g_print ("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 (c,
2145                                   opt_monitor_dest,
2146                                   G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
2147                                   monitor_on_name_appeared,
2148                                   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 (c);
2160   g_option_context_free (o);
2161   return ret;
2162 }
2163 
2164 /* ---------------------------------------------------------------------------------------------------- */
2165 
2166 static gboolean opt_wait_activate_set = FALSE;
2167 static gchar *opt_wait_activate_name = NULL;
2168 static gint64 opt_wait_timeout_secs = 0;  /* no timeout */
2169 
2170 typedef 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 
2176 static gboolean
opt_wait_activate_cb(const gchar * option_name,const gchar * value,gpointer data,GError ** error)2177 opt_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 (value);
2185 
2186   return TRUE;
2187 }
2188 
2189 static 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 
2201 static void
wait_name_appeared_cb(GDBusConnection * connection,const gchar * name,const gchar * name_owner,gpointer user_data)2202 wait_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 
2212 static gboolean
wait_timeout_cb(gpointer user_data)2213 wait_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 
2223 static gboolean
handle_wait(gint * argc,gchar ** argv[],gboolean request_completion,const gchar * completion_cur,const gchar * completion_prev)2224 handle_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, "wait");
2243 
2244   o = command_option_context_new (_("[OPTION…] BUS-NAME"),
2245                                   _("Wait for a bus name to appear."),
2246                                   wait_entries, request_completion);
2247   g_option_context_add_group (o, connection_get_group ());
2248 
2249   if (!g_option_context_parse (o, argc, argv, NULL))
2250     {
2251       if (!request_completion)
2252         {
2253           s = g_option_context_get_help (o, FALSE, NULL);
2254           g_printerr ("%s", s);
2255           g_free (s);
2256           goto out;
2257         }
2258     }
2259 
2260   error = NULL;
2261   c = connection_get_dbus_connection (TRUE, &error);
2262   if (c == NULL)
2263     {
2264       if (request_completion)
2265         {
2266           if (g_strcmp0 (completion_prev, "--address") == 0)
2267             {
2268               g_print ("unix:\n"
2269                        "tcp:\n"
2270                        "nonce-tcp:\n");
2271             }
2272           else
2273             {
2274               g_print ("--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 (activate_service) ||
2337        g_dbus_is_unique_name (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 (wait_service) || g_dbus_is_unique_name (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 (c, activate_service,
2355                                                           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 (c,
2366                                              wait_service,
2367                                              G_BUS_NAME_WATCHER_FLAGS_NONE,
2368                                              wait_name_appeared_cb,
2369                                              NULL, &wait_state, NULL);
2370 
2371   /* Safety timeout. */
2372   if (opt_wait_timeout_secs > 0)
2373     timer_id = g_timeout_add_seconds (opt_wait_timeout_secs, wait_timeout_cb, &wait_state);
2374 
2375   while (wait_state == WAIT_STATE_RUNNING)
2376     g_main_context_iteration (NULL, TRUE);
2377 
2378   g_bus_unwatch_name (watch_id);
2379   if (timer_id != 0)
2380       g_source_remove (timer_id);
2381   if (activate_watch_id != 0)
2382       g_bus_unwatch_name (activate_watch_id);
2383 
2384   ret = (wait_state == WAIT_STATE_SUCCESS);
2385 
2386  out:
2387   g_clear_object (&c);
2388   g_option_context_free (o);
2389   g_free (opt_wait_activate_name);
2390   opt_wait_activate_name = NULL;
2391 
2392   return ret;
2393 }
2394 
2395 /* ---------------------------------------------------------------------------------------------------- */
2396 
2397 static gchar *
pick_word_at(const gchar * s,gint cursor,gint * out_word_begins_at)2398 pick_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 ("");
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 (s + begin, end - begin);
2431 }
2432 
2433 gint
main(gint argc,gchar * argv[])2434 main (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, "");
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, "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, &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 (command, "help") == 0)
2477     {
2478       if (request_completion)
2479         {
2480           /* do nothing */
2481         }
2482       else
2483         {
2484           usage (&argc, &argv, TRUE);
2485           ret = 0;
2486         }
2487       goto out;
2488     }
2489   else if (g_strcmp0 (command, "emit") == 0)
2490     {
2491       if (handle_emit (&argc,
2492                        &argv,
2493                        request_completion,
2494                        completion_cur,
2495                        completion_prev))
2496         ret = 0;
2497       goto out;
2498     }
2499   else if (g_strcmp0 (command, "call") == 0)
2500     {
2501       if (handle_call (&argc,
2502                        &argv,
2503                        request_completion,
2504                        completion_cur,
2505                        completion_prev))
2506         ret = 0;
2507       goto out;
2508     }
2509   else if (g_strcmp0 (command, "introspect") == 0)
2510     {
2511       if (handle_introspect (&argc,
2512                              &argv,
2513                              request_completion,
2514                              completion_cur,
2515                              completion_prev))
2516         ret = 0;
2517       goto out;
2518     }
2519   else if (g_strcmp0 (command, "monitor") == 0)
2520     {
2521       if (handle_monitor (&argc,
2522                           &argv,
2523                           request_completion,
2524                           completion_cur,
2525                           completion_prev))
2526         ret = 0;
2527       goto out;
2528     }
2529   else if (g_strcmp0 (command, "wait") == 0)
2530     {
2531       if (handle_wait (&argc,
2532                        &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 (command, "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 (argv[3], &endp, 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 (completion_line,
2574                                &completion_argc,
2575                                &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 (completion_line, completion_point, &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 (completion_line, 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 ("help \nemit \ncall \nintrospect \nmonitor \nwait \n");
2617           ret = 0;
2618           goto out;
2619         }
2620       else
2621         {
2622           g_printerr ("Unknown command '%s'\n", command);
2623           usage (&argc, &argv, FALSE);
2624           goto out;
2625         }
2626     }
2627 
2628  out:
2629   g_free (completion_cur);
2630   g_free (completion_prev);
2631   return ret;
2632 }
2633