Patchwork [v4,12/23] monitor: Add completion for qdev paths

login
register
mail settings
Submitter Jan Kiszka
Date June 15, 2010, 10:38 p.m.
Message ID <689485bfec05dff41775e244f8f8da06026669f9.1276641524.git.jan.kiszka@web.de>
Download mbox | patch
Permalink /patch/55813/
State New
Headers show

Comments

Jan Kiszka - June 15, 2010, 10:38 p.m.
From: Jan Kiszka <jan.kiszka@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.kiszka@siemens.com>
---
 hw/qdev.c       |   19 +++++----
 hw/qdev.h       |    3 +
 monitor.c       |  115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 qemu-monitor.hx |    4 +-
 4 files changed, 129 insertions(+), 12 deletions(-)
Markus Armbruster - June 23, 2010, 9:46 a.m.
Jan Kiszka <jan.kiszka@web.de> writes:

> From: Jan Kiszka <jan.kiszka@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.kiszka@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

Nitpick: you add "Example: 'device:O(bus:Q)'" in patch 11, before 'Q'
gets defined in patch 12.

Patch

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,