MkfsSion <mkfss...@mkfssion.com> writes:

> When using JSON syntax for -device, -set option can not find device
> specified in JSON by id field. The following commandline is an example:
>
> $ qemu-system-x86_64 -device '{"id":"foo"}' -set device.foo.bar=1
> qemu-system-x86_64: -set device.foo.bar=1: there is no device "foo" defined

Is this a regression?  I suspect commit 5dacda5167 "vl: Enable JSON
syntax for -device" (v6.2.0).

I believe any conversion away from QemuOpts loses -set.

> The patch adds -set options to JSON device opts dict for adding
> options to device by latter object_set_properties_from_keyval call.
>
> Signed-off-by: YuanYang Meng <mkfss...@mkfssion.com>
> ---
>  include/qemu/option.h |  4 ++++
>  softmmu/vl.c          | 28 ++++++++++++++++++++++++++++
>  util/qemu-option.c    |  2 +-
>  3 files changed, 33 insertions(+), 1 deletion(-)
>
> diff --git a/include/qemu/option.h b/include/qemu/option.h
> index 306bf07575..31fa9fdc25 100644
> --- a/include/qemu/option.h
> +++ b/include/qemu/option.h
> @@ -45,6 +45,10 @@ const char *get_opt_value(const char *p, char **value);
>  
>  bool parse_option_size(const char *name, const char *value,
>                         uint64_t *ret, Error **errp);
> +
> +bool parse_option_number(const char *name, const char *value,
> +                         uint64_t *ret, Error **errp);
> +
>  bool has_help_option(const char *param);
>  
>  enum QemuOptType {
> diff --git a/softmmu/vl.c b/softmmu/vl.c
> index 620a1f1367..feb3c49a65 100644
> --- a/softmmu/vl.c
> +++ b/softmmu/vl.c
> @@ -30,7 +30,9 @@
>  #include "hw/qdev-properties.h"
>  #include "qapi/compat-policy.h"
>  #include "qapi/error.h"
> +#include "qapi/qmp/qbool.h"
>  #include "qapi/qmp/qdict.h"
> +#include "qapi/qmp/qnum.h"
>  #include "qapi/qmp/qstring.h"
>  #include "qapi/qmp/qjson.h"
>  #include "qemu-version.h"
> @@ -2279,6 +2281,7 @@ static void qemu_set_option(const char *str, Error 
> **errp)
>      char group[64], id[64], arg[64];
>      QemuOptsList *list;
>      QemuOpts *opts;
> +    DeviceOption *opt;
>      int rc, offset;
>  
>      rc = sscanf(str, "%63[^.].%63[^.].%63[^=]%n", group, id, arg, &offset);
> @@ -2294,6 +2297,31 @@ static void qemu_set_option(const char *str, Error 
> **errp)
>          if (list) {
>              opts = qemu_opts_find(list, id);
>              if (!opts) {
> +                QTAILQ_FOREACH(opt, &device_opts, next) {
> +                    const char *device_id = qdict_get_try_str(opt->opts, 
> "id");
> +                    if (device_id && (strcmp(device_id, id) == 0)) {
> +                        if (qdict_get(opt->opts, arg)) {
> +                            qdict_del(opt->opts, arg);
> +                        }
> +                        const char *value = str + offset + 1;
> +                        QObject *obj = NULL;
> +                        bool boolean;
> +                        uint64_t num;
> +                        if (qapi_bool_parse(arg, value, &boolean, NULL)) {
> +                            obj = QOBJECT(qbool_from_bool(boolean));
> +                        } else if (parse_option_number(arg, value, &num, 
> NULL)) {
> +                            obj = QOBJECT(qnum_from_uint(num));
> +                        } else if (parse_option_size(arg, value, &num, 
> NULL)) {
> +                            obj = QOBJECT(qnum_from_uint(num));
> +                        } else {
> +                            obj = QOBJECT(qstring_from_str(value));
> +                        }
> +                        if (obj) {
> +                            qdict_put_obj(opt->opts, arg, obj);
> +                            return;
> +                        }
> +                    }
> +                }
>                  error_setg(errp, "there is no %s \"%s\" defined", group, id);
>                  return;
>              }
               qemu_opt_set(opts, arg, str + offset + 1, errp);
           }
       }
   }

