From patchwork Tue Jun 15 22:38:36 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Kiszka X-Patchwork-Id: 55813 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 3880EB7D85 for ; Wed, 16 Jun 2010 08:51:51 +1000 (EST) Received: from localhost ([127.0.0.1]:46434 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OOeyw-0006Oc-5h for incoming@patchwork.ozlabs.org; Tue, 15 Jun 2010 18:51:46 -0400 Received: from [140.186.70.92] (port=33480 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OOems-00085x-3q for qemu-devel@nongnu.org; Tue, 15 Jun 2010 18:39:25 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OOemj-0005YD-Sy for qemu-devel@nongnu.org; Tue, 15 Jun 2010 18:39:17 -0400 Received: from fmmailgate02.web.de ([217.72.192.227]:39305) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OOemj-0005XM-7B for qemu-devel@nongnu.org; Tue, 15 Jun 2010 18:39:09 -0400 Received: from smtp04.web.de ( [172.20.0.225]) by fmmailgate02.web.de (Postfix) with ESMTP id 86FBB167181E2; Wed, 16 Jun 2010 00:39:08 +0200 (CEST) Received: from [92.74.52.35] (helo=localhost.localdomain) by smtp04.web.de with asmtp (TLSv1:AES256-SHA:256) (WEB.DE 4.110 #4) id 1OOemi-0003C9-00; Wed, 16 Jun 2010 00:39:08 +0200 From: Jan Kiszka To: qemu-devel@nongnu.org, Anthony Liguori Date: Wed, 16 Jun 2010 00:38:36 +0200 Message-Id: <689485bfec05dff41775e244f8f8da06026669f9.1276641524.git.jan.kiszka@web.de> X-Mailer: git-send-email 1.6.0.2 In-Reply-To: References: In-Reply-To: References: X-Sender: jan.kiszka@web.de X-Provags-ID: V01U2FsdGVkX1/dmD4Xd4bUIA+YJ/OL2YdpFRHcchD90k/3KK1w XzZr3ULn9GdWm8lfuvIi6YaFbPz8PV01ZPr1caIS3xfYDGXoA6 WEBXNVDEA= X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4-2.6 Cc: Juan Quintela , Jan Kiszka , Markus Armbruster , Luiz Capitulino , Blue Swirl , Avi Kivity Subject: [Qemu-devel] [PATCH v4 12/23] monitor: Add completion for qdev paths X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Jan Kiszka Implement monitor command line completion for device tree paths. The first user are device_add ('bus' option) and device_del. Signed-off-by: Jan Kiszka --- hw/qdev.c | 19 +++++---- hw/qdev.h | 3 + monitor.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- qemu-monitor.hx | 4 +- 4 files changed, 129 insertions(+), 12 deletions(-) diff --git a/hw/qdev.c b/hw/qdev.c index 466d8d5..dbc5b10 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -39,7 +39,6 @@ DeviceInfo *device_info_list; static BusState *qbus_find_recursive(BusState *bus, const char *name, const BusInfo *info); -static BusState *qbus_find(const char *path, bool report_errors); /* Register a new device type. */ void qdev_register(DeviceInfo *info) @@ -514,7 +513,7 @@ static DeviceState *qdev_find_id_recursive(BusState *bus, const char *id) return qdev_iterate_recursive(bus, find_id_callback, (void *)id); } -static int qdev_instance_no(DeviceState *dev) +int qdev_instance_no(DeviceState *dev) { struct DeviceState *sibling; int instance = 0; @@ -611,7 +610,7 @@ static DeviceState *qbus_find_dev(BusState *bus, const char *elem) return NULL; } -static BusState *qbus_find(const char *path, bool report_errors) +BusState *qbus_find(const char *path, bool report_errors) { DeviceState *dev; BusState *bus = main_system_bus; @@ -678,7 +677,7 @@ static BusState *qbus_find(const char *path, bool report_errors) } } -static DeviceState *qdev_find(const char *path) +DeviceState *qdev_find(const char *path, bool report_errors) { const char *dev_name; DeviceState *dev; @@ -688,7 +687,7 @@ static DeviceState *qdev_find(const char *path) /* search for unique ID recursively if path is not absolute */ if (path[0] != '/') { dev = qdev_find_id_recursive(main_system_bus, path); - if (!dev) { + if (!dev && report_errors) { qerror_report(QERR_DEVICE_NOT_FOUND, path); } return dev; @@ -702,8 +701,10 @@ static DeviceState *qdev_find(const char *path) bus = qbus_find(bus_path, false); qemu_free(bus_path); if (!bus) { - /* retry with full path to generate correct error message */ - bus = qbus_find(path, true); + if (report_errors) { + /* retry with full path to generate correct error message */ + bus = qbus_find(path, report_errors); + } if (!bus) { return NULL; } @@ -711,7 +712,7 @@ static DeviceState *qdev_find(const char *path) } dev = qbus_find_dev(bus, dev_name); - if (!dev) { + if (!dev && report_errors) { qerror_report(QERR_DEVICE_NOT_FOUND, dev_name); qbus_list_dev(bus); } @@ -880,7 +881,7 @@ int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data) const char *path = qdict_get_str(qdict, "device"); DeviceState *dev; - dev = qdev_find(path); + dev = qdev_find(path, true); if (!dev) { return -1; } diff --git a/hw/qdev.h b/hw/qdev.h index 111c876..59159a0 100644 --- a/hw/qdev.h +++ b/hw/qdev.h @@ -171,6 +171,8 @@ CharDriverState *qdev_init_chardev(DeviceState *dev); BusState *qdev_get_parent_bus(DeviceState *dev); void *qdev_iterate_recursive(BusState *bus, qdev_iteratefn callback, void *opaque); +DeviceState *qdev_find(const char *path, bool report_errors); +int qdev_instance_no(DeviceState *dev); /*** BUS API. ***/ @@ -178,6 +180,7 @@ void qbus_create_inplace(BusState *bus, BusInfo *info, DeviceState *parent, const char *name); BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name); void qbus_free(BusState *bus); +BusState *qbus_find(const char *path, bool report_errors); #define FROM_QBUS(type, dev) DO_UPCAST(type, qbus, dev) diff --git a/monitor.c b/monitor.c index 3e0d862..f535c56 100644 --- a/monitor.c +++ b/monitor.c @@ -64,12 +64,14 @@ * * 'F' filename * 'B' block device name + * 'q' qdev bus path + * 'Q' qdev device path * 's' string (accept optional quote) * 'O' option string of the form NAME=VALUE,... * parsed according to QemuOptsList given by its name * Example: 'device:O' uses qemu_device_opts. * Command completion for specific keys can be requested via - * appending '(NAME:TYPE,...)' with 'F', 'B' as type. + * appending '(NAME:TYPE,...)' with 'F', 'B', 'q', 'Q' as type. * Example: 'device:O(bus:Q)' to expand 'bus=...' as qtree path. * Restriction: only lists with empty desc are supported * TODO lift the restriction @@ -3318,6 +3320,8 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon, switch(c) { case 'F': case 'B': + case 'q': + case 'Q': case 's': { int ret; @@ -3342,6 +3346,14 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon, monitor_printf(mon, "%s: block device name expected\n", cmdname); break; + case 'q': + monitor_printf(mon, "%s: qdev bus path expected\n", + cmdname); + break; + case 'Q': + monitor_printf(mon, "%s: qdev device path expected\n", + cmdname); + break; default: monitor_printf(mon, "%s: string expected\n", cmdname); break; @@ -3833,6 +3845,99 @@ static void block_completion_it(void *opaque, BlockDriverState *bs) } } +static void add_qdev_completion(const char *parent_path, const char *name, + bool trailing_slash) +{ + size_t parent_len = strlen(parent_path); + size_t name_len = strlen(name); + char *completion = qemu_malloc(parent_len + name_len + 2); + + memcpy(completion, parent_path, parent_len); + memcpy(completion + parent_len, name, name_len); + if (trailing_slash) { + completion[parent_len + name_len] = '/'; + completion[parent_len + name_len + 1] = 0; + } else { + completion[parent_len + name_len] = 0; + } + readline_add_completion(cur_mon->rs, completion); + + qemu_free(completion); +} + +static void *inspect_qdev_id(DeviceState *dev, void *opaque) +{ + const char *input = opaque; + + if (dev->id && strncmp(dev->id, input, strlen(input)) == 0) { + add_qdev_completion("", dev->id, false); + } + return NULL; +} + +static void qdev_completion(const char *input, bool find_bus) +{ + size_t parent_len, name_len; + char *parent_path; + const char *p; + char *name; + DeviceState *dev; + BusState *bus; + + p = strrchr(input, '/'); + if (!p) { + if (*input == '\0') { + readline_add_completion(cur_mon->rs, "/"); + } + if (!find_bus) { + qdev_iterate_recursive(NULL, inspect_qdev_id, (void *)input); + } + return; + } + + p++; + parent_path = qemu_strndup(input, p - input); + bus = qbus_find(parent_path, false); + + if (bus) { + parent_len = strlen(parent_path); + name_len = strlen(bus->name); + if (bus->parent && strncmp(bus->name, p, strlen(p)) == 0 && + (parent_len - 1 < name_len || + strncmp(parent_path + parent_len - 1 - name_len, bus->name, + name_len) != 0)) { + add_qdev_completion(parent_path, bus->name, true); + } + QTAILQ_FOREACH(dev, &bus->children, sibling) { + name_len = strlen(dev->info->name) + 16; + name = qemu_malloc(name_len); + snprintf(name, name_len, "%s.%d", dev->info->name, + qdev_instance_no(dev)); + if (strncmp(name, p, strlen(p)) == 0) { + if (!find_bus) { + add_qdev_completion(parent_path, name, false); + } + if (!QTAILQ_EMPTY(&dev->child_bus)) { + add_qdev_completion(parent_path, name, true); + } + } + qemu_free(name); + } + } else { + parent_path[strlen(parent_path) - 1] = 0; + dev = qdev_find(parent_path, false); + if (dev) { + parent_path[strlen(parent_path)] = '/'; + QTAILQ_FOREACH(bus, &dev->child_bus, sibling) { + if (strncmp(bus->name, p, strlen(p)) == 0) { + add_qdev_completion(parent_path, bus->name, true); + } + } + } + } + qemu_free(parent_path); +} + /* NOTE: this parser is an approximate form of the real command parser */ static void parse_cmdline(const char *cmdline, int *pnb_args, char **args) @@ -3878,6 +3983,12 @@ static bool process_completion_type(char type, const char *str) readline_set_completion_index(cur_mon->rs, strlen(str)); bdrv_iterate(block_completion_it, (void *)str); return true; + case 'q': + case 'Q': + /* qdev bus/device path completion */ + readline_set_completion_index(cur_mon->rs, strlen(str)); + qdev_completion(str, (type == 'q')); + return true; default: return false; } @@ -4048,6 +4159,8 @@ static int check_arg(const CmdArgs *cmd_args, QDict *args) switch (cmd_args->type) { case 'F': case 'B': + case 'q': + case 'Q': case 's': if (qobject_type(value) != QTYPE_QSTRING) { qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "string"); diff --git a/qemu-monitor.hx b/qemu-monitor.hx index b5d0f6d..0034fed 100644 --- a/qemu-monitor.hx +++ b/qemu-monitor.hx @@ -660,7 +660,7 @@ ETEXI { .name = "device_add", - .args_type = "device:O(drive:B)", + .args_type = "device:O(bus:q,drive:B)", .params = "driver[,prop=value][,...]", .help = "add device, like -device on the command line", .user_print = monitor_user_noop, @@ -703,7 +703,7 @@ EQMP { .name = "device_del", - .args_type = "device:s", + .args_type = "device:Q", .params = "device", .help = "remove device", .user_print = monitor_user_noop,