dbus-bash-completion-helper.c

00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 /* dbus-bash-completion-helper.c  Bash Completion helper routines
00003  *
00004  * Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  */
00021 
00022 #include <config.h>
00023 #include <ctype.h>
00024 #include <stdio.h>
00025 #include <stdlib.h>
00026 #include <string.h>
00027 
00028 #include <dbus/dbus.h>
00029 #include <glib.h>
00030 #include "dbus-gparser.h"
00031 
00032 static void
00033 print_services (DBusConnection *connection)
00034 {
00035   DBusMessage *message;
00036   DBusMessage *reply;
00037   DBusError error;
00038   DBusMessageIter iter;
00039   DBusMessageIter iter_array;
00040   const char *name;
00041 
00042   /* list both active and activatable names (the shell will sort and
00043    * uniquify them) - also avoid names that are not well-known
00044    * (e.g. :1.42).
00045    */
00046 
00047   message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
00048                                           DBUS_PATH_DBUS,
00049                                           DBUS_INTERFACE_DBUS,
00050                                           "ListNames");
00051   dbus_error_init (&error);
00052   reply = dbus_connection_send_with_reply_and_block (connection,
00053                                                      message,
00054                                                      -1,
00055                                                      &error);
00056   dbus_message_unref (message);
00057   dbus_message_iter_init (reply, &iter);
00058   dbus_message_iter_recurse (&iter, &iter_array);
00059   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
00060     {
00061       dbus_message_iter_get_basic (&iter_array, &name);
00062       if (name[0] != ':')
00063         printf ("%s \n", name);
00064       dbus_message_iter_next (&iter_array);
00065     }
00066   dbus_message_unref (reply);
00067 
00068   message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
00069                                           DBUS_PATH_DBUS,
00070                                           DBUS_INTERFACE_DBUS,
00071                                           "ListActivatableNames");
00072   dbus_error_init (&error);
00073   reply = dbus_connection_send_with_reply_and_block (connection,
00074                                                      message,
00075                                                      -1,
00076                                                      &error);
00077   dbus_message_unref (message);
00078   dbus_message_iter_init (reply, &iter);
00079   dbus_message_iter_recurse (&iter, &iter_array);
00080   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
00081     {
00082       dbus_message_iter_get_basic (&iter_array, &name);
00083       printf ("%s \n", name);
00084       dbus_message_iter_next (&iter_array);
00085     }
00086   dbus_message_unref (reply);
00087 }
00088 
00089 static gboolean
00090 have_option (char **tokens, const char *option)
00091 {
00092   int n;
00093   for (n = 0; tokens[n] != NULL; n++)
00094     if (strcmp (tokens[n], option) == 0)
00095       return TRUE;
00096   return FALSE;
00097 }
00098 
00099 static gboolean
00100 have_option_with_value (char **tokens, const char *option, const char **value)
00101 {
00102   int n;
00103   for (n = 0; tokens[n] != NULL; n++)
00104     {
00105       if (g_str_has_prefix (tokens[n], option))
00106         {
00107           if (strlen (tokens[n]) > strlen (option))
00108             *value = tokens[n] + strlen (option);
00109           return TRUE;
00110         }
00111     }
00112   return FALSE;
00113 }
00114 
00115 static void
00116 print_objects (DBusConnection *connection, const char *service_name, const char *cur)
00117 {
00118   DBusMessage *message;
00119   DBusMessage *reply;
00120   DBusError error;
00121   DBusMessageIter iter;
00122   const char *introspection_xml;
00123   NodeInfo *root;
00124   GSList *nodes;
00125   GSList *l;
00126 
00127   if (cur == NULL)
00128     cur = "/";
00129 
00130   message = dbus_message_new_method_call (service_name,
00131                                           cur,
00132                                           DBUS_INTERFACE_INTROSPECTABLE,
00133                                           "Introspect");
00134   dbus_error_init (&error);
00135   reply = dbus_connection_send_with_reply_and_block (connection,
00136                                                      message,
00137                                                      -1,
00138                                                      &error);
00139   dbus_message_unref (message);
00140   dbus_message_iter_init (reply, &iter);
00141   dbus_message_iter_get_basic (&iter, &introspection_xml);
00142 
00143   root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL);
00144   nodes = node_info_get_nodes (root);
00145 
00146   if (g_slist_length (node_info_get_interfaces (root)) > 0)
00147     printf ("%s \n", cur);
00148 
00149   for (l = nodes; l != NULL; l = l->next)
00150     {
00151       NodeInfo *node = (NodeInfo *) l->data;
00152       const char *name;
00153       char *new_path;
00154 
00155       name = node_info_get_name (node);
00156       if (strcmp (cur, "/") == 0)
00157         new_path = g_strdup_printf ("/%s", name);
00158       else
00159         new_path = g_strdup_printf ("%s/%s", cur, name);
00160 
00161       print_objects (connection, service_name, new_path);
00162 
00163       g_free (new_path);
00164     }
00165   node_info_unref (root);
00166 
00167   dbus_message_unref (reply);
00168 }
00169 
00170 static gboolean
00171 is_object_path_with_interfaces (DBusConnection *connection, const char *service_name, const char *object_path)
00172 {
00173   DBusMessage *message;
00174   DBusMessage *reply;
00175   DBusError error;
00176   DBusMessageIter iter;
00177   const char *introspection_xml;
00178   NodeInfo *root;
00179   gboolean ret;
00180 
00181   ret = FALSE;
00182 
00183   message = dbus_message_new_method_call (service_name,
00184                                           object_path,
00185                                           DBUS_INTERFACE_INTROSPECTABLE,
00186                                           "Introspect");
00187   dbus_error_init (&error);
00188   reply = dbus_connection_send_with_reply_and_block (connection,
00189                                                      message,
00190                                                      -1,
00191                                                      &error);
00192   dbus_message_unref (message);
00193   dbus_message_iter_init (reply, &iter);
00194   dbus_message_iter_get_basic (&iter, &introspection_xml);
00195 
00196   root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL);
00197 
00198   if (g_slist_length (node_info_get_interfaces (root)) > 0)
00199     ret = TRUE;
00200 
00201   node_info_unref (root);
00202   dbus_message_unref (reply);
00203 
00204   return ret;
00205 }
00206 
00207 static void
00208 print_methods (DBusConnection *connection, const char *service_name, const char *object_path)
00209 {
00210   DBusMessage *message;
00211   DBusMessage *reply;
00212   DBusError error;
00213   DBusMessageIter iter;
00214   const char *introspection_xml;
00215   NodeInfo *root;
00216   GSList *interfaces;
00217   GSList *l;
00218 
00219   message = dbus_message_new_method_call (service_name,
00220                                           object_path,
00221                                           DBUS_INTERFACE_INTROSPECTABLE,
00222                                           "Introspect");
00223   dbus_error_init (&error);
00224   reply = dbus_connection_send_with_reply_and_block (connection,
00225                                                      message,
00226                                                      -1,
00227                                                      &error);
00228   dbus_message_unref (message);
00229   dbus_message_iter_init (reply, &iter);
00230   dbus_message_iter_get_basic (&iter, &introspection_xml);
00231 
00232   root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL);
00233   interfaces = node_info_get_interfaces (root);
00234   for (l = interfaces; l != NULL; l = l->next)
00235     {
00236       InterfaceInfo *interface = (InterfaceInfo *) l->data;
00237       GSList *methods;
00238       GSList *ll;
00239       methods = interface_info_get_methods (interface);
00240       for (ll = methods; ll != NULL; ll = ll->next)
00241         {
00242           MethodInfo *method = (MethodInfo *) ll->data;
00243           printf ("%s.%s \n", interface_info_get_name (interface), method_info_get_name (method));
00244         }
00245     }
00246   node_info_unref (root);
00247   dbus_message_unref (reply);
00248 }
00249 
00250 static void
00251 print_signature (DBusConnection *connection, const char *service_name, const char *object_path, const char *method)
00252 {
00253   DBusMessage *message;
00254   DBusMessage *reply;
00255   DBusError error;
00256   DBusMessageIter iter;
00257   const char *introspection_xml;
00258   NodeInfo *root;
00259   GSList *interfaces;
00260   GSList *l;
00261   char *s;
00262   char *method_name;
00263   char *interface_name;
00264   int n;
00265 
00266   method_name = NULL;
00267   interface_name = NULL;
00268 
00269   s = strrchr (method, '.');
00270   if (s == NULL || strlen (s) < 2 || s - method < 1)
00271     goto fail;
00272   method_name = g_strdup (s + 1);
00273   interface_name = g_strndup (method, s - method);
00274   printf (" \n");
00275 
00276   message = dbus_message_new_method_call (service_name,
00277                                           object_path,
00278                                           DBUS_INTERFACE_INTROSPECTABLE,
00279                                           "Introspect");
00280   dbus_error_init (&error);
00281   reply = dbus_connection_send_with_reply_and_block (connection,
00282                                                      message,
00283                                                      -1,
00284                                                      &error);
00285   dbus_message_unref (message);
00286   dbus_message_iter_init (reply, &iter);
00287   dbus_message_iter_get_basic (&iter, &introspection_xml);
00288 
00289   root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL);
00290   interfaces = node_info_get_interfaces (root);
00291   for (l = interfaces; l != NULL; l = l->next)
00292     {
00293       InterfaceInfo *interface = (InterfaceInfo *) l->data;
00294 
00295       if (strcmp (interface_name, interface_info_get_name (interface)) == 0)
00296         {
00297           GSList *methods;
00298           GSList *ll;
00299           methods = interface_info_get_methods (interface);
00300           for (ll = methods; ll != NULL; ll = ll->next)
00301             {
00302               MethodInfo *method = (MethodInfo *) ll->data;
00303               if (strcmp (method_name, method_info_get_name (method)) == 0)
00304                 {
00305                   GSList *args;
00306                   GSList *lll;
00307                   args = method_info_get_args (method);
00308                   for (lll = args, n = 0; lll != NULL; lll = lll->next, n++)
00309                     {
00310                       ArgInfo *arg = (ArgInfo *) lll->data;
00311                       printf ("#    %s: arg %d: %s (%s)         \n",
00312                               arg_info_get_direction (arg) == ARG_IN ? " IN" : "OUT",
00313                               n,
00314                               arg_info_get_name (arg),
00315                               arg_info_get_type (arg));
00316                     }
00317                   break;
00318                 }
00319             }
00320         }
00321     }
00322   node_info_unref (root);
00323   dbus_message_unref (reply);
00324  fail:
00325   g_free (method_name);
00326   g_free (interface_name);
00327 }
00328 
00329 
00330 static int
00331 complete_dbus_send (char *str)
00332 {
00333   int ret;
00334   char **tokens;
00335   int num_tokens;
00336   const char *cur;
00337   const char *prev;
00338   gboolean have_system;
00339   gboolean have_session;
00340   gboolean have_print_reply;
00341   gboolean have_dest;
00342   DBusConnection *connection;
00343   DBusBusType bus_type;
00344   DBusError error;
00345   const char *target_service;
00346   const char *object_path;
00347   const char *method;
00348   int n;
00349   int object_path_index;
00350 
00351   ret = 1;
00352   connection = NULL;
00353   target_service = NULL;
00354 
00355   tokens = g_strsplit (str, " ", 0);
00356   num_tokens = g_strv_length (tokens);
00357   if (num_tokens >= 2) {
00358     cur = tokens[num_tokens - 1];
00359     prev = tokens[num_tokens - 2];
00360   } else if (num_tokens == 1) {
00361     cur = tokens[num_tokens - 1];
00362     prev = "";
00363   } else {
00364     cur = "";
00365     prev = "";
00366   }
00367 
00368   have_system = have_option (tokens, "--system");
00369   have_session = have_option (tokens, "--session");
00370   have_print_reply = have_option (tokens, "--print-reply");
00371   have_dest = have_option_with_value (tokens, "--dest=", &target_service);
00372 
00373   if (!have_print_reply)
00374     printf ("--print-reply \n");
00375 
00376   if (!have_system && !have_session)
00377     {
00378       printf ("--system \n");
00379       printf ("--session \n");
00380       goto done;
00381     }
00382 
00383   if (!have_dest && !g_str_has_prefix (cur, "--dest="))
00384     {
00385       printf ("--dest=\n");
00386       goto done;
00387     }
00388 
00389   if (have_system || have_session)
00390     {
00391       bus_type = have_system ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION;
00392 
00393       dbus_error_init (&error);
00394       connection = dbus_bus_get (bus_type, &error);
00395       if (connection == NULL)
00396         {
00397           fprintf (stderr, "Failed to open connection to %s message bus: %s: %s\n",
00398                    (bus_type == DBUS_BUS_SYSTEM) ? "system" : "session",
00399                    error.name, error.message);
00400           dbus_error_free (&error);
00401           goto fail;
00402         }
00403     }
00404 
00405   if (connection != NULL && g_str_has_prefix (cur, "--dest="))
00406     {
00407       print_services (connection);
00408       goto done;
00409     }
00410 
00411   /* see if we have an object path */
00412   object_path = NULL;
00413   object_path_index = 0;
00414   if (connection != NULL && target_service != NULL)
00415     {
00416       for (n = 0; tokens[n] != NULL; n++)
00417         {
00418           if (tokens[n] == cur)
00419             continue;
00420 
00421           if (*(tokens[n]) == '/')
00422             {
00423               if (is_object_path_with_interfaces (connection, target_service, tokens[n]))
00424                 {
00425                   object_path = tokens[n];
00426                   object_path_index = n;
00427                 }
00428             }
00429         }
00430     }
00431 
00432   /* if we have a connection and a destination but no object path, go ahead and list the object paths */
00433   if (connection != NULL && target_service != NULL && object_path == NULL)
00434     {
00435       print_objects (connection, target_service, NULL);
00436       goto done;
00437     }
00438 
00439   /* see if we have a method; it's directly after the object_path */
00440   method = NULL;
00441   if (connection != NULL && target_service != NULL && object_path != NULL)
00442     {
00443       if ((object_path_index + 1 < num_tokens - 1) &&
00444           (strlen (tokens[object_path_index + 1]) > 0) &&
00445           !(strcmp (cur, tokens[object_path_index + 1]) == 0))
00446         method = tokens[object_path_index + 1];
00447     }
00448 
00449   /* if we have connection, destination and object path but no method yet, list the methods */
00450   if (connection != NULL && target_service != NULL && object_path != NULL && method == NULL)
00451     {
00452       print_methods (connection, target_service, object_path);
00453       goto done;
00454     }
00455 
00456   /* print signature as comment */
00457   if (connection != NULL && target_service != NULL && object_path != NULL && method != NULL)
00458     {
00459       print_signature (connection, target_service, object_path, method);
00460     }
00461 
00462  done:
00463   ret = 0;
00464 
00465  fail:
00466 
00467   g_strfreev (tokens);
00468 
00469   if (connection != NULL)
00470     dbus_connection_unref (connection);
00471   return ret;
00472 }
00473 
00474 int
00475 main (int argc, char *argv[])
00476 {
00477   int ret;
00478   char *cur;
00479   gboolean dbus_send;
00480 
00481   ret = 1;
00482   dbus_send = FALSE;
00483 
00484   if (argc != 3)
00485     {
00486       fprintf (stderr, "invalid use\n");
00487       goto out;
00488     }
00489 
00490   if (strcmp (argv[1], "dbus-send") == 0)
00491     {
00492       dbus_send = TRUE;
00493     }
00494   else
00495     {
00496       fprintf (stderr, "unknown program '%s'\n", argv[1]);
00497       goto out;
00498     }
00499 
00500   if (strlen (argv[2]) < strlen (argv[1]) + 1)
00501     {
00502       fprintf (stderr, "error");
00503       goto out;
00504     }
00505 
00506   cur = argv[2] + strlen (argv[1]) + 1;
00507 
00508   if (dbus_send)
00509     ret = complete_dbus_send (cur);
00510 
00511  out:
00512   return ret;
00513 }

Generated on Tue Feb 24 16:47:58 2009 for D-BUSGLibBindings by  doxygen 1.5.1