qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v4 12/23] monitor: Add completion for qdev paths


From: Jan Kiszka
Subject: [Qemu-devel] [PATCH v4 12/23] monitor: Add completion for qdev paths
Date: Wed, 16 Jun 2010 00:38:36 +0200

From: Jan Kiszka <address@hidden>

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 <address@hidden>
---
 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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]