Home | History | Annotate | Download | only in tools
      1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
      2 /* dbus-viewer.c Graphical D-Bus frontend utility
      3  *
      4  * Copyright (C) 2003 Red Hat, Inc.
      5  *
      6  * Licensed under the Academic Free License version 2.1
      7  *
      8  * This program is free software; you can redistribute it and/or modify
      9  * it under the terms of the GNU General Public License as published by
     10  * the Free Software Foundation; either version 2 of the License, or
     11  * (at your option) any later version.
     12  *
     13  * This program is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  * GNU General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU General Public License
     19  * along with this program; if not, write to the Free Software
     20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     21  *
     22  */
     23 #include <config.h>
     24 #include <stdlib.h>
     25 #include <errno.h>
     26 #include <stdio.h>
     27 #include <string.h>
     28 #include <gtk/gtk.h>
     29 #include "dbus-tree-view.h"
     30 #include "dbus-names-model.h"
     31 #include <glib/dbus-gparser.h>
     32 #include <glib/dbus-gutils.h>
     33 #include <dbus/dbus-glib.h>
     34 #include <glib/gi18n.h>
     35 
     36 static void
     37 show_error_dialog (GtkWindow *transient_parent,
     38                    GtkWidget **weak_ptr,
     39                    const char *message_format,
     40                    ...)
     41 {
     42   char *message;
     43   va_list args;
     44 
     45   if (message_format)
     46     {
     47       va_start (args, message_format);
     48       message = g_strdup_vprintf (message_format, args);
     49       va_end (args);
     50     }
     51   else
     52     message = NULL;
     53 
     54   if (weak_ptr == NULL || *weak_ptr == NULL)
     55     {
     56       GtkWidget *dialog;
     57       dialog = gtk_message_dialog_new (transient_parent,
     58                                        GTK_DIALOG_DESTROY_WITH_PARENT,
     59                                        GTK_MESSAGE_ERROR,
     60                                        GTK_BUTTONS_CLOSE,
     61                                        message);
     62 
     63       g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (gtk_widget_destroy), NULL);
     64 
     65       if (weak_ptr != NULL)
     66         {
     67           *weak_ptr = dialog;
     68           g_object_add_weak_pointer (G_OBJECT (dialog), (void**)weak_ptr);
     69         }
     70 
     71       gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
     72 
     73       gtk_widget_show_all (dialog);
     74     }
     75   else
     76     {
     77       g_return_if_fail (GTK_IS_MESSAGE_DIALOG (*weak_ptr));
     78 
     79       gtk_label_set_text (GTK_LABEL (GTK_MESSAGE_DIALOG (*weak_ptr)->label), message);
     80 
     81       gtk_window_present (GTK_WINDOW (*weak_ptr));
     82     }
     83 }
     84 
     85 typedef struct
     86 {
     87   DBusGConnection *connection;
     88 
     89   GtkWidget *window;
     90   GtkWidget *treeview;
     91   GtkWidget *name_menu;
     92 
     93   GtkTreeModel *names_model;
     94 
     95   GtkWidget *error_dialog;
     96 
     97 } TreeWindow;
     98 
     99 
    100 static void
    101 tree_window_set_node (TreeWindow *w,
    102                       NodeInfo   *node)
    103 {
    104   char **path;
    105   const char *name;
    106 
    107   name = node_info_get_name (node);
    108   if (name == NULL ||
    109       name[0] != '/')
    110     {
    111       g_printerr (_("Assuming root node is at path /, since no absolute path is specified"));
    112       name = "/";
    113     }
    114 
    115   path = _dbus_gutils_split_path (name);
    116 
    117   dbus_tree_view_update (GTK_TREE_VIEW (w->treeview),
    118                          (const char**) path,
    119                          node);
    120 
    121   g_strfreev (path);
    122 }
    123 
    124 typedef struct
    125 {
    126   DBusGConnection *connection;
    127   char *service_name;
    128   GError *error;
    129   NodeInfo *node;
    130   TreeWindow *window; /* Not touched from child thread */
    131 } LoadFromServiceData;
    132 
    133 static gboolean
    134 load_child_nodes (const char *service_name,
    135                   NodeInfo   *parent,
    136                   GString    *path,
    137                   GError    **error)
    138 {
    139   DBusGConnection *connection;
    140   GSList *tmp;
    141 
    142   connection = dbus_g_bus_get (DBUS_BUS_SESSION, error);
    143   if (connection == NULL)
    144     return FALSE;
    145 
    146   tmp = node_info_get_nodes (parent);
    147   while (tmp != NULL)
    148     {
    149       DBusGProxy *proxy;
    150       char *data;
    151       NodeInfo *child;
    152       NodeInfo *complete_child;
    153       int save_len;
    154 
    155       complete_child = NULL;
    156 
    157       child = tmp->data;
    158 
    159       save_len = path->len;
    160 
    161       if (save_len > 1)
    162         g_string_append (path, "/");
    163       g_string_append (path, base_info_get_name ((BaseInfo*)child));
    164 
    165       if (*service_name == ':')
    166         {
    167           proxy = dbus_g_proxy_new_for_name (connection,
    168                                              service_name,
    169                                              path->str,
    170                                              DBUS_INTERFACE_INTROSPECTABLE);
    171           g_assert (proxy != NULL);
    172         }
    173       else
    174         {
    175           proxy = dbus_g_proxy_new_for_name_owner (connection,
    176                                                    service_name,
    177                                                    path->str,
    178                                                    DBUS_INTERFACE_INTROSPECTABLE,
    179                                                    error);
    180           if (proxy == NULL)
    181             goto done;
    182         }
    183 
    184       if (!dbus_g_proxy_call (proxy, "Introspect", error,
    185                               G_TYPE_INVALID,
    186 			      G_TYPE_STRING, &data,
    187 			      G_TYPE_INVALID))
    188 	  goto done;
    189 
    190       complete_child = description_load_from_string (data, -1, error);
    191       g_free (data);
    192       if (complete_child == NULL)
    193         {
    194           g_printerr ("%s\n", data);
    195           goto done;
    196         }
    197 
    198     done:
    199       g_object_unref (proxy);
    200 
    201       if (complete_child == NULL)
    202         return FALSE;
    203 
    204       /* change complete_child's name to relative */
    205       base_info_set_name ((BaseInfo*)complete_child,
    206                           base_info_get_name ((BaseInfo*)child));
    207 
    208       /* Stitch in complete_child rather than child */
    209       node_info_replace_node (parent, child, complete_child);
    210       node_info_unref (complete_child); /* ref still held by parent */
    211 
    212       /* Now recurse */
    213       if (!load_child_nodes (service_name, complete_child, path, error))
    214         return FALSE;
    215 
    216       /* restore path */
    217       g_string_set_size (path, save_len);
    218 
    219       tmp = tmp->next;
    220     }
    221 
    222   return TRUE;
    223 }
    224 
    225 static gboolean
    226 load_from_service_complete_idle (void *data)
    227 {
    228   /* Called in main thread */
    229   GThread *thread = data;
    230   LoadFromServiceData *d;
    231   NodeInfo *node;
    232 
    233   d = g_thread_join (thread);
    234 
    235   node = d->node;
    236 
    237   if (d->error)
    238     {
    239       g_assert (d->node == NULL);
    240       show_error_dialog (GTK_WINDOW (d->window->window), &d->window->error_dialog,
    241                          _("Unable to load \"%s\": %s\n"),
    242                          d->service_name, d->error->message);
    243       g_error_free (d->error);
    244     }
    245   else
    246     {
    247       g_assert (d->error == NULL);
    248 
    249       tree_window_set_node (d->window, node);
    250       node_info_unref (node);
    251     }
    252 
    253   g_free (d->service_name);
    254   dbus_g_connection_unref (d->connection);
    255   g_free (d);
    256 
    257   return FALSE;
    258 }
    259 
    260 static void*
    261 load_from_service_thread_func (void *thread_data)
    262 {
    263   DBusGProxy *root_proxy;
    264   const char *data;
    265   NodeInfo *node;
    266   GString *path;
    267   LoadFromServiceData *lfsd;
    268 
    269   lfsd = thread_data;
    270 
    271   node = NULL;
    272   path = NULL;
    273 
    274 #if 1
    275   /* this will end up autolaunching the service when we introspect it */
    276   root_proxy = dbus_g_proxy_new_for_name (lfsd->connection,
    277                                           lfsd->service_name,
    278                                           "/",
    279                                           DBUS_INTERFACE_INTROSPECTABLE);
    280   g_assert (root_proxy != NULL);
    281 #else
    282   /* this will be an error if the service doesn't exist */
    283   root_proxy = dbus_g_proxy_new_for_name_owner (lfsd->connection,
    284                                                 lfsd->service_name,
    285                                                 "/",
    286                                                 DBUS_INTERFACE_INTROSPECTABLE,
    287                                                 &lfsd->error);
    288   if (root_proxy == NULL)
    289     {
    290       g_printerr ("Failed to get owner of '%s'\n", lfsd->service_name);
    291       return lfsd->data;
    292     }
    293 #endif
    294 
    295   if (!dbus_g_proxy_call (root_proxy, "Introspect", &lfsd->error,
    296 			  G_TYPE_INVALID,
    297 			  G_TYPE_STRING, &data,
    298 			  G_TYPE_INVALID))
    299     {
    300       g_printerr ("Failed to Introspect() %s\n",
    301 		  dbus_g_proxy_get_bus_name (root_proxy));
    302       goto out;
    303     }
    304 
    305   node = description_load_from_string (data, -1, &lfsd->error);
    306 
    307   /* g_print ("%s\n", data); */
    308 
    309   if (node == NULL)
    310     goto out;
    311 
    312   base_info_set_name ((BaseInfo*)node, "/");
    313 
    314   path = g_string_new ("/");
    315 
    316   if (!load_child_nodes (dbus_g_proxy_get_bus_name (root_proxy),
    317                          node, path, &lfsd->error))
    318     {
    319       node_info_unref (node);
    320       node = NULL;
    321       goto out;
    322     }
    323 
    324  out:
    325   g_object_unref (root_proxy);
    326 
    327   if (path)
    328     g_string_free (path, TRUE);
    329 
    330   lfsd->node = node;
    331   g_assert (lfsd->node || lfsd->error);
    332   g_assert (lfsd->node == NULL || lfsd->error == NULL);
    333 
    334   /* Add idle to main thread that will join us back */
    335   g_idle_add (load_from_service_complete_idle, g_thread_self ());
    336 
    337   return lfsd;
    338 }
    339 
    340 static void
    341 start_load_from_service (TreeWindow      *w,
    342                          DBusGConnection *connection,
    343                          const char      *service_name)
    344 {
    345   LoadFromServiceData *d;
    346 
    347   d = g_new0 (LoadFromServiceData, 1);
    348 
    349   d->connection = dbus_g_connection_ref (connection);
    350   d->service_name = g_strdup (service_name);
    351   d->error = NULL;
    352   d->node = NULL;
    353   d->window = w;
    354 
    355   g_thread_create (load_from_service_thread_func, d, TRUE, NULL);
    356 }
    357 
    358 static void
    359 tree_window_set_service (TreeWindow *w,
    360                          const char *service_name)
    361 {
    362   start_load_from_service (w, w->connection, service_name);
    363 }
    364 
    365 static void
    366 name_combo_changed_callback (GtkComboBox *combo,
    367                              TreeWindow  *w)
    368 {
    369   GtkTreeIter iter;
    370 
    371   if (gtk_combo_box_get_active_iter (combo, &iter))
    372     {
    373       GtkTreeModel *model;
    374       char *text;
    375 
    376       model = gtk_combo_box_get_model (combo);
    377       gtk_tree_model_get (model, &iter, 0, &text, -1);
    378 
    379       if (text)
    380         {
    381           tree_window_set_service (w, text);
    382           g_free (text);
    383         }
    384     }
    385 }
    386 
    387 static void
    388 window_closed_callback (GtkWidget  *window,
    389                         TreeWindow *w)
    390 {
    391   g_assert (window == w->window);
    392   w->window = NULL;
    393   gtk_main_quit ();
    394 }
    395 
    396 static TreeWindow*
    397 tree_window_new (DBusGConnection *connection,
    398                  GtkTreeModel    *names_model)
    399 {
    400   TreeWindow *w;
    401   GtkWidget *sw;
    402   GtkWidget *vbox;
    403   GtkWidget *hbox;
    404   GtkWidget *combo;
    405 
    406   /* Should use glade, blah */
    407 
    408   w = g_new0 (TreeWindow, 1);
    409   w->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    410 
    411   gtk_window_set_title (GTK_WINDOW (w->window), "D-Bus Viewer");
    412   gtk_window_set_default_size (GTK_WINDOW (w->window), 400, 500);
    413 
    414   g_signal_connect (w->window, "destroy", G_CALLBACK (window_closed_callback),
    415                     w);
    416   gtk_container_set_border_width (GTK_CONTAINER (w->window), 6);
    417 
    418   vbox = gtk_vbox_new (FALSE, 6);
    419   gtk_container_add (GTK_CONTAINER (w->window), vbox);
    420 
    421   /* Create names option menu */
    422   if (connection)
    423     {
    424       GtkCellRenderer *cell;
    425 
    426       w->connection = connection;
    427 
    428       w->names_model = names_model;
    429 
    430       combo = gtk_combo_box_new_with_model (w->names_model);
    431 
    432       cell = gtk_cell_renderer_text_new ();
    433       gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
    434       gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
    435                                       "text", 0,
    436                                       NULL);
    437 
    438       gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
    439 
    440       g_signal_connect (combo, "changed",
    441                         G_CALLBACK (name_combo_changed_callback),
    442                         w);
    443     }
    444 
    445   /* Create tree view */
    446   hbox = gtk_hbox_new (FALSE, 6);
    447   gtk_container_add (GTK_CONTAINER (vbox), hbox);
    448 
    449   sw = gtk_scrolled_window_new (NULL, NULL);
    450   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
    451                                   GTK_POLICY_AUTOMATIC,
    452                                   GTK_POLICY_AUTOMATIC);
    453 
    454   gtk_box_pack_start (GTK_BOX (hbox), sw, TRUE, TRUE, 0);
    455 
    456   w->treeview = dbus_tree_view_new ();
    457 
    458   gtk_container_add (GTK_CONTAINER (sw), w->treeview);
    459 
    460   /* Show everything */
    461   gtk_widget_show_all (w->window);
    462 
    463   return w;
    464 }
    465 
    466 static void
    467 usage (int ecode)
    468 {
    469   fprintf (stderr, "dbus-viewer [--version] [--help]\n");
    470   exit (ecode);
    471 }
    472 
    473 static void
    474 version (void)
    475 {
    476   printf ("D-Bus Message Bus Viewer %s\n"
    477           "Copyright (C) 2003 Red Hat, Inc.\n"
    478           "This is free software; see the source for copying conditions.\n"
    479           "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
    480           VERSION);
    481   exit (0);
    482 }
    483 
    484 int
    485 main (int argc, char **argv)
    486 {
    487   int i;
    488   GSList *files;
    489   gboolean end_of_args;
    490   GSList *tmp;
    491   gboolean services;
    492   DBusGConnection *connection;
    493   GError *error;
    494   GtkTreeModel *names_model;
    495 
    496   g_thread_init (NULL);
    497   dbus_g_thread_init ();
    498 
    499   bindtextdomain (GETTEXT_PACKAGE, DBUS_LOCALEDIR);
    500   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
    501   textdomain (GETTEXT_PACKAGE);
    502 
    503   gtk_init (&argc, &argv);
    504 
    505   services = FALSE;
    506   end_of_args = FALSE;
    507   files = NULL;
    508   i = 1;
    509   while (i < argc)
    510     {
    511       const char *arg = argv[i];
    512 
    513       if (!end_of_args)
    514         {
    515           if (strcmp (arg, "--help") == 0 ||
    516               strcmp (arg, "-h") == 0 ||
    517               strcmp (arg, "-?") == 0)
    518             usage (0);
    519           else if (strcmp (arg, "--version") == 0)
    520             version ();
    521           else if (strcmp (arg, "--services") == 0)
    522             services = TRUE;
    523           else if (arg[0] == '-' &&
    524                    arg[1] == '-' &&
    525                    arg[2] == '\0')
    526             end_of_args = TRUE;
    527           else if (arg[0] == '-')
    528             {
    529               usage (1);
    530             }
    531           else
    532             {
    533               files = g_slist_prepend (files, (char*) arg);
    534             }
    535         }
    536       else
    537         files = g_slist_prepend (files, (char*) arg);
    538 
    539       ++i;
    540     }
    541 
    542   if (services || files == NULL)
    543     {
    544       error = NULL;
    545       connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
    546       if (connection == NULL)
    547         {
    548           g_printerr ("Could not open bus connection: %s\n",
    549                       error->message);
    550           g_error_free (error);
    551           exit (1);
    552         }
    553 
    554       g_assert (connection == dbus_g_bus_get (DBUS_BUS_SESSION, NULL));
    555 
    556       names_model = names_model_new (connection);
    557     }
    558   else
    559     {
    560       connection = NULL;
    561       names_model = NULL;
    562     }
    563 
    564   if (files == NULL)
    565     {
    566       TreeWindow *w;
    567 
    568       w = tree_window_new (connection, names_model);
    569     }
    570 
    571   files = g_slist_reverse (files);
    572 
    573   tmp = files;
    574   while (tmp != NULL)
    575     {
    576       const char *filename;
    577       TreeWindow *w;
    578 
    579       filename = tmp->data;
    580 
    581       if (services)
    582         {
    583           w = tree_window_new (connection, names_model);
    584           tree_window_set_service (w, filename);
    585         }
    586       else
    587         {
    588           NodeInfo *node;
    589 
    590           error = NULL;
    591           node = description_load_from_file (filename,
    592                                              &error);
    593 
    594           if (node == NULL)
    595             {
    596               g_assert (error != NULL);
    597               show_error_dialog (NULL, NULL,
    598                                  _("Unable to load \"%s\": %s\n"),
    599                                  filename, error->message);
    600               g_error_free (error);
    601             }
    602           else
    603             {
    604               w = tree_window_new (connection, names_model);
    605               tree_window_set_node (w, node);
    606               node_info_unref (node);
    607             }
    608         }
    609 
    610       tmp = tmp->next;
    611     }
    612 
    613   gtk_main ();
    614 
    615   return 0;
    616 }
    617 
    618