Two issues, and only looks fixable.

-device accepts either a QemuOpts or a JSON argument.

It parses the former with qemu_opts_parse_noisily() into a QemuOpt
stored in @qemu_device_opts.

It parses the latter with qobject_from_json() into a QObject stored in
@device_opts.  Yes, the names are confusing.

-set parses its argument into @group, @id, and @arg (the value).

Before the patch, it uses @group and @id to look up the QemuOpt in
@qemu_device_opts.  If found, it updates it with qemu_opt_set().

By design, -set operates on the QemuOpts store.

Options stored in @device_opts are not found.  Your patch tries to fix
that.  Before I can explain what's wrong with it, I need more
background.

QemuOpts arguments are parsed as a set of (key, value) pairs, where the
values are all strings (because qemu_device_opts.desc[] is empty).  We
access them with a qobject_input_visitor_new_keyval() visitor.  This
parses the strings according to the types being visited.

Example: key=42 gets stored as {"key": "42"}.  If we visit it with
visit_type_str(), we use string value "42".  If we visit it with
visit_type_int() or similar, we use integer value 42.  If we visit it
with visit_type_number(), we use double value 42.0.  If we visit it with
something else, we error out.

JSON arguments are parsed as arbitrary JSON object.  We access them with
a qobject_input_visitor_new() visitor.  This expects the values to have
JSON types appropriate for the types being visited.

Example: {"key": "42"} gets stored as is.  Now everything but
visit_type_str() errors out.

Back to your patch.  It adds code to look up a QObject in @device_opts.
If found, it updates it.

Issue#1: it does so regardless of @group.

Example:

    $ qemu-system-x86_64 -nodefaults -S -display none -M q35,usb=on -device 
'{"driver": "usb-mouse", "id": "ms0"}' -set chardev.ms0.serial=456

Here, -set chardev... gets misinterpreted as -set device...

Issue#2 is the value to store in @device_opts.  Always storing a string,
like in the QemuOpts case, would be wrong, because it works only when
its accessed with visit_type_str(), i.e. the property is actually of
string type.

Example:

    $ qemu-system-x86_64 -nodefaults -S -display none -M q35,usb=on -device 
'{"driver": "usb-mouse", "id": "ms0"}' -set device.ms0.serial=123

    $ qemu-system-x86_64 -nodefaults -S -display none -M q35,usb=on -device 
'{"driver": "usb-mouse", "id": "ms0"}' -set device.ms0.msos-desc=off
    qemu-system-x86_64: -device {"driver": "usb-mouse", "id": "ms0"}: Invalid 
parameter type for 'msos-desc', expected: boolean

Your patch stores a boolean if possible, else a number if possible, else
a string.  This is differently wrong.

Example:

    $ qemu-system-x86_64 -nodefaults -S -display none -M q35,usb=on -device 
'{"driver": "usb-mouse", "id": "ms0"}'

Example:

    $ qemu-system-x86_64 -nodefaults -S -display none -M q35,usb=on -device 
'{"driver": "usb-mouse", "id": "ms0"}' -set device.ms0.serial=123
    qemu-system-x86_64: -device {"driver": "usb-mouse", "id": "ms0", "serial": 
"123"}: Invalid parameter type for 'serial', expected: string

I can't see how -set can store the right thing.

Aside: the error messages refer to -device instead of -set.  Known bug
in -set, hard to fix.

> diff --git a/util/qemu-option.c b/util/qemu-option.c
> index eedd08929b..b2427cba9f 100644
> --- a/util/qemu-option.c
> +++ b/util/qemu-option.c
> @@ -88,7 +88,7 @@ const char *get_opt_value(const char *p, char **value)
>      return offset;
>  }
>  
> -static bool parse_option_number(const char *name, const char *value,
> +bool parse_option_number(const char *name, const char *value,
>                                  uint64_t *ret, Error **errp)
>  {
>      uint64_t number;


Reply via email to