From: Jan Kiszka <jan.kis...@siemens.com> Implement monitor command line completion for device tree paths. The first user is device_del.
Signed-off-by: Jan Kiszka <jan.kis...@siemens.com> --- hw/qdev.c | 50 ++++++++++++++++++++++---------- hw/qdev.h | 2 + monitor.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ qemu-monitor.hx | 2 +- 4 files changed, 123 insertions(+), 16 deletions(-) diff --git a/hw/qdev.c b/hw/qdev.c index db005ce..7db839f 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -39,7 +39,7 @@ DeviceInfo *device_info_list; static BusState *qbus_find_recursive(BusState *bus, const char *name, const BusInfo *info); -static BusState *qbus_find(const char *path); +static BusState *qbus_find_internal(const char *path, bool report_errors); /* Register a new device type. */ void qdev_register(DeviceInfo *info) @@ -217,7 +217,7 @@ DeviceState *qdev_device_add(QemuOpts *opts) /* find bus */ path = qemu_opt_get(opts, "bus"); if (path != NULL) { - bus = qbus_find(path); + bus = qbus_find_internal(path, true); if (!bus) { return NULL; } @@ -575,7 +575,7 @@ static DeviceState *qbus_find_dev(BusState *bus, const char *elem) return NULL; } -static BusState *qbus_find(const char *path) +static BusState *qbus_find_internal(const char *path, bool report_errors) { DeviceState *dev, *next_dev; BusState *bus; @@ -593,7 +593,9 @@ static BusState *qbus_find(const char *path) } bus = qbus_find_recursive(main_system_bus, elem, NULL); if (!bus) { - qerror_report(QERR_BUS_NOT_FOUND, elem); + if (report_errors) { + qerror_report(QERR_BUS_NOT_FOUND, elem); + } return NULL; } pos = len; @@ -616,8 +618,10 @@ static BusState *qbus_find(const char *path) pos += len; dev = qbus_find_dev(bus, elem); if (!dev) { - qerror_report(QERR_DEVICE_NOT_FOUND, elem); - qbus_list_dev(bus); + if (report_errors) { + qerror_report(QERR_DEVICE_NOT_FOUND, elem); + qbus_list_dev(bus); + } return NULL; } @@ -631,13 +635,17 @@ search_dev_bus: * one child bus accept it nevertheless */ switch (dev->num_child_bus) { case 0: - qerror_report(QERR_DEVICE_NO_BUS, elem); + if (report_errors) { + qerror_report(QERR_DEVICE_NO_BUS, elem); + } return NULL; case 1: return QTAILQ_FIRST(&dev->child_bus); default: - qerror_report(QERR_DEVICE_MULTIPLE_BUSSES, elem); - qbus_list_bus(dev); + if (report_errors) { + qerror_report(QERR_DEVICE_MULTIPLE_BUSSES, elem); + qbus_list_bus(dev); + } return NULL; } } @@ -659,14 +667,21 @@ search_dev_bus: goto search_dev_bus; } } - qerror_report(QERR_BUS_NOT_FOUND, elem); - qbus_list_bus(dev); + if (report_errors) { + qerror_report(QERR_BUS_NOT_FOUND, elem); + qbus_list_bus(dev); + } return NULL; } } } -static DeviceState *qdev_find(const char *path) +BusState *qbus_find(const char *path) +{ + return qbus_find_internal(path, false); +} + +static DeviceState *qdev_find_internal(const char *path, bool report_errors) { const char *dev_name; DeviceState *dev; @@ -686,7 +701,7 @@ static DeviceState *qdev_find(const char *path) bus_path = qemu_strdup(path); bus_path[dev_name - path] = 0; - bus = qbus_find(bus_path); + bus = qbus_find_internal(bus_path, report_errors); qemu_free(bus_path); if (!bus) { @@ -695,13 +710,18 @@ 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); } return dev; } +DeviceState *qdev_find(const char *path) +{ + return qdev_find_internal(path, false); +} + void qbus_create_inplace(BusState *bus, BusInfo *info, DeviceState *parent, const char *name) { @@ -863,7 +883,7 @@ int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data) const char *path = qdict_get_str(qdict, "path"); DeviceState *dev; - dev = qdev_find(path); + dev = qdev_find_internal(path, true); if (!dev) { qerror_report(QERR_DEVICE_NOT_FOUND, path); return -1; diff --git a/hw/qdev.h b/hw/qdev.h index 53f5565..b27d33b 100644 --- a/hw/qdev.h +++ b/hw/qdev.h @@ -165,6 +165,7 @@ void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n); CharDriverState *qdev_init_chardev(DeviceState *dev); BusState *qdev_get_parent_bus(DeviceState *dev); +DeviceState *qdev_find(const char *path); /*** BUS API. ***/ @@ -172,6 +173,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); #define FROM_QBUS(type, dev) DO_UPCAST(type, qbus, dev) diff --git a/monitor.c b/monitor.c index 4c95d7b..64de10a 100644 --- a/monitor.c +++ b/monitor.c @@ -64,6 +64,7 @@ * * 'F' filename * 'B' block device name + * '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 @@ -3437,6 +3438,7 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon, switch(c) { case 'F': case 'B': + case 'Q': case 's': { int ret; @@ -3461,6 +3463,10 @@ 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 device path expected\n", + cmdname); + break; default: monitor_printf(mon, "%s: string expected\n", cmdname); break; @@ -3947,6 +3953,79 @@ 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 qdev_completion(const char *input) +{ + size_t parent_len, name_len; + char *parent_path = NULL; + const char *p, *name; + DeviceState *dev; + BusState *bus; + + p = strrchr(input, '/'); + if (!p) { + if (*input == '\0') { + readline_add_completion(cur_mon->rs, "/"); + } + return; + } + + p++; + parent_path = qemu_strndup(input, p - input); + bus = qbus_find(parent_path); + + 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 = dev->id ? : dev->info->name; + if (strncmp(name, p, strlen(p)) == 0) { + add_qdev_completion(parent_path, name, false); + if (!QTAILQ_EMPTY(&dev->child_bus)) { + add_qdev_completion(parent_path, name, true); + } + } + } + } else { + parent_path[strlen(parent_path) - 1] = 0; + dev = qdev_find(parent_path); + 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) @@ -4044,6 +4123,11 @@ static void monitor_find_completion(const char *cmdline) readline_set_completion_index(cur_mon->rs, strlen(str)); bdrv_iterate(block_completion_it, (void *)str); break; + case 'Q': + /* qdev device path completion */ + readline_set_completion_index(cur_mon->rs, strlen(str)); + qdev_completion(str); + break; case 's': /* XXX: more generic ? */ if (!strcmp(cmd->name, "info")) { @@ -4122,6 +4206,7 @@ static int check_arg(const CmdArgs *cmd_args, QDict *args) switch (cmd_args->type) { case 'F': case 'B': + 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 754d71e..eb257a6 100644 --- a/qemu-monitor.hx +++ b/qemu-monitor.hx @@ -703,7 +703,7 @@ EQMP { .name = "device_del", - .args_type = "path:s", + .args_type = "path:Q", .params = "device", .help = "remove device", .user_print = monitor_user_noop, -- 1.6.0.2