/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dbus-bash-completion-helper.c Bash Completion helper routines * * Copyright (C) 2008 David Zeuthen * * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include "dbus-gparser.h" static void print_services (DBusConnection *connection) { DBusMessage *message; DBusMessage *reply; DBusError error; DBusMessageIter iter; DBusMessageIter iter_array; const char *name; /* list both active and activatable names (the shell will sort and * uniquify them) - also avoid names that are not well-known * (e.g. :1.42). */ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "ListNames"); dbus_error_init (&error); reply = dbus_connection_send_with_reply_and_block (connection, message, -1, &error); dbus_message_unref (message); dbus_message_iter_init (reply, &iter); dbus_message_iter_recurse (&iter, &iter_array); while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) { dbus_message_iter_get_basic (&iter_array, &name); if (name[0] != ':') printf ("%s \n", name); dbus_message_iter_next (&iter_array); } dbus_message_unref (reply); message = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "ListActivatableNames"); dbus_error_init (&error); reply = dbus_connection_send_with_reply_and_block (connection, message, -1, &error); dbus_message_unref (message); dbus_message_iter_init (reply, &iter); dbus_message_iter_recurse (&iter, &iter_array); while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) { dbus_message_iter_get_basic (&iter_array, &name); printf ("%s \n", name); dbus_message_iter_next (&iter_array); } dbus_message_unref (reply); } static gboolean have_option (char **tokens, const char *option) { int n; for (n = 0; tokens[n] != NULL; n++) if (strcmp (tokens[n], option) == 0) return TRUE; return FALSE; } static gboolean have_option_with_value (char **tokens, const char *option, const char **value) { int n; for (n = 0; tokens[n] != NULL; n++) { if (g_str_has_prefix (tokens[n], option)) { if (strlen (tokens[n]) > strlen (option)) *value = tokens[n] + strlen (option); return TRUE; } } return FALSE; } static void print_objects (DBusConnection *connection, const char *service_name, const char *cur) { DBusMessage *message; DBusMessage *reply; DBusError error; DBusMessageIter iter; const char *introspection_xml; NodeInfo *root; GSList *nodes; GSList *l; if (cur == NULL) cur = "/"; message = dbus_message_new_method_call (service_name, cur, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"); dbus_error_init (&error); reply = dbus_connection_send_with_reply_and_block (connection, message, -1, &error); dbus_message_unref (message); dbus_message_iter_init (reply, &iter); dbus_message_iter_get_basic (&iter, &introspection_xml); root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL); nodes = node_info_get_nodes (root); if (g_slist_length (node_info_get_interfaces (root)) > 0) printf ("%s \n", cur); for (l = nodes; l != NULL; l = l->next) { NodeInfo *node = (NodeInfo *) l->data; const char *name; char *new_path; name = node_info_get_name (node); if (strcmp (cur, "/") == 0) new_path = g_strdup_printf ("/%s", name); else new_path = g_strdup_printf ("%s/%s", cur, name); print_objects (connection, service_name, new_path); g_free (new_path); } node_info_unref (root); dbus_message_unref (reply); } static gboolean is_object_path_with_interfaces (DBusConnection *connection, const char *service_name, const char *object_path) { DBusMessage *message; DBusMessage *reply; DBusError error; DBusMessageIter iter; const char *introspection_xml; NodeInfo *root; gboolean ret; ret = FALSE; message = dbus_message_new_method_call (service_name, object_path, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"); dbus_error_init (&error); reply = dbus_connection_send_with_reply_and_block (connection, message, -1, &error); dbus_message_unref (message); dbus_message_iter_init (reply, &iter); dbus_message_iter_get_basic (&iter, &introspection_xml); root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL); if (g_slist_length (node_info_get_interfaces (root)) > 0) ret = TRUE; node_info_unref (root); dbus_message_unref (reply); return ret; } static void print_methods (DBusConnection *connection, const char *service_name, const char *object_path) { DBusMessage *message; DBusMessage *reply; DBusError error; DBusMessageIter iter; const char *introspection_xml; NodeInfo *root; GSList *interfaces; GSList *l; message = dbus_message_new_method_call (service_name, object_path, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"); dbus_error_init (&error); reply = dbus_connection_send_with_reply_and_block (connection, message, -1, &error); dbus_message_unref (message); dbus_message_iter_init (reply, &iter); dbus_message_iter_get_basic (&iter, &introspection_xml); root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL); interfaces = node_info_get_interfaces (root); for (l = interfaces; l != NULL; l = l->next) { InterfaceInfo *interface = (InterfaceInfo *) l->data; GSList *methods; GSList *ll; methods = interface_info_get_methods (interface); for (ll = methods; ll != NULL; ll = ll->next) { MethodInfo *method = (MethodInfo *) ll->data; printf ("%s.%s \n", interface_info_get_name (interface), method_info_get_name (method)); } } node_info_unref (root); dbus_message_unref (reply); } static void print_signature (DBusConnection *connection, const char *service_name, const char *object_path, const char *method) { DBusMessage *message; DBusMessage *reply; DBusError error; DBusMessageIter iter; const char *introspection_xml; NodeInfo *root; GSList *interfaces; GSList *l; char *s; char *method_name; char *interface_name; int n; method_name = NULL; interface_name = NULL; s = strrchr (method, '.'); if (s == NULL || strlen (s) < 2 || s - method < 1) goto fail; method_name = g_strdup (s + 1); interface_name = g_strndup (method, s - method); printf (" \n"); message = dbus_message_new_method_call (service_name, object_path, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"); dbus_error_init (&error); reply = dbus_connection_send_with_reply_and_block (connection, message, -1, &error); dbus_message_unref (message); dbus_message_iter_init (reply, &iter); dbus_message_iter_get_basic (&iter, &introspection_xml); root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL); interfaces = node_info_get_interfaces (root); for (l = interfaces; l != NULL; l = l->next) { InterfaceInfo *interface = (InterfaceInfo *) l->data; if (strcmp (interface_name, interface_info_get_name (interface)) == 0) { GSList *methods; GSList *ll; methods = interface_info_get_methods (interface); for (ll = methods; ll != NULL; ll = ll->next) { MethodInfo *method = (MethodInfo *) ll->data; if (strcmp (method_name, method_info_get_name (method)) == 0) { GSList *args; GSList *lll; args = method_info_get_args (method); for (lll = args, n = 0; lll != NULL; lll = lll->next, n++) { ArgInfo *arg = (ArgInfo *) lll->data; printf ("# %s: arg %d: %s (%s) \n", arg_info_get_direction (arg) == ARG_IN ? " IN" : "OUT", n, arg_info_get_name (arg), arg_info_get_type (arg)); } break; } } } } node_info_unref (root); dbus_message_unref (reply); fail: g_free (method_name); g_free (interface_name); } static int complete_dbus_send (char *str) { int ret; char **tokens; int num_tokens; const char *cur; gboolean have_system; gboolean have_session; gboolean have_print_reply; gboolean have_dest; DBusConnection *connection; DBusBusType bus_type; DBusError error; const char *target_service; const char *object_path; const char *method; int n; int object_path_index; ret = 1; connection = NULL; target_service = NULL; tokens = g_strsplit (str, " ", 0); num_tokens = g_strv_length (tokens); if (num_tokens >= 1) { cur = tokens[num_tokens - 1]; } else { cur = ""; } have_system = have_option (tokens, "--system"); have_session = have_option (tokens, "--session"); have_print_reply = have_option (tokens, "--print-reply"); have_dest = have_option_with_value (tokens, "--dest=", &target_service); if (!have_print_reply) printf ("--print-reply \n"); if (!have_system && !have_session) { printf ("--system \n"); printf ("--session \n"); goto done; } if (!have_dest && !g_str_has_prefix (cur, "--dest=")) { printf ("--dest=\n"); goto done; } if (have_system || have_session) { bus_type = have_system ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION; dbus_error_init (&error); connection = dbus_bus_get (bus_type, &error); if (connection == NULL) { fprintf (stderr, "Failed to open connection to %s message bus: %s: %s\n", (bus_type == DBUS_BUS_SYSTEM) ? "system" : "session", error.name, error.message); dbus_error_free (&error); goto fail; } } if (connection != NULL && g_str_has_prefix (cur, "--dest=")) { print_services (connection); goto done; } /* see if we have an object path */ object_path = NULL; object_path_index = 0; if (connection != NULL && target_service != NULL) { for (n = 0; tokens[n] != NULL; n++) { if (tokens[n] == cur) continue; if (*(tokens[n]) == '/') { if (is_object_path_with_interfaces (connection, target_service, tokens[n])) { object_path = tokens[n]; object_path_index = n; } } } } /* if we have a connection and a destination but no object path, go ahead and list the object paths */ if (connection != NULL && target_service != NULL && object_path == NULL) { print_objects (connection, target_service, NULL); goto done; } /* see if we have a method; it's directly after the object_path */ method = NULL; if (connection != NULL && target_service != NULL && object_path != NULL) { if ((object_path_index + 1 < num_tokens - 1) && (strlen (tokens[object_path_index + 1]) > 0) && !(strcmp (cur, tokens[object_path_index + 1]) == 0)) method = tokens[object_path_index + 1]; } /* if we have connection, destination and object path but no method yet, list the methods */ if (connection != NULL && target_service != NULL && object_path != NULL && method == NULL) { print_methods (connection, target_service, object_path); goto done; } /* print signature as comment */ if (connection != NULL && target_service != NULL && object_path != NULL && method != NULL) { print_signature (connection, target_service, object_path, method); } done: ret = 0; fail: g_strfreev (tokens); if (connection != NULL) dbus_connection_unref (connection); return ret; } int main (int argc, char *argv[]) { int ret; char *cur; gboolean dbus_send; ret = 1; dbus_send = FALSE; if (argc != 3) { fprintf (stderr, "invalid use\n"); goto out; } if (strcmp (argv[1], "dbus-send") == 0) { dbus_send = TRUE; } else { fprintf (stderr, "unknown program '%s'\n", argv[1]); goto out; } if (strlen (argv[2]) < strlen (argv[1]) + 1) { fprintf (stderr, "error"); goto out; } cur = argv[2] + strlen (argv[1]) + 1; if (dbus_send) ret = complete_dbus_send (cur); out: return ret; }