From: Jan Kiszka <jan.kis...@siemens.com> 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 <jan.kis...@siemens.com> --- 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, -- 1.6.0.2