qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing
@ 2021-01-18 16:30 Paolo Bonzini
  2021-01-18 16:30 ` [PATCH 01/25] qemu-option: clean up id vs. list->merge_lists Paolo Bonzini
                   ` (25 more replies)
  0 siblings, 26 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

This series switches -object, -M and -accel from QemuOpts to keyval.
Monitor commands device_add and netdev_add are also switched to keyval,
though -device and -netdev for now are not.

Along the way, the syntax of keyval and QemuOpts becomes more consistent
and support for keyval-based options is added to -readconfig.  -writeconfig
instead is removed (see patch 13 for rationale).

The reason to do this is:

- to make qemu-io, qemu-nbd, qemu-img and QEMU's parsing of -object
  consistent with qemu-storage-daemon's

- to allow using compound properties in -object, -M and -accel

Patch 1-3: make QemuOpts parsing a bit more restrictive, warning for
short-form boolean options and removing weird ways to request help
such as "help=foo" or "no?".

Patch 4-10: let keyval accept escaped commas in implied options,
switch comma-separated syntax for HMP from QemuOpts to keyval,
add help support to object_add

Patch 11-16: plumbing for reading keyval-based options in vl.c,
including -set and -readconfig.  The patches up to this point are
the ones that I'd like to get a relatively fast review, since the
remaining nine are more "boring".

Patch 17-21: switch -object to keyval everywhere

Patch 22-25: switch -M and -accel to keyval

Based-on: <20210118162537.779542-1-pbonzini@redhat.com>

Paolo Bonzini (25):
  qemu-option: clean up id vs. list->merge_lists
  qemu-option: move help handling to get_opt_name_value
  qemu-option: warn for short-form boolean options
  keyval: accept escaped commas in implied option
  keyval: simplify keyval_parse_one
  tests: convert check-qom-proplist to keyval
  keyval: introduce keyval_parse_into
  hmp: replace "O" parser with keyval
  qom: use qemu_printf to print help for user-creatable objects
  hmp: special case help options for object_add
  remove -writeconfig
  qemu-config: add error propagation to qemu_config_parse
  qemu-option: support accept-any QemuOptsList in qemu_opts_absorb_qdict
  qemu-config: parse configuration files to a QDict
  vl: plumb keyval-based options into -set and -readconfig
  qom: do not modify QDict argument in user_creatable_add_dict
  qemu-io: use keyval for -object parsing
  qemu-nbd: use keyval for -object parsing
  qemu-img: use keyval for -object parsing
  qemu: use keyval for -object parsing
  storage-daemon: do not register the "object" group with QemuOpts
  qom: export more functions for use with non-UserCreatable objects
  vl: switch -M parsing to keyval
  qemu-option: remove now-dead code
  vl: switch -accel parsing to keyval

 accel/accel.c                        |   6 +
 block/blkdebug.c                     |   3 +-
 docs/system/deprecated.rst           |   6 +
 hmp-commands.hx                      |   6 +-
 include/block/qdict.h                |   2 -
 include/qapi/qmp/qdict.h             |   3 +
 include/qemu/config-file.h           |   9 +-
 include/qemu/help_option.h           |  11 -
 include/qemu/option.h                |   6 +-
 include/qom/object.h                 |  21 +
 include/qom/object_interfaces.h      |  68 +--
 include/sysemu/accel.h               |   1 +
 monitor/hmp-cmds.c                   |  22 +-
 monitor/hmp.c                        |  20 +-
 qemu-img.c                           | 258 +++-------
 qemu-io.c                            |  42 +-
 qemu-nbd.c                           |  42 +-
 qemu-options.hx                      |  13 +-
 qom/object_interfaces.c              | 152 ++----
 softmmu/vl.c                         | 673 ++++++++++++++-------------
 storage-daemon/qemu-storage-daemon.c |  10 -
 tests/check-qom-proplist.c           |  58 ++-
 tests/test-keyval.c                  |  58 ++-
 tests/test-qemu-opts.c               |  37 +-
 util/keyval.c                        | 231 +++++----
 util/qemu-config.c                   | 141 +++---
 util/qemu-option.c                   | 126 +++--
 27 files changed, 918 insertions(+), 1107 deletions(-)

-- 
2.26.2



^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH 01/25] qemu-option: clean up id vs. list->merge_lists
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
@ 2021-01-18 16:30 ` Paolo Bonzini
  2021-01-19 12:33   ` Kevin Wolf
  2021-01-19 13:58   ` Markus Armbruster
  2021-01-18 16:30 ` [PATCH 02/25] qemu-option: move help handling to get_opt_name_value Paolo Bonzini
                   ` (24 subsequent siblings)
  25 siblings, 2 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Looking at all merge-lists QemuOptsList, here is how they access their
QemuOpts:

reopen_opts in qemu-io-cmds.c ("qemu-img reopen -o")
	qemu_opts_find(&reopen_opts, NULL)

empty_opts in qemu-io.c ("qemu-io open -o")
	qemu_opts_find(&empty_opts, NULL)

qemu_rtc_opts ("-rtc")
	qemu_find_opts_singleton("rtc")

qemu_machine_opts ("-M")
	qemu_find_opts_singleton("machine")

qemu_boot_opts ("-boot")
	QTAILQ_FIRST(&qemu_find_opts("bootopts")->head)

qemu_name_opts ("-name")
	qemu_opts_foreach->parse_name
	parse_name does not use id

qemu_mem_opts ("-m")
	qemu_find_opts_singleton("memory")

qemu_icount_opts ("-icount")
	qemu_opts_foreach->do_configuree_icount
	do_configure_icount->icount_configure
	icount_configure does not use id

qemu_smp_opts ("-smp")
	qemu_opts_find(qemu_find_opts("smp-opts"), NULL)

qemu_spice_opts ("-spice")
	QTAILQ_FIRST(&qemu_spice_opts.head)

i.e. they don't need an id.  Sometimes its presence is ignored
(e.g. when using qemu_opts_foreach), sometimes all the options
with the id are skipped, sometimes only the first option on the
command line is considered.  With this patch we just forbid id
on merge-lists QemuOptsLists; if the command line still works,
it has the same semantics as before.

qemu_opts_create's fail_if_exists parameter is now unnecessary:

- it is unused if id is NULL

- opts_parse only passes false if reached from qemu_opts_set_defaults,
in which case this patch enforces that id must be NULL

- other callers that can pass a non-NULL id always set it to true

Assert that it is true in the only case where "fail_if_exists" matters,
i.e. "id && !lists->merge_lists".  This means that if an id is present,
duplicates are always forbidden, which was already the status quo.

Discounting the case that aborts as it's not user-controlled (it's
"just" a matter of inspecting qemu_opts_create callers), the paths
through qemu_opts_create can be summarized as:

- merge_lists = true: singleton opts with NULL id; non-NULL id fails

- merge_lists = false: always return new opts; non-NULL id fails if dup

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 util/qemu-option.c | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/util/qemu-option.c b/util/qemu-option.c
index c88e159f18..91f4120ce1 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -619,7 +619,17 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
 {
     QemuOpts *opts = NULL;
 
-    if (id) {
+    if (list->merge_lists) {
+        if (id) {
+            error_setg(errp, QERR_INVALID_PARAMETER, "id");
+            return NULL;
+        }
+        opts = qemu_opts_find(list, NULL);
+        if (opts) {
+            return opts;
+        }
+    } else if (id) {
+        assert(fail_if_exists);
         if (!id_wellformed(id)) {
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id",
                        "an identifier");
@@ -629,17 +639,8 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
         }
         opts = qemu_opts_find(list, id);
         if (opts != NULL) {
-            if (fail_if_exists && !list->merge_lists) {
-                error_setg(errp, "Duplicate ID '%s' for %s", id, list->name);
-                return NULL;
-            } else {
-                return opts;
-            }
-        }
-    } else if (list->merge_lists) {
-        opts = qemu_opts_find(list, NULL);
-        if (opts) {
-            return opts;
+            error_setg(errp, "Duplicate ID '%s' for %s", id, list->name);
+            return NULL;
         }
     }
     opts = g_malloc0(sizeof(*opts));
@@ -893,7 +894,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
      * (if unlikely) future misuse:
      */
     assert(!defaults || list->merge_lists);
-    opts = qemu_opts_create(list, id, !defaults, errp);
+    opts = qemu_opts_create(list, id, !list->merge_lists, errp);
     g_free(id);
     if (opts == NULL) {
         return NULL;
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 02/25] qemu-option: move help handling to get_opt_name_value
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
  2021-01-18 16:30 ` [PATCH 01/25] qemu-option: clean up id vs. list->merge_lists Paolo Bonzini
@ 2021-01-18 16:30 ` Paolo Bonzini
  2021-01-19 15:10   ` Markus Armbruster
  2021-01-18 16:30 ` [PATCH 03/25] qemu-option: warn for short-form boolean options Paolo Bonzini
                   ` (23 subsequent siblings)
  25 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Right now, help options are parsed normally and then checked
specially in opt_validate, but only if coming from
qemu_opts_parse_noisily.  has_help_option does the check on its own.

opt_validate() has two callers: qemu_opt_set(), which passes null and is
therefore unaffected, and opts_do_parse(), which is affected.

opts_do_parse() is called by qemu_opts_do_parse(), which passes null and
is therefore unaffected, and opts_parse().

opts_parse() is called by qemu_opts_parse() and qemu_opts_set_defaults(),
which pass null and are therefore unaffected, and
qemu_opts_parse_noisily().

Move the check from opt_validate to the parsing workhorse of QemuOpts,
get_opt_name_value.  This will come in handy in the next patch, which
will raise a warning for "-object memory-backend-ram,share" ("flag" option
with no =on/=off part) but not for "-object memory-backend-ram,help".

As a result:

- opts_parse and opts_do_parse do not return an error anymore
  when help is requested; qemu_opts_parse_noisily does not have
  to work around that anymore.

- various crazy ways to request help are not recognized anymore:
  - "help=..."
  - "nohelp" (sugar for "help=off")
  - "?=..."
  - "no?" (sugar for "?=off")

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 util/qemu-option.c | 38 +++++++++++++++++++++++---------------
 1 file changed, 23 insertions(+), 15 deletions(-)

diff --git a/util/qemu-option.c b/util/qemu-option.c
index 91f4120ce1..5f27d4369d 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -496,8 +496,7 @@ static QemuOpt *opt_create(QemuOpts *opts, const char *name, char *value,
     return opt;
 }
 
-static bool opt_validate(QemuOpt *opt, bool *help_wanted,
-                         Error **errp)
+static bool opt_validate(QemuOpt *opt, Error **errp)
 {
     const QemuOptDesc *desc;
     const QemuOptsList *list = opt->opts->list;
@@ -505,9 +504,6 @@ static bool opt_validate(QemuOpt *opt, bool *help_wanted,
     desc = find_desc_by_name(list->desc, opt->name);
     if (!desc && !opts_accepts_any(list)) {
         error_setg(errp, QERR_INVALID_PARAMETER, opt->name);
-        if (help_wanted && is_help_option(opt->name)) {
-            *help_wanted = true;
-        }
         return false;
     }
 
@@ -524,7 +520,7 @@ bool qemu_opt_set(QemuOpts *opts, const char *name, const char *value,
 {
     QemuOpt *opt = opt_create(opts, name, g_strdup(value), false);
 
-    if (!opt_validate(opt, NULL, errp)) {
+    if (!opt_validate(opt, errp)) {
         qemu_opt_del(opt);
         return false;
     }
@@ -760,10 +756,12 @@ void qemu_opts_print(QemuOpts *opts, const char *separator)
 
 static const char *get_opt_name_value(const char *params,
                                       const char *firstname,
+                                      bool *help_wanted,
                                       char **name, char **value)
 {
     const char *p;
     size_t len;
+    bool is_help = false;
 
     len = strcspn(params, "=,");
     if (params[len] != '=') {
@@ -780,6 +778,7 @@ static const char *get_opt_name_value(const char *params,
                 *value = g_strdup("off");
             } else {
                 *value = g_strdup("on");
+                is_help = is_help_option(*name);
             }
         }
     } else {
@@ -791,6 +790,9 @@ static const char *get_opt_name_value(const char *params,
     }
 
     assert(!*p || *p == ',');
+    if (help_wanted && is_help) {
+        *help_wanted = true;
+    }
     if (*p == ',') {
         p++;
     }
@@ -806,7 +808,12 @@ static bool opts_do_parse(QemuOpts *opts, const char *params,
     QemuOpt *opt;
 
     for (p = params; *p;) {
-        p = get_opt_name_value(p, firstname, &option, &value);
+        p = get_opt_name_value(p, firstname, help_wanted, &option, &value);
+        if (help_wanted && *help_wanted) {
+            g_free(option);
+            g_free(value);
+            return false;
+        }
         firstname = NULL;
 
         if (!strcmp(option, "id")) {
@@ -817,7 +824,7 @@ static bool opts_do_parse(QemuOpts *opts, const char *params,
 
         opt = opt_create(opts, option, value, prepend);
         g_free(option);
-        if (!opt_validate(opt, help_wanted, errp)) {
+        if (!opt_validate(opt, errp)) {
             qemu_opt_del(opt);
             return false;
         }
@@ -832,7 +839,7 @@ static char *opts_parse_id(const char *params)
     char *name, *value;
 
     for (p = params; *p;) {
-        p = get_opt_name_value(p, NULL, &name, &value);
+        p = get_opt_name_value(p, NULL, NULL, &name, &value);
         if (!strcmp(name, "id")) {
             g_free(name);
             return value;
@@ -848,11 +855,10 @@ bool has_help_option(const char *params)
 {
     const char *p;
     char *name, *value;
-    bool ret;
+    bool ret = false;
 
     for (p = params; *p;) {
-        p = get_opt_name_value(p, NULL, &name, &value);
-        ret = is_help_option(name);
+        p = get_opt_name_value(p, NULL, &ret, &name, &value);
         g_free(name);
         g_free(value);
         if (ret) {
@@ -937,11 +943,13 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
     QemuOpts *opts;
     bool help_wanted = false;
 
-    opts = opts_parse(list, params, permit_abbrev, false, &help_wanted, &err);
-    if (err) {
+    opts = opts_parse(list, params, permit_abbrev, false,
+                      opts_accepts_any(list) ? NULL : &help_wanted,
+                      &err);
+    if (!opts) {
+        assert(!!err + !!help_wanted == 1);
         if (help_wanted) {
             qemu_opts_print_help(list, true);
-            error_free(err);
         } else {
             error_report_err(err);
         }
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 03/25] qemu-option: warn for short-form boolean options
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
  2021-01-18 16:30 ` [PATCH 01/25] qemu-option: clean up id vs. list->merge_lists Paolo Bonzini
  2021-01-18 16:30 ` [PATCH 02/25] qemu-option: move help handling to get_opt_name_value Paolo Bonzini
@ 2021-01-18 16:30 ` Paolo Bonzini
  2021-01-19 15:56   ` Markus Armbruster
  2021-01-18 16:30 ` [PATCH 04/25] keyval: accept escaped commas in implied option Paolo Bonzini
                   ` (22 subsequent siblings)
  25 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Options such as "server" or "nowait", that are commonly found in -chardev,
are sugar for "server=on" and "wait=off".  This is quite surprising and
also does not have any notion of typing attached.  It is even possible to
do "-device e1000,noid" and get a device with "id=off".

Deprecate it and print a warning when it is encountered.  In general,
this short form for boolean options only seems to be in wide use for
-chardev and -spice.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 docs/system/deprecated.rst |  6 ++++++
 tests/test-qemu-opts.c     |  2 +-
 util/qemu-option.c         | 29 ++++++++++++++++++-----------
 3 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst
index e20bfcb17a..e71faefbe5 100644
--- a/docs/system/deprecated.rst
+++ b/docs/system/deprecated.rst
@@ -127,6 +127,12 @@ Drives with interface types other than ``if=none`` are for onboard
 devices.  It is possible to use drives the board doesn't pick up with
 -device.  This usage is now deprecated.  Use ``if=none`` instead.
 
+Short-form boolean options (since 5.2)
+''''''''''''''''''''''''''''''''''''''
+
+Boolean options such as ``share=on``/``share=off`` can be written
+in short form as ``share`` and ``noshare``.  This is deprecated
+and will cause a warning.
 
 QEMU Machine Protocol (QMP) commands
 ------------------------------------
diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c
index 2aab831d10..8bbb17b1c7 100644
--- a/tests/test-qemu-opts.c
+++ b/tests/test-qemu-opts.c
@@ -515,7 +515,7 @@ static void test_opts_parse(void)
     error_free_or_abort(&err);
     g_assert(!opts);
 
-    /* Implied value */
+    /* Implied value (qemu_opts_parse warns but accepts it) */
     opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=",
                            false, &error_abort);
     g_assert_cmpuint(opts_count(opts), ==, 3);
diff --git a/util/qemu-option.c b/util/qemu-option.c
index 5f27d4369d..40564a12eb 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -756,10 +756,12 @@ void qemu_opts_print(QemuOpts *opts, const char *separator)
 
 static const char *get_opt_name_value(const char *params,
                                       const char *firstname,
+                                      bool warn_on_flag,
                                       bool *help_wanted,
                                       char **name, char **value)
 {
     const char *p;
+    const char *prefix = "";
     size_t len;
     bool is_help = false;
 
@@ -776,10 +778,15 @@ static const char *get_opt_name_value(const char *params,
             if (strncmp(*name, "no", 2) == 0) {
                 memmove(*name, *name + 2, strlen(*name + 2) + 1);
                 *value = g_strdup("off");
+                prefix = "no";
             } else {
                 *value = g_strdup("on");
                 is_help = is_help_option(*name);
             }
+            if (!is_help && warn_on_flag) {
+                warn_report("short-form boolean option '%s%s' deprecated", prefix, *name);
+                error_printf("Please use %s=%s instead\n", *name, *value);
+            }
         }
     } else {
         /* found "foo=bar,more" */
@@ -801,14 +808,14 @@ static const char *get_opt_name_value(const char *params,
 
 static bool opts_do_parse(QemuOpts *opts, const char *params,
                           const char *firstname, bool prepend,
-                          bool *help_wanted, Error **errp)
+                          bool warn_on_flag, bool *help_wanted, Error **errp)
 {
     char *option, *value;
     const char *p;
     QemuOpt *opt;
 
     for (p = params; *p;) {
-        p = get_opt_name_value(p, firstname, help_wanted, &option, &value);
+        p = get_opt_name_value(p, firstname, warn_on_flag, help_wanted, &option, &value);
         if (help_wanted && *help_wanted) {
             g_free(option);
             g_free(value);
@@ -839,7 +846,7 @@ static char *opts_parse_id(const char *params)
     char *name, *value;
 
     for (p = params; *p;) {
-        p = get_opt_name_value(p, NULL, NULL, &name, &value);
+        p = get_opt_name_value(p, NULL, false, NULL, &name, &value);
         if (!strcmp(name, "id")) {
             g_free(name);
             return value;
@@ -858,7 +865,7 @@ bool has_help_option(const char *params)
     bool ret = false;
 
     for (p = params; *p;) {
-        p = get_opt_name_value(p, NULL, &ret, &name, &value);
+        p = get_opt_name_value(p, NULL, false, &ret, &name, &value);
         g_free(name);
         g_free(value);
         if (ret) {
@@ -878,12 +885,12 @@ bool has_help_option(const char *params)
 bool qemu_opts_do_parse(QemuOpts *opts, const char *params,
                        const char *firstname, Error **errp)
 {
-    return opts_do_parse(opts, params, firstname, false, NULL, errp);
+    return opts_do_parse(opts, params, firstname, false, false, NULL, errp);
 }
 
 static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
                             bool permit_abbrev, bool defaults,
-                            bool *help_wanted, Error **errp)
+                            bool warn_on_flag, bool *help_wanted, Error **errp)
 {
     const char *firstname;
     char *id = opts_parse_id(params);
@@ -906,8 +913,8 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
         return NULL;
     }
 
-    if (!opts_do_parse(opts, params, firstname, defaults, help_wanted,
-                       errp)) {
+    if (!opts_do_parse(opts, params, firstname, defaults,
+                       warn_on_flag, help_wanted, errp)) {
         qemu_opts_del(opts);
         return NULL;
     }
@@ -925,7 +932,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
 QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params,
                           bool permit_abbrev, Error **errp)
 {
-    return opts_parse(list, params, permit_abbrev, false, NULL, errp);
+    return opts_parse(list, params, permit_abbrev, false, false, NULL, errp);
 }
 
 /**
@@ -943,7 +950,7 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
     QemuOpts *opts;
     bool help_wanted = false;
 
-    opts = opts_parse(list, params, permit_abbrev, false,
+    opts = opts_parse(list, params, permit_abbrev, false, true,
                       opts_accepts_any(list) ? NULL : &help_wanted,
                       &err);
     if (!opts) {
@@ -962,7 +969,7 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
 {
     QemuOpts *opts;
 
-    opts = opts_parse(list, params, permit_abbrev, true, NULL, NULL);
+    opts = opts_parse(list, params, permit_abbrev, true, false, NULL, NULL);
     assert(opts);
 }
 
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 04/25] keyval: accept escaped commas in implied option
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (2 preceding siblings ...)
  2021-01-18 16:30 ` [PATCH 03/25] qemu-option: warn for short-form boolean options Paolo Bonzini
@ 2021-01-18 16:30 ` Paolo Bonzini
  2021-01-21 12:58   ` Markus Armbruster
  2021-01-22  8:39   ` Markus Armbruster
  2021-01-18 16:30 ` [PATCH 05/25] keyval: simplify keyval_parse_one Paolo Bonzini
                   ` (21 subsequent siblings)
  25 siblings, 2 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

This is used with the weirdly-named device "SUNFD,fdtwo":

  $ qemu-system-sparc -device SUNW,,fdtwo,help
  SUNW,fdtwo options:
    drive=<str>            - Node name or ID of a block device to use as a backend
    fallback=<FdcDriveType> - FDC drive type, 144/288/120/none/auto (default: "144")
    ...

Therefore, accepting it is a preparatory step towards keyval-ifying
-device and the device_add monitor command.  In general, however, this
unexpected wart of the keyval syntax leads to suboptimal errors compared
to QemuOpts:

  $ ./qemu-system-x86_64 -object foo,,bar,id=obj
  qemu-system-x86_64: -object foo,,bar,id=obj: invalid object type: foo,bar
  $ storage-daemon/qemu-storage-daemon --object foo,,bar,id=obj
  qemu-storage-daemon: Invalid parameter ''

To implement this, the flow of the parser is changed to first unescape
everything up to the next comma or equal sign.  This is done in a
new function keyval_fetch_string for both the key and value part.
Keys therefore are now parsed in unescaped form, but this makes no
difference in practice because a comma is an invalid character for a
QAPI name.  Thus keys with a comma in them are rejected anyway, as
demonstrated by the new testcase.

As a side effect of the new code, parse errors are slightly improved as
well: "Invalid parameter ''" becomes "Expected parameter before '='"
when keyval is fed a string starting with an equal sign.

The slightly baroque interface of keyval_fetch_string lets me keep the
key parsing loop mostly untouched.  It is simplified in the next patch,
however.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/qemu/help_option.h |  11 ---
 tests/test-keyval.c        |  21 ++++--
 util/keyval.c              | 145 ++++++++++++++++++++-----------------
 3 files changed, 92 insertions(+), 85 deletions(-)

diff --git a/include/qemu/help_option.h b/include/qemu/help_option.h
index ca6389a154..328d2a89fd 100644
--- a/include/qemu/help_option.h
+++ b/include/qemu/help_option.h
@@ -19,15 +19,4 @@ static inline bool is_help_option(const char *s)
     return !strcmp(s, "?") || !strcmp(s, "help");
 }
 
-static inline int starts_with_help_option(const char *s)
-{
-    if (*s == '?') {
-        return 1;
-    }
-    if (g_str_has_prefix(s, "help")) {
-        return 4;
-    }
-    return 0;
-}
-
 #endif
diff --git a/tests/test-keyval.c b/tests/test-keyval.c
index ee927fe4e4..19f664f535 100644
--- a/tests/test-keyval.c
+++ b/tests/test-keyval.c
@@ -89,6 +89,11 @@ static void test_keyval_parse(void)
     error_free_or_abort(&err);
     g_assert(!qdict);
 
+    /* Keys must be QAPI identifiers */
+    qdict = keyval_parse("weird,,=key", NULL, NULL, &err);
+    error_free_or_abort(&err);
+    g_assert(!qdict);
+
     /* Multiple keys, last one wins */
     qdict = keyval_parse("a=1,b=2,,x,a=3", NULL, NULL, &error_abort);
     g_assert_cmpuint(qdict_size(qdict), ==, 2);
@@ -178,15 +183,15 @@ static void test_keyval_parse(void)
     error_free_or_abort(&err);
     g_assert(!qdict);
 
-    /* Likewise (qemu_opts_parse(): implied key with comma value) */
-    qdict = keyval_parse(",,,a=1", "implied", NULL, &err);
-    error_free_or_abort(&err);
-    g_assert(!qdict);
+    /* Implied key's value can have a comma */
+    qdict = keyval_parse(",,,a=1", "implied", NULL, &error_abort);
+    g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, ",");
+    g_assert_cmpstr(qdict_get_try_str(qdict, "a"), ==, "1");
+    qobject_unref(qdict);
 
-    /* Implied key's value can't have comma (qemu_opts_parse(): it can) */
-    qdict = keyval_parse("val,,ue", "implied", NULL, &err);
-    error_free_or_abort(&err);
-    g_assert(!qdict);
+    qdict = keyval_parse("val,,ue", "implied", NULL, &error_abort);
+    g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "val,ue");
+    qobject_unref(qdict);
 
     /* Empty key is not an implied key */
     qdict = keyval_parse("=val", "implied", NULL, &err);
diff --git a/util/keyval.c b/util/keyval.c
index be34928813..eb9b9c55ec 100644
--- a/util/keyval.c
+++ b/util/keyval.c
@@ -16,8 +16,8 @@
  *   key-vals     = [ key-val { ',' key-val } [ ',' ] ]
  *   key-val      = key '=' val | help
  *   key          = key-fragment { '.' key-fragment }
- *   key-fragment = / [^=,.]+ /
- *   val          = { / [^,]+ / | ',,' }
+ *   key-fragment = { / [^=,.] / | ',,' }
+ *   val          = { / [^,] / | ',,' }
  *   help         = 'help' | '?'
  *
  * Semantics defined by reduction to JSON:
@@ -78,13 +78,13 @@
  * Alternative syntax for use with an implied key:
  *
  *   key-vals     = [ key-val-1st { ',' key-val } [ ',' ] ]
- *   key-val-1st  = val-no-key | key-val
- *   val-no-key   = / [^=,]+ / - help
+ *   key-val-1st  = (val-no-key - help) | key-val
+ *   val-no-key   = { / [^=,] / | ',,' }
  *
  * where val-no-key is syntactic sugar for implied-key=val-no-key.
  *
- * Note that you can't use the sugared form when the value contains
- * '=' or ','.
+ * Note that you can't use the sugared form when the value is empty
+ * or contains '='.
  */
 
 #include "qemu/osdep.h"
@@ -141,7 +141,7 @@ static int key_to_index(const char *key, const char **end)
  * On failure, store an error through @errp and return NULL.
  */
 static QObject *keyval_parse_put(QDict *cur,
-                                 const char *key_in_cur, QString *value,
+                                 const char *key_in_cur, const char *value,
                                  const char *key, const char *key_cursor,
                                  Error **errp)
 {
@@ -152,20 +152,56 @@ static QObject *keyval_parse_put(QDict *cur,
         if (qobject_type(old) != (value ? QTYPE_QSTRING : QTYPE_QDICT)) {
             error_setg(errp, "Parameters '%.*s.*' used inconsistently",
                        (int)(key_cursor - key), key);
-            qobject_unref(value);
             return NULL;
         }
         if (!value) {
             return old;         /* already QDict, do nothing */
         }
-        new = QOBJECT(value);   /* replacement */
-    } else {
-        new = value ? QOBJECT(value) : QOBJECT(qdict_new());
     }
+    new = value ? QOBJECT(qstring_from_str(value)) : QOBJECT(qdict_new());
     qdict_put_obj(cur, key_in_cur, new);
     return new;
 }
 
+/*
+ * Parse and unescape the string (key or value) pointed to by @start,
+ * stopping at a single comma or if @key is true an equal sign.
+ * The string is unescaped and NUL-terminated in place.
+ *
+ * On return:
+ * - either NUL or the separator (comma or equal sign) is returned.
+ * - the length of the string is stored in @len.
+ * - @start is advanced to either the NUL or the first character past the
+ *   separator.
+ */
+static char keyval_fetch_string(char **start, size_t *len, bool key)
+{
+    char sep;
+    char *p, *unescaped;
+    p = unescaped = *start;
+    for (;;) {
+        sep = *p;
+        if (!sep) {
+            break;
+        }
+        if (key && sep == '=') {
+            ++p;
+            break;
+        }
+        if (sep == ',') {
+            if (*++p != ',') {
+                break;
+            }
+        }
+        *unescaped++ = *p++;
+    }
+
+    *unescaped = 0;
+    *len = unescaped - *start;
+    *start = p;
+    return sep;
+}
+
 /*
  * Parse one parameter from @params.
  *
@@ -179,35 +215,42 @@ static QObject *keyval_parse_put(QDict *cur,
  * On success, return a pointer to the next parameter, or else to '\0'.
  * On failure, return NULL.
  */
-static const char *keyval_parse_one(QDict *qdict, const char *params,
-                                    const char *implied_key, bool *help,
-                                    Error **errp)
+static char *keyval_parse_one(QDict *qdict, char *params,
+                              const char *implied_key, bool *help,
+                              Error **errp)
 {
-    const char *key, *key_end, *val_end, *s, *end;
+    const char *key, *key_end, *s, *end;
+    const char *val = NULL;
+    char sep;
     size_t len;
     char key_in_cur[128];
     QDict *cur;
     int ret;
     QObject *next;
-    GString *val;
 
     key = params;
-    val_end = NULL;
-    len = strcspn(params, "=,");
-    if (len && key[len] != '=') {
-        if (starts_with_help_option(key) == len) {
+    sep = keyval_fetch_string(&params, &len, true);
+    if (!len) {
+        if (sep) {
+            error_setg(errp, "Expected parameter before '%c%s'", sep, params);
+        } else {
+            error_setg(errp, "Expected parameter at end of string");
+        }
+        return NULL;
+    }
+    if (sep != '=') {
+        if (is_help_option(key)) {
             *help = true;
-            s = key + len;
-            if (*s == ',') {
-                s++;
-            }
-            return s;
+            return params;
         }
         if (implied_key) {
             /* Desugar implied key */
+            val = key;
             key = implied_key;
-            val_end = params + len;
             len = strlen(implied_key);
+        } else {
+            error_setg(errp, "No implicit parameter name for value '%s'", key);
+            return NULL;
         }
     }
     key_end = key + len;
@@ -218,7 +261,7 @@ static const char *keyval_parse_one(QDict *qdict, const char *params,
      */
     cur = qdict;
     s = key;
-    for (;;) {
+    do {
         /* Want a key index (unless it's first) or a QAPI name */
         if (s != key && key_to_index(s, &end) >= 0) {
             len = end - s;
@@ -254,47 +297,16 @@ static const char *keyval_parse_one(QDict *qdict, const char *params,
         memcpy(key_in_cur, s, len);
         key_in_cur[len] = 0;
         s += len;
+    } while (*s++ == '.');
 
-        if (*s != '.') {
-            break;
-        }
-        s++;
-    }
-
-    if (key == implied_key) {
-        assert(!*s);
-        val = g_string_new_len(params, val_end - params);
-        s = val_end;
-        if (*s == ',') {
-            s++;
-        }
-    } else {
-        if (*s != '=') {
-            error_setg(errp, "Expected '=' after parameter '%.*s'",
-                       (int)(s - key), key);
-            return NULL;
-        }
-        s++;
-
-        val = g_string_new(NULL);
-        for (;;) {
-            if (!*s) {
-                break;
-            } else if (*s == ',') {
-                s++;
-                if (*s != ',') {
-                    break;
-                }
-            }
-            g_string_append_c(val, *s++);
-        }
+    if (key != implied_key) {
+        val = params;
+        keyval_fetch_string(&params, &len, false);
     }
-
-    if (!keyval_parse_put(cur, key_in_cur, qstring_from_gstring(val),
-                          key, key_end, errp)) {
+    if (!keyval_parse_put(cur, key_in_cur, val, key, key_end, errp)) {
         return NULL;
     }
-    return s;
+    return params;
 }
 
 static char *reassemble_key(GSList *key)
@@ -439,10 +451,11 @@ QDict *keyval_parse(const char *params, const char *implied_key,
 {
     QDict *qdict = qdict_new();
     QObject *listified;
-    const char *s;
+    g_autofree char *dup;
+    char *s;
     bool help = false;
 
-    s = params;
+    s = dup = g_strdup(params);
     while (*s) {
         s = keyval_parse_one(qdict, s, implied_key, &help, errp);
         if (!s) {
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 05/25] keyval: simplify keyval_parse_one
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (3 preceding siblings ...)
  2021-01-18 16:30 ` [PATCH 04/25] keyval: accept escaped commas in implied option Paolo Bonzini
@ 2021-01-18 16:30 ` Paolo Bonzini
  2021-01-22 13:48   ` Markus Armbruster
  2021-01-18 16:30 ` [PATCH 06/25] tests: convert check-qom-proplist to keyval Paolo Bonzini
                   ` (20 subsequent siblings)
  25 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Now that the key is NULL terminated, we can remove some of the contortions
that were done to operate on possibly '='-terminated strings in
keyval_parse_one.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 util/keyval.c | 27 ++++++++++-----------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/util/keyval.c b/util/keyval.c
index eb9b9c55ec..e7f708cd1e 100644
--- a/util/keyval.c
+++ b/util/keyval.c
@@ -170,11 +170,10 @@ static QObject *keyval_parse_put(QDict *cur,
  *
  * On return:
  * - either NUL or the separator (comma or equal sign) is returned.
- * - the length of the string is stored in @len.
  * - @start is advanced to either the NUL or the first character past the
  *   separator.
  */
-static char keyval_fetch_string(char **start, size_t *len, bool key)
+static char keyval_fetch_string(char **start, bool key)
 {
     char sep;
     char *p, *unescaped;
@@ -197,7 +196,6 @@ static char keyval_fetch_string(char **start, size_t *len, bool key)
     }
 
     *unescaped = 0;
-    *len = unescaped - *start;
     *start = p;
     return sep;
 }
@@ -219,7 +217,7 @@ static char *keyval_parse_one(QDict *qdict, char *params,
                               const char *implied_key, bool *help,
                               Error **errp)
 {
-    const char *key, *key_end, *s, *end;
+    const char *key, *s, *end;
     const char *val = NULL;
     char sep;
     size_t len;
@@ -229,8 +227,8 @@ static char *keyval_parse_one(QDict *qdict, char *params,
     QObject *next;
 
     key = params;
-    sep = keyval_fetch_string(&params, &len, true);
-    if (!len) {
+    sep = keyval_fetch_string(&params, true);
+    if (!*key) {
         if (sep) {
             error_setg(errp, "Expected parameter before '%c%s'", sep, params);
         } else {
@@ -247,13 +245,11 @@ static char *keyval_parse_one(QDict *qdict, char *params,
             /* Desugar implied key */
             val = key;
             key = implied_key;
-            len = strlen(implied_key);
         } else {
             error_setg(errp, "No implicit parameter name for value '%s'", key);
             return NULL;
         }
     }
-    key_end = key + len;
 
     /*
      * Loop over key fragments: @s points to current fragment, it
@@ -269,24 +265,21 @@ static char *keyval_parse_one(QDict *qdict, char *params,
             ret = parse_qapi_name(s, false);
             len = ret < 0 ? 0 : ret;
         }
-        assert(s + len <= key_end);
-        if (!len || (s + len < key_end && s[len] != '.')) {
+        if (!len || (s[len] != '\0' && s[len] != '.')) {
             assert(key != implied_key);
-            error_setg(errp, "Invalid parameter '%.*s'",
-                       (int)(key_end - key), key);
+            error_setg(errp, "Invalid parameter '%s'", key);
             return NULL;
         }
         if (len >= sizeof(key_in_cur)) {
             assert(key != implied_key);
             error_setg(errp, "Parameter%s '%.*s' is too long",
-                       s != key || s + len != key_end ? " fragment" : "",
+                       s != key || s[len] == '.' ? " fragment" : "",
                        (int)len, s);
             return NULL;
         }
 
         if (s != key) {
-            next = keyval_parse_put(cur, key_in_cur, NULL,
-                                    key, s - 1, errp);
+            next = keyval_parse_put(cur, key_in_cur, NULL, key, s - 1, errp);
             if (!next) {
                 return NULL;
             }
@@ -301,9 +294,9 @@ static char *keyval_parse_one(QDict *qdict, char *params,
 
     if (key != implied_key) {
         val = params;
-        keyval_fetch_string(&params, &len, false);
+        keyval_fetch_string(&params, false);
     }
-    if (!keyval_parse_put(cur, key_in_cur, val, key, key_end, errp)) {
+    if (!keyval_parse_put(cur, key_in_cur, val, key, s - 1, errp)) {
         return NULL;
     }
     return params;
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 06/25] tests: convert check-qom-proplist to keyval
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (4 preceding siblings ...)
  2021-01-18 16:30 ` [PATCH 05/25] keyval: simplify keyval_parse_one Paolo Bonzini
@ 2021-01-18 16:30 ` Paolo Bonzini
  2021-01-22 14:14   ` Markus Armbruster
  2021-01-18 16:30 ` [PATCH 07/25] keyval: introduce keyval_parse_into Paolo Bonzini
                   ` (19 subsequent siblings)
  25 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

The command-line creation test is using QemuOpts.  Switch it to keyval,
since all the -object command line options will follow
qemu-storage-daemon and do the same.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 tests/check-qom-proplist.c | 58 +++++++++++++++++++++++++-------------
 1 file changed, 38 insertions(+), 20 deletions(-)

diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c
index 1b76581980..8dba26fb3c 100644
--- a/tests/check-qom-proplist.c
+++ b/tests/check-qom-proplist.c
@@ -21,6 +21,8 @@
 #include "qemu/osdep.h"
 
 #include "qapi/error.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qobject.h"
 #include "qom/object.h"
 #include "qemu/module.h"
 #include "qemu/option.h"
@@ -400,42 +402,58 @@ static void test_dummy_createlist(void)
 
 static void test_dummy_createcmdl(void)
 {
-    QemuOpts *opts;
+    QDict *qdict;
     DummyObject *dobj;
     Error *err = NULL;
+    bool help;
     const char *params = TYPE_DUMMY \
                          ",id=dev0," \
                          "bv=yes,sv=Hiss hiss hiss,av=platypus";
 
-    qemu_add_opts(&qemu_object_opts);
-    opts = qemu_opts_parse(&qemu_object_opts, params, true, &err);
+    qdict = keyval_parse(params, "qom-type", &help, &err);
     g_assert(err == NULL);
-    g_assert(opts);
+    g_assert(qdict);
+    g_assert(!help);
 
-    dobj = DUMMY_OBJECT(user_creatable_add_opts(opts, &err));
+    g_assert(user_creatable_add_dict(qdict, true, &err));
     g_assert(err == NULL);
+    qobject_unref(qdict);
+
+    dobj = DUMMY_OBJECT(object_resolve_path_component(object_get_objects_root(),
+                                                      "dev0"));
     g_assert(dobj);
     g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
     g_assert(dobj->bv == true);
     g_assert(dobj->av == DUMMY_PLATYPUS);
 
+    qdict = keyval_parse(params, "qom-type", &help, &err);
+    g_assert(!user_creatable_add_dict(qdict, true, &err));
+    g_assert(err);
+    g_assert(object_resolve_path_component(object_get_objects_root(), "dev0")
+             == OBJECT(dobj));
+    qobject_unref(qdict);
+    error_free(err);
+    err = NULL;
+
+    qdict = keyval_parse(params, "qom-type", &help, &err);
     user_creatable_del("dev0", &error_abort);
+    g_assert(object_resolve_path_component(object_get_objects_root(), "dev0")
+             == NULL);
 
-    object_unref(OBJECT(dobj));
-
-    /*
-     * cmdline-parsing via qemu_opts_parse() results in a QemuOpts entry
-     * corresponding to the Object's ID to be added to the QemuOptsList
-     * for objects. To avoid having this entry conflict with future
-     * Objects using the same ID (which can happen in cases where
-     * qemu_opts_parse() is used to parse the object params, such as
-     * with hmp_object_add() at the time of this comment), we need to
-     * check for this in user_creatable_del() and remove the QemuOpts if
-     * it is present.
-     *
-     * The below check ensures this works as expected.
-     */
-    g_assert_null(qemu_opts_find(&qemu_object_opts, "dev0"));
+    g_assert(user_creatable_add_dict(qdict, true, &err));
+    g_assert(err == NULL);
+    qobject_unref(qdict);
+
+    dobj = DUMMY_OBJECT(object_resolve_path_component(object_get_objects_root(),
+                                                      "dev0"));
+    g_assert(dobj);
+    g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
+    g_assert(dobj->bv == true);
+    g_assert(dobj->av == DUMMY_PLATYPUS);
+    g_assert(object_resolve_path_component(object_get_objects_root(), "dev0")
+             == OBJECT(dobj));
+
+    object_unparent(OBJECT(dobj));
 }
 
 static void test_dummy_badenum(void)
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 07/25] keyval: introduce keyval_parse_into
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (5 preceding siblings ...)
  2021-01-18 16:30 ` [PATCH 06/25] tests: convert check-qom-proplist to keyval Paolo Bonzini
@ 2021-01-18 16:30 ` Paolo Bonzini
  2021-01-22 14:22   ` Markus Armbruster
  2021-01-18 16:30 ` [PATCH 08/25] hmp: replace "O" parser with keyval Paolo Bonzini
                   ` (18 subsequent siblings)
  25 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Allow parsing multiple keyval sequences into the same dictionary.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/qemu/option.h |  2 ++
 util/keyval.c         | 39 ++++++++++++++++++++++++++++++++-------
 2 files changed, 34 insertions(+), 7 deletions(-)

diff --git a/include/qemu/option.h b/include/qemu/option.h
index f73e0dc7d9..092e291c37 100644
--- a/include/qemu/option.h
+++ b/include/qemu/option.h
@@ -147,6 +147,8 @@ void qemu_opts_print_help(QemuOptsList *list, bool print_caption);
 void qemu_opts_free(QemuOptsList *list);
 QemuOptsList *qemu_opts_append(QemuOptsList *dst, QemuOptsList *list);
 
+QDict *keyval_parse_into(QDict *qdict, const char *params, const char *implied_key,
+                         bool *p_help, Error **errp);
 QDict *keyval_parse(const char *params, const char *implied_key,
                     bool *help, Error **errp);
 
diff --git a/util/keyval.c b/util/keyval.c
index e7f708cd1e..1d4ca12129 100644
--- a/util/keyval.c
+++ b/util/keyval.c
@@ -436,13 +436,12 @@ static QObject *keyval_listify(QDict *cur, GSList *key_of_cur, Error **errp)
  * If @p_help is not NULL, store whether help is requested there.
  * If @p_help is NULL and help is requested, fail.
  *
- * On success, return a dictionary of the parsed keys and values.
+ * On success, return @dict, now filled with the parsed keys and values.
  * On failure, store an error through @errp and return NULL.
  */
-QDict *keyval_parse(const char *params, const char *implied_key,
-                    bool *p_help, Error **errp)
+QDict *keyval_parse_into(QDict *qdict, const char *params, const char *implied_key,
+                         bool *p_help, Error **errp)
 {
-    QDict *qdict = qdict_new();
     QObject *listified;
     g_autofree char *dup;
     char *s;
@@ -452,7 +451,6 @@ QDict *keyval_parse(const char *params, const char *implied_key,
     while (*s) {
         s = keyval_parse_one(qdict, s, implied_key, &help, errp);
         if (!s) {
-            qobject_unref(qdict);
             return NULL;
         }
         implied_key = NULL;
@@ -462,15 +460,42 @@ QDict *keyval_parse(const char *params, const char *implied_key,
         *p_help = help;
     } else if (help) {
         error_setg(errp, "Help is not available for this option");
-        qobject_unref(qdict);
         return NULL;
     }
 
     listified = keyval_listify(qdict, NULL, errp);
     if (!listified) {
-        qobject_unref(qdict);
         return NULL;
     }
     assert(listified == QOBJECT(qdict));
     return qdict;
 }
+
+/*
+ * Parse @params in QEMU's traditional KEY=VALUE,... syntax.
+ *
+ * If @implied_key, the first KEY= can be omitted.  @implied_key is
+ * implied then, and VALUE can't be empty or contain ',' or '='.
+ *
+ * A parameter "help" or "?" without a value isn't added to the
+ * resulting dictionary, but instead is interpreted as help request.
+ * All other options are parsed and returned normally so that context
+ * specific help can be printed.
+ *
+ * If @p_help is not NULL, store whether help is requested there.
+ * If @p_help is NULL and help is requested, fail.
+ *
+ * On success, return a dictionary of the parsed keys and values.
+ * On failure, store an error through @errp and return NULL.
+ */
+QDict *keyval_parse(const char *params, const char *implied_key,
+                    bool *p_help, Error **errp)
+{
+    QDict *qdict = qdict_new();
+    QDict *ret = keyval_parse_into(qdict, params, implied_key, p_help, errp);
+
+    if (!ret) {
+        qobject_unref(qdict);
+    }
+    return ret;
+}
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 08/25] hmp: replace "O" parser with keyval
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (6 preceding siblings ...)
  2021-01-18 16:30 ` [PATCH 07/25] keyval: introduce keyval_parse_into Paolo Bonzini
@ 2021-01-18 16:30 ` Paolo Bonzini
  2021-01-25  9:00   ` Markus Armbruster
  2021-01-18 16:30 ` [PATCH 09/25] qom: use qemu_printf to print help for user-creatable objects Paolo Bonzini
                   ` (17 subsequent siblings)
  25 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

HMP is using QemuOpts to parse free-form commands device_add,
netdev_add and object_add.  However, none of these need QemuOpts
for validation (these three QemuOptsLists are all of the catch-all
kind), and keyval is already able to parse into QDict.  So use
keyval directly, avoiding the detour from
string to QemuOpts to QDict.

The args_type now stores the implied key.  This arguably makes more
sense than storing the QemuOptsList name; at least, it _is_ a key
that might end up in the arguments QDict.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hmp-commands.hx |  6 +++---
 monitor/hmp.c   | 20 +++++++++-----------
 2 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/hmp-commands.hx b/hmp-commands.hx
index 73e0832ea1..6ee746b53e 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -669,7 +669,7 @@ ERST
 
     {
         .name       = "device_add",
-        .args_type  = "device:O",
+        .args_type  = "driver:O",
         .params     = "driver[,prop=value][,...]",
         .help       = "add device, like -device on the command line",
         .cmd        = hmp_device_add,
@@ -1315,7 +1315,7 @@ ERST
 
     {
         .name       = "netdev_add",
-        .args_type  = "netdev:O",
+        .args_type  = "type:O",
         .params     = "[user|tap|socket|vde|bridge|hubport|netmap|vhost-user],id=str[,prop=value][,...]",
         .help       = "add host network device",
         .cmd        = hmp_netdev_add,
@@ -1343,7 +1343,7 @@ ERST
 
     {
         .name       = "object_add",
-        .args_type  = "object:O",
+        .args_type  = "qom-type:O",
         .params     = "[qom-type=]type,id=str[,prop=value][,...]",
         .help       = "create QOM object",
         .cmd        = hmp_object_add,
diff --git a/monitor/hmp.c b/monitor/hmp.c
index 6c0b33a0b1..d2cb886da5 100644
--- a/monitor/hmp.c
+++ b/monitor/hmp.c
@@ -744,13 +744,9 @@ static QDict *monitor_parse_arguments(Monitor *mon,
             break;
         case 'O':
             {
-                QemuOptsList *opts_list;
-                QemuOpts *opts;
+                Error *errp;
+                bool help;
 
-                opts_list = qemu_find_opts(key);
-                if (!opts_list || opts_list->desc->name) {
-                    goto bad_type;
-                }
                 while (qemu_isspace(*p)) {
                     p++;
                 }
@@ -760,12 +756,14 @@ static QDict *monitor_parse_arguments(Monitor *mon,
                 if (get_str(buf, sizeof(buf), &p) < 0) {
                     goto fail;
                 }
-                opts = qemu_opts_parse_noisily(opts_list, buf, true);
-                if (!opts) {
-                    goto fail;
+                keyval_parse_into(qdict, buf, key, &help, &errp);
+                if (help) {
+                    if (qdict_haskey(qdict, key)) {
+                        qdict_put_bool(qdict, "help", true);
+                    } else {
+                        qdict_put_str(qdict, key, "help");
+                    }
                 }
-                qemu_opts_to_qdict(opts, qdict);
-                qemu_opts_del(opts);
             }
             break;
         case '/':
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 09/25] qom: use qemu_printf to print help for user-creatable objects
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (7 preceding siblings ...)
  2021-01-18 16:30 ` [PATCH 08/25] hmp: replace "O" parser with keyval Paolo Bonzini
@ 2021-01-18 16:30 ` Paolo Bonzini
  2021-01-25 12:47   ` Markus Armbruster
  2021-01-18 16:30 ` [PATCH 10/25] hmp: special case help options for object_add Paolo Bonzini
                   ` (16 subsequent siblings)
  25 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

This is needed when we add help support for object_add.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 qom/object_interfaces.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index 1e9ad6f08a..6c7bd025dd 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -12,6 +12,7 @@
 #include "qemu/option.h"
 #include "qapi/opts-visitor.h"
 #include "qemu/config-file.h"
+#include "qemu/qemu-print.h"
 
 bool user_creatable_complete(UserCreatable *uc, Error **errp)
 {
@@ -214,15 +215,15 @@ char *object_property_help(const char *name, const char *type,
     return g_string_free(str, false);
 }
 
-static void user_creatable_print_types(void)
+void user_creatable_print_types(void)
 {
     GSList *l, *list;
 
-    printf("List of user creatable objects:\n");
+    qemu_printf("List of user creatable objects:\n");
     list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false);
     for (l = list; l != NULL; l = l->next) {
         ObjectClass *oc = OBJECT_CLASS(l->data);
-        printf("  %s\n", object_class_get_name(oc));
+        qemu_printf("  %s\n", object_class_get_name(oc));
     }
     g_slist_free(list);
 }
@@ -253,12 +254,12 @@ static bool user_creatable_print_type_properites(const char *type)
     }
     g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
     if (array->len > 0) {
-        printf("%s options:\n", type);
+        qemu_printf("%s options:\n", type);
     } else {
-        printf("There are no options for %s.\n", type);
+        qemu_printf("There are no options for %s.\n", type);
     }
     for (i = 0; i < array->len; i++) {
-        printf("%s\n", (char *)array->pdata[i]);
+        qemu_printf("%s\n", (char *)array->pdata[i]);
     }
     g_ptr_array_set_free_func(array, g_free);
     g_ptr_array_free(array, true);
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 10/25] hmp: special case help options for object_add
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (8 preceding siblings ...)
  2021-01-18 16:30 ` [PATCH 09/25] qom: use qemu_printf to print help for user-creatable objects Paolo Bonzini
@ 2021-01-18 16:30 ` Paolo Bonzini
  2021-01-25 12:48   ` Markus Armbruster
  2021-01-18 16:30 ` [PATCH 11/25] remove -writeconfig Paolo Bonzini
                   ` (15 subsequent siblings)
  25 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Fix "object_add help" and "object_add TYPE,help".

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/qom/object_interfaces.h |  9 ++++++++-
 monitor/hmp-cmds.c              | 22 ++++++++--------------
 qom/object_interfaces.c         |  2 +-
 3 files changed, 17 insertions(+), 16 deletions(-)

diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
index 07d5cc8832..abb23eaea3 100644
--- a/include/qom/object_interfaces.h
+++ b/include/qom/object_interfaces.h
@@ -149,6 +149,13 @@ typedef bool (*user_creatable_add_opts_predicate)(const char *type);
 int user_creatable_add_opts_foreach(void *opaque,
                                     QemuOpts *opts, Error **errp);
 
+/**
+ * user_creatable_print_types:
+ *
+ * Prints a list of user-creatable objects to stdout or the monitor.
+ */
+void user_creatable_print_types(void);
+
 /**
  * user_creatable_print_help:
  * @type: the QOM type to be added
@@ -174,7 +181,7 @@ bool user_creatable_print_help(const char *type, QemuOpts *opts);
  * no help was requested. It should only be called if we know that help is
  * requested and it will always print some help.
  */
-void user_creatable_print_help_from_qdict(QDict *args);
+void user_creatable_print_help_from_qdict(const QDict *args);
 
 /**
  * user_creatable_del:
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index fd4d77e246..90dd91c6f5 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -1663,23 +1663,17 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict)
 void hmp_object_add(Monitor *mon, const QDict *qdict)
 {
     Error *err = NULL;
-    QemuOpts *opts;
-    Object *obj = NULL;
 
-    opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err);
-    if (err) {
-        goto end;
+    if (is_help_option(qdict_get_str(qdict, "qom-type"))) {
+        user_creatable_print_types();
+        return;
     }
-
-    obj = user_creatable_add_opts(opts, &err);
-    qemu_opts_del(opts);
-
-end:
-    hmp_handle_error(mon, err);
-
-    if (obj) {
-        object_unref(obj);
+    if (qdict_haskey(qdict, "help")) {
+        user_creatable_print_help_from_qdict(qdict);
+        return;
     }
+    user_creatable_add_dict((QDict *)qdict, true, &err);
+    hmp_handle_error(mon, err);
 }
 
 void hmp_getfd(Monitor *mon, const QDict *qdict)
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index 6c7bd025dd..97bf88908e 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -280,7 +280,7 @@ bool user_creatable_print_help(const char *type, QemuOpts *opts)
     return false;
 }
 
-void user_creatable_print_help_from_qdict(QDict *args)
+void user_creatable_print_help_from_qdict(const QDict *args)
 {
     const char *type = qdict_get_try_str(args, "qom-type");
 
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 11/25] remove -writeconfig
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (9 preceding siblings ...)
  2021-01-18 16:30 ` [PATCH 10/25] hmp: special case help options for object_add Paolo Bonzini
@ 2021-01-18 16:30 ` Paolo Bonzini
  2021-01-25 12:53   ` Markus Armbruster
  2021-01-18 16:31 ` [PATCH 12/25] qemu-config: add error propagation to qemu_config_parse Paolo Bonzini
                   ` (14 subsequent siblings)
  25 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:30 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Like -set and -readconfig, it would not really be too hard to
extend -writeconfig to parsing mechanisms other than QemuOpts.
However, the uses of -writeconfig are substantially more
limited, as it is generally easier to write the configuration
by hand in the first place.  In addition, -writeconfig does
not even try to detect cases where it prints incorrect
syntax (for example if values have a quote in them, since
qemu_config_parse does not support any kind of escaping.
Just remove it.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/qemu/config-file.h |  1 -
 qemu-options.hx            | 13 ++----------
 softmmu/vl.c               | 19 -----------------
 util/qemu-config.c         | 42 --------------------------------------
 4 files changed, 2 insertions(+), 73 deletions(-)

diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h
index 29226107bd..7d26fe3816 100644
--- a/include/qemu/config-file.h
+++ b/include/qemu/config-file.h
@@ -10,7 +10,6 @@ void qemu_add_opts(QemuOptsList *list);
 void qemu_add_drive_opts(QemuOptsList *list);
 int qemu_global_option(const char *str);
 
-void qemu_config_write(FILE *fp);
 int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname);
 
 int qemu_read_config_file(const char *filename);
diff --git a/qemu-options.hx b/qemu-options.hx
index 62791f56d8..7480b6a03f 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4301,23 +4301,14 @@ SRST
 ERST
 
 DEF("readconfig", HAS_ARG, QEMU_OPTION_readconfig,
-    "-readconfig <file>\n", QEMU_ARCH_ALL)
+    "-readconfig <file>\n"
+    "                read config file\n", QEMU_ARCH_ALL)
 SRST
 ``-readconfig file``
     Read device configuration from file. This approach is useful when
     you want to spawn QEMU process with many command line options but
     you don't want to exceed the command line character limit.
 ERST
-DEF("writeconfig", HAS_ARG, QEMU_OPTION_writeconfig,
-    "-writeconfig <file>\n"
-    "                read/write config file\n", QEMU_ARCH_ALL)
-SRST
-``-writeconfig file``
-    Write device configuration to file. The file can be either filename
-    to save command line and device configuration into file or dash
-    ``-``) character to print the output to stdout. This can be later
-    used as input file for ``-readconfig`` option.
-ERST
 
 DEF("no-user-config", 0, QEMU_OPTION_nouserconfig,
     "-no-user-config\n"
diff --git a/softmmu/vl.c b/softmmu/vl.c
index 7ddf405d76..d34307bf11 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -3337,25 +3337,6 @@ void qemu_init(int argc, char **argv, char **envp)
                 }
                 display_remote++;
                 break;
-            case QEMU_OPTION_writeconfig:
-                {
-                    FILE *fp;
-                    if (strcmp(optarg, "-") == 0) {
-                        fp = stdout;
-                    } else {
-                        fp = fopen(optarg, "w");
-                        if (fp == NULL) {
-                            error_report("open %s: %s", optarg,
-                                         strerror(errno));
-                            exit(1);
-                        }
-                    }
-                    qemu_config_write(fp);
-                    if (fp != stdout) {
-                        fclose(fp);
-                    }
-                    break;
-                }
             case QEMU_OPTION_qtest:
                 qtest_chrdev = optarg;
                 break;
diff --git a/util/qemu-config.c b/util/qemu-config.c
index e2a700b284..a4a1324c68 100644
--- a/util/qemu-config.c
+++ b/util/qemu-config.c
@@ -307,48 +307,6 @@ void qemu_add_opts(QemuOptsList *list)
     abort();
 }
 
-struct ConfigWriteData {
-    QemuOptsList *list;
-    FILE *fp;
-};
-
-static int config_write_opt(void *opaque, const char *name, const char *value,
-                            Error **errp)
-{
-    struct ConfigWriteData *data = opaque;
-
-    fprintf(data->fp, "  %s = \"%s\"\n", name, value);
-    return 0;
-}
-
-static int config_write_opts(void *opaque, QemuOpts *opts, Error **errp)
-{
-    struct ConfigWriteData *data = opaque;
-    const char *id = qemu_opts_id(opts);
-
-    if (id) {
-        fprintf(data->fp, "[%s \"%s\"]\n", data->list->name, id);
-    } else {
-        fprintf(data->fp, "[%s]\n", data->list->name);
-    }
-    qemu_opt_foreach(opts, config_write_opt, data, NULL);
-    fprintf(data->fp, "\n");
-    return 0;
-}
-
-void qemu_config_write(FILE *fp)
-{
-    struct ConfigWriteData data = { .fp = fp };
-    QemuOptsList **lists = vm_config_groups;
-    int i;
-
-    fprintf(fp, "# qemu config file\n\n");
-    for (i = 0; lists[i] != NULL; i++) {
-        data.list = lists[i];
-        qemu_opts_foreach(data.list, config_write_opts, &data, NULL);
-    }
-}
-
 /* Returns number of config groups on success, -errno on error */
 int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
 {
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 12/25] qemu-config: add error propagation to qemu_config_parse
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (10 preceding siblings ...)
  2021-01-18 16:30 ` [PATCH 11/25] remove -writeconfig Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-25 13:54   ` Markus Armbruster
  2021-01-18 16:31 ` [PATCH 13/25] qemu-option: support accept-any QemuOptsList in qemu_opts_absorb_qdict Paolo Bonzini
                   ` (13 subsequent siblings)
  25 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

This enables some simplification of vl.c via error_fatal.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 block/blkdebug.c           |  3 +--
 include/qemu/config-file.h |  4 ++--
 softmmu/vl.c               | 30 ++++++++++++------------------
 util/qemu-config.c         | 20 ++++++++++----------
 4 files changed, 25 insertions(+), 32 deletions(-)

diff --git a/block/blkdebug.c b/block/blkdebug.c
index 5fe6172da9..7eaa8a28bf 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -279,9 +279,8 @@ static int read_config(BDRVBlkdebugState *s, const char *filename,
             return -errno;
         }
 
-        ret = qemu_config_parse(f, config_groups, filename);
+        ret = qemu_config_parse(f, config_groups, filename, errp);
         if (ret < 0) {
-            error_setg(errp, "Could not parse blkdebug config file");
             goto fail;
         }
     }
diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h
index 7d26fe3816..da6f4690b7 100644
--- a/include/qemu/config-file.h
+++ b/include/qemu/config-file.h
@@ -10,9 +10,9 @@ void qemu_add_opts(QemuOptsList *list);
 void qemu_add_drive_opts(QemuOptsList *list);
 int qemu_global_option(const char *str);
 
-int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname);
+int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp);
 
-int qemu_read_config_file(const char *filename);
+int qemu_read_config_file(const char *filename, Error **errp);
 
 /* Parse QDict options as a replacement for a config file (allowing multiple
    enumerated (0..(n-1)) configuration "sections") */
diff --git a/softmmu/vl.c b/softmmu/vl.c
index d34307bf11..d991919155 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -2056,17 +2056,20 @@ static int global_init_func(void *opaque, QemuOpts *opts, Error **errp)
     return 0;
 }
 
-static int qemu_read_default_config_file(void)
+static void qemu_read_default_config_file(Error **errp)
 {
     int ret;
+    Error *local_err = NULL;
     g_autofree char *file = get_relocated_path(CONFIG_QEMU_CONFDIR "/qemu.conf");
 
-    ret = qemu_read_config_file(file);
-    if (ret < 0 && ret != -ENOENT) {
-        return ret;
+    ret = qemu_read_config_file(file, &local_err);
+    if (ret < 0) {
+        if (ret == -ENOENT) {
+            error_free(local_err);
+        } else {
+            error_propagate(errp, local_err);
+        }
     }
-
-    return 0;
 }
 
 static int qemu_set_option(const char *str)
@@ -2622,9 +2625,7 @@ void qemu_init(int argc, char **argv, char **envp)
     }
 
     if (userconfig) {
-        if (qemu_read_default_config_file() < 0) {
-            exit(1);
-        }
+        qemu_read_default_config_file(&error_fatal);
     }
 
     /* second pass of option parsing */
@@ -3312,15 +3313,8 @@ void qemu_init(int argc, char **argv, char **envp)
                 qemu_plugin_opt_parse(optarg, &plugin_list);
                 break;
             case QEMU_OPTION_readconfig:
-                {
-                    int ret = qemu_read_config_file(optarg);
-                    if (ret < 0) {
-                        error_report("read config %s: %s", optarg,
-                                     strerror(-ret));
-                        exit(1);
-                    }
-                    break;
-                }
+                qemu_read_config_file(optarg, &error_fatal);
+                break;
             case QEMU_OPTION_spice:
                 olist = qemu_find_opts_err("spice", NULL);
                 if (!olist) {
diff --git a/util/qemu-config.c b/util/qemu-config.c
index a4a1324c68..d0629f4960 100644
--- a/util/qemu-config.c
+++ b/util/qemu-config.c
@@ -308,7 +308,7 @@ void qemu_add_opts(QemuOptsList *list)
 }
 
 /* Returns number of config groups on success, -errno on error */
-int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
+int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp)
 {
     char line[1024], group[64], id[64], arg[64], value[1024];
     Location loc;
@@ -333,7 +333,7 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
             /* group with id */
             list = find_list(lists, group, &local_err);
             if (local_err) {
-                error_report_err(local_err);
+                error_propagate(errp, local_err);
                 goto out;
             }
             opts = qemu_opts_create(list, id, 1, NULL);
@@ -344,7 +344,7 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
             /* group without id */
             list = find_list(lists, group, &local_err);
             if (local_err) {
-                error_report_err(local_err);
+                error_propagate(errp, local_err);
                 goto out;
             }
             opts = qemu_opts_create(list, NULL, 0, &error_abort);
@@ -356,20 +356,19 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
             sscanf(line, " %63s = \"\"", arg) == 1) {
             /* arg = value */
             if (opts == NULL) {
-                error_report("no group defined");
+                error_setg(errp, "no group defined");
                 goto out;
             }
-            if (!qemu_opt_set(opts, arg, value, &local_err)) {
-                error_report_err(local_err);
+            if (!qemu_opt_set(opts, arg, value, errp)) {
                 goto out;
             }
             continue;
         }
-        error_report("parse error");
+        error_setg(errp, "parse error");
         goto out;
     }
     if (ferror(fp)) {
-        error_report("error reading file");
+        error_setg(errp, "error reading file");
         goto out;
     }
     res = count;
@@ -378,16 +377,17 @@ out:
     return res;
 }
 
-int qemu_read_config_file(const char *filename)
+int qemu_read_config_file(const char *filename, Error **errp)
 {
     FILE *f = fopen(filename, "r");
     int ret;
 
     if (f == NULL) {
+        error_setg_errno(errp, errno, "Cannot read config file %s", filename);
         return -errno;
     }
 
-    ret = qemu_config_parse(f, vm_config_groups, filename);
+    ret = qemu_config_parse(f, vm_config_groups, filename, errp);
     fclose(f);
     return ret;
 }
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 13/25] qemu-option: support accept-any QemuOptsList in qemu_opts_absorb_qdict
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (11 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 12/25] qemu-config: add error propagation to qemu_config_parse Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-18 16:31 ` [PATCH 14/25] qemu-config: parse configuration files to a QDict Paolo Bonzini
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 util/qemu-option.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/util/qemu-option.c b/util/qemu-option.c
index 40564a12eb..afba08d92e 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -1052,7 +1052,8 @@ bool qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp)
     while (entry != NULL) {
         next = qdict_next(qdict, entry);
 
-        if (find_desc_by_name(opts->list->desc, entry->key)) {
+        if (opts_accepts_any(opts->list) ||
+            find_desc_by_name(opts->list->desc, entry->key)) {
             if (!qemu_opts_from_qdict_entry(opts, entry, errp)) {
                 return false;
             }
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 14/25] qemu-config: parse configuration files to a QDict
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (12 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 13/25] qemu-option: support accept-any QemuOptsList in qemu_opts_absorb_qdict Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-18 16:31 ` [PATCH 15/25] vl: plumb keyval-based options into -set and -readconfig Paolo Bonzini
                   ` (11 subsequent siblings)
  25 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Change the parser to put the values into a QDict and pass them
to a callback.  qemu_config_parse's QemuOpts creation is
itself turned into a callback function.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/qemu/config-file.h |  6 ++-
 softmmu/vl.c               |  4 +-
 util/qemu-config.c         | 91 +++++++++++++++++++++++++-------------
 3 files changed, 68 insertions(+), 33 deletions(-)

diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h
index da6f4690b7..dcf2948435 100644
--- a/include/qemu/config-file.h
+++ b/include/qemu/config-file.h
@@ -1,6 +1,7 @@
 #ifndef QEMU_CONFIG_FILE_H
 #define QEMU_CONFIG_FILE_H
 
+typedef void QEMUConfigCB(const char *group, QDict *qdict, void *opaque, Error **errp);
 
 QemuOptsList *qemu_find_opts(const char *group);
 QemuOptsList *qemu_find_opts_err(const char *group, Error **errp);
@@ -12,7 +13,10 @@ int qemu_global_option(const char *str);
 
 int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp);
 
-int qemu_read_config_file(const char *filename, Error **errp);
+/* A default callback for qemu_read_config_file().  */
+void qemu_config_do_parse(const char *group, QDict *qdict, void *opaque, Error **errp);
+
+int qemu_read_config_file(const char *filename, QEMUConfigCB *f, Error **errp);
 
 /* Parse QDict options as a replacement for a config file (allowing multiple
    enumerated (0..(n-1)) configuration "sections") */
diff --git a/softmmu/vl.c b/softmmu/vl.c
index d991919155..e4fe1b33e3 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -2062,7 +2062,7 @@ static void qemu_read_default_config_file(Error **errp)
     Error *local_err = NULL;
     g_autofree char *file = get_relocated_path(CONFIG_QEMU_CONFDIR "/qemu.conf");
 
-    ret = qemu_read_config_file(file, &local_err);
+    ret = qemu_read_config_file(file, qemu_config_do_parse, &local_err);
     if (ret < 0) {
         if (ret == -ENOENT) {
             error_free(local_err);
@@ -3313,7 +3313,7 @@ void qemu_init(int argc, char **argv, char **envp)
                 qemu_plugin_opt_parse(optarg, &plugin_list);
                 break;
             case QEMU_OPTION_readconfig:
-                qemu_read_config_file(optarg, &error_fatal);
+                qemu_read_config_file(optarg, qemu_config_do_parse, &error_fatal);
                 break;
             case QEMU_OPTION_spice:
                 olist = qemu_find_opts_err("spice", NULL);
diff --git a/util/qemu-config.c b/util/qemu-config.c
index d0629f4960..ce7c9469b8 100644
--- a/util/qemu-config.c
+++ b/util/qemu-config.c
@@ -308,19 +308,19 @@ void qemu_add_opts(QemuOptsList *list)
 }
 
 /* Returns number of config groups on success, -errno on error */
-int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp)
+static int qemu_config_foreach(FILE *fp, QEMUConfigCB *cb, void *opaque,
+                               const char *fname, Error **errp)
 {
-    char line[1024], group[64], id[64], arg[64], value[1024];
+    char line[1024], prev_group[64], group[64], arg[64], value[1024];
     Location loc;
-    QemuOptsList *list = NULL;
     Error *local_err = NULL;
-    QemuOpts *opts = NULL;
+    QDict *qdict = NULL;
     int res = -EINVAL, lno = 0;
     int count = 0;
 
     loc_push_none(&loc);
     while (fgets(line, sizeof(line), fp) != NULL) {
-        loc_set_file(fname, ++lno);
+        ++lno;
         if (line[0] == '\n') {
             /* skip empty lines */
             continue;
@@ -329,39 +329,39 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error *
             /* comment */
             continue;
         }
-        if (sscanf(line, "[%63s \"%63[^\"]\"]", group, id) == 2) {
-            /* group with id */
-            list = find_list(lists, group, &local_err);
-            if (local_err) {
-                error_propagate(errp, local_err);
-                goto out;
+        if (line[0] == '[') {
+            QDict *prev = qdict;
+            if (sscanf(line, "[%63s \"%63[^\"]\"]", group, value) == 2) {
+                qdict = qdict_new();
+                qdict_put_str(qdict, "id", value);
+                count++;
+            } else if (sscanf(line, "[%63[^]]]", group) == 1) {
+                qdict = qdict_new();
+                count++;
             }
-            opts = qemu_opts_create(list, id, 1, NULL);
-            count++;
-            continue;
-        }
-        if (sscanf(line, "[%63[^]]]", group) == 1) {
-            /* group without id */
-            list = find_list(lists, group, &local_err);
-            if (local_err) {
-                error_propagate(errp, local_err);
-                goto out;
+            if (qdict != prev) {
+                if (prev) {
+                    cb(prev_group, prev, opaque, &local_err);
+                    qobject_unref(prev);
+                    if (local_err) {
+                        error_propagate(errp, local_err);
+                        goto out;
+                    }
+                }
+                strcpy(prev_group, group);
+                continue;
             }
-            opts = qemu_opts_create(list, NULL, 0, &error_abort);
-            count++;
-            continue;
         }
+        loc_set_file(fname, lno);
         value[0] = '\0';
         if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2 ||
             sscanf(line, " %63s = \"\"", arg) == 1) {
             /* arg = value */
-            if (opts == NULL) {
+            if (qdict == NULL) {
                 error_setg(errp, "no group defined");
                 goto out;
             }
-            if (!qemu_opt_set(opts, arg, value, errp)) {
-                goto out;
-            }
+            qdict_put_str(qdict, arg, value);
             continue;
         }
         error_setg(errp, "parse error");
@@ -374,10 +374,41 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error *
     res = count;
 out:
     loc_pop(&loc);
+    if (qdict) {
+        cb(group, qdict, opaque, errp);
+        qobject_unref(qdict);
+    }
     return res;
 }
 
-int qemu_read_config_file(const char *filename, Error **errp)
+void qemu_config_do_parse(const char *group, QDict *qdict, void *opaque, Error **errp)
+{
+    QemuOptsList **lists = opaque;
+    const char *id = qdict_get_try_str(qdict, "id");
+    QemuOptsList *list;
+    QemuOpts *opts;
+
+    list = find_list(lists, group, errp);
+    if (!list) {
+        return;
+    }
+
+    opts = qemu_opts_create(list, id, 1, errp);
+    if (!opts) {
+        return;
+    }
+    if (id) {
+        qdict_del(qdict, "id");
+    }
+    qemu_opts_absorb_qdict(opts, qdict, errp);
+}
+
+int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp)
+{
+    return qemu_config_foreach(fp, qemu_config_do_parse, lists, fname, errp);
+}
+
+int qemu_read_config_file(const char *filename, QEMUConfigCB *cb, Error **errp)
 {
     FILE *f = fopen(filename, "r");
     int ret;
@@ -387,7 +418,7 @@ int qemu_read_config_file(const char *filename, Error **errp)
         return -errno;
     }
 
-    ret = qemu_config_parse(f, vm_config_groups, filename, errp);
+    ret = qemu_config_foreach(f, cb, vm_config_groups, filename, errp);
     fclose(f);
     return ret;
 }
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 15/25] vl: plumb keyval-based options into -set and -readconfig
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (13 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 14/25] qemu-config: parse configuration files to a QDict Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-25 11:48   ` Markus Armbruster
  2021-01-18 16:31 ` [PATCH 16/25] qom: do not modify QDict argument in user_creatable_add_dict Paolo Bonzini
                   ` (10 subsequent siblings)
  25 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Add generic machinery to support parsing command line options with
keyval in -set and -readconfig, choosing between QDict and
QemuOpts as the underlying data structure.

The keyval_merge function is slightly heavyweight as a way to
do qemu_set_option for QDict-based options, but it will be put
to further use later to merge entire -readconfig sections together
with their command-line equivalents.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/block/qdict.h    |   2 -
 include/qapi/qmp/qdict.h |   3 +
 include/qemu/option.h    |   1 +
 softmmu/vl.c             | 131 ++++++++++++++++++++++++++++++++-------
 tests/test-keyval.c      |  37 +++++++++++
 util/keyval.c            |  36 +++++++++++
 6 files changed, 187 insertions(+), 23 deletions(-)

diff --git a/include/block/qdict.h b/include/block/qdict.h
index d8cb502d7d..ced2acfb92 100644
--- a/include/block/qdict.h
+++ b/include/block/qdict.h
@@ -20,8 +20,6 @@ void qdict_join(QDict *dest, QDict *src, bool overwrite);
 void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
 void qdict_array_split(QDict *src, QList **dst);
 int qdict_array_entries(QDict *src, const char *subqdict);
-QObject *qdict_crumple(const QDict *src, Error **errp);
-void qdict_flatten(QDict *qdict);
 
 typedef struct QDictRenames {
     const char *from;
diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
index 9934539c1b..d5b5430e21 100644
--- a/include/qapi/qmp/qdict.h
+++ b/include/qapi/qmp/qdict.h
@@ -64,4 +64,7 @@ const char *qdict_get_try_str(const QDict *qdict, const char *key);
 
 QDict *qdict_clone_shallow(const QDict *src);
 
+QObject *qdict_crumple(const QDict *src, Error **errp);
+void qdict_flatten(QDict *qdict);
+
 #endif /* QDICT_H */
diff --git a/include/qemu/option.h b/include/qemu/option.h
index 092e291c37..fffb03d848 100644
--- a/include/qemu/option.h
+++ b/include/qemu/option.h
@@ -151,5 +151,6 @@ QDict *keyval_parse_into(QDict *qdict, const char *params, const char *implied_k
                          bool *p_help, Error **errp);
 QDict *keyval_parse(const char *params, const char *implied_key,
                     bool *help, Error **errp);
+void keyval_merge(QDict *old, const QDict *new, Error **errp);
 
 #endif
diff --git a/softmmu/vl.c b/softmmu/vl.c
index e4fe1b33e3..ce0693cdda 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -117,6 +117,7 @@
 #include "qapi/qapi-commands-migration.h"
 #include "qapi/qapi-commands-misc.h"
 #include "qapi/qapi-commands-ui.h"
+#include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qerror.h"
 #include "sysemu/iothread.h"
 #include "qemu/guest-random.h"
@@ -2056,13 +2057,94 @@ static int global_init_func(void *opaque, QemuOpts *opts, Error **errp)
     return 0;
 }
 
+/*
+ * Return whether configuration group @group is stored in QemuOpts or
+ * in a list of QDicts.
+ */
+static bool is_qemuopts_group(const char *group)
+{
+    return true;
+}
+
+/*
+ * Return a pointer to a list of QDicts, used to store options for
+ * "-set GROUP.*".
+ */
+static GSList **qemu_config_list(const char *group)
+{
+    return NULL;
+}
+
+/*
+ * Return a pointer to a QDict inside the list starting at @head,
+ * used to store options for "-GROUP id=...".
+ */
+static QDict *qemu_find_config(GSList *head, const char *id)
+{
+    assert(id);
+    while (head) {
+        QDict *dict = head->data;
+        if (g_strcmp0(qdict_get_try_str(dict, "id"), id) == 0) {
+            return dict;
+        }
+        head = head->next;
+    }
+    return NULL;
+}
+
+static void qemu_record_config_group(const char *group, QDict *dict, Error **errp)
+{
+    GSList **p_head;
+
+    p_head = qemu_config_list(group);
+    if (p_head) {
+        *p_head = g_slist_prepend(*p_head, dict);
+    } else {
+        abort();
+    }
+}
+
+static void qemu_set_qdict_option(QDict *dict, const char *key, const char *value,
+                                  Error **errp)
+{
+    QDict *merge_dict;
+
+    merge_dict = qdict_new();
+    qdict_put_str(merge_dict, key, value);
+    keyval_merge(dict, merge_dict, errp);
+    qobject_unref(merge_dict);
+}
+
+/*
+ * Parse non-QemuOpts config file groups, pass the rest to
+ * qemu_config_do_parse.
+ */
+static void qemu_parse_config_group(const char *group, QDict *qdict,
+                                    void *opaque, Error **errp)
+{
+    if (is_qemuopts_group(group)) {
+        QObject *crumpled = qdict_crumple(qdict, errp);
+        if (!crumpled) {
+            return;
+        }
+        if (qobject_type(crumpled) != QTYPE_QDICT) {
+            assert(qobject_type(crumpled) == QTYPE_QLIST);
+            error_setg(errp, "Lists cannot be at top level of a configuration section");
+            return;
+        }
+        qemu_record_config_group(group, qobject_to(QDict, crumpled), errp);
+    } else {
+        qemu_config_do_parse(group, qdict, opaque, errp);
+    }
+}
+
 static void qemu_read_default_config_file(Error **errp)
 {
     int ret;
     Error *local_err = NULL;
     g_autofree char *file = get_relocated_path(CONFIG_QEMU_CONFDIR "/qemu.conf");
 
-    ret = qemu_read_config_file(file, qemu_config_do_parse, &local_err);
+    ret = qemu_read_config_file(file, qemu_parse_config_group, &local_err);
     if (ret < 0) {
         if (ret == -ENOENT) {
             error_free(local_err);
@@ -2072,37 +2154,45 @@ static void qemu_read_default_config_file(Error **errp)
     }
 }
 
-static int qemu_set_option(const char *str)
+static void qemu_set_option(const char *str, Error **errp)
 {
-    Error *local_err = NULL;
     char group[64], id[64], arg[64];
     QemuOptsList *list;
     QemuOpts *opts;
+    GSList **p_head;
+    QDict *dict;
     int rc, offset;
 
     rc = sscanf(str, "%63[^.].%63[^.].%63[^=]%n", group, id, arg, &offset);
     if (rc < 3 || str[offset] != '=') {
-        error_report("can't parse: \"%s\"", str);
-        return -1;
+        error_setg(errp, "can't parse: \"%s\"", str);
+        return;
     }
 
-    list = qemu_find_opts(group);
-    if (list == NULL) {
-        return -1;
+    p_head = qemu_config_list(group);
+    if (p_head) {
+        dict = qemu_find_config(*p_head, id);
+        if (!dict) {
+            goto not_found;
+        }
+        qemu_set_qdict_option(dict, arg, str + offset + 1, errp);
+        return;
     }
 
-    opts = qemu_opts_find(list, id);
-    if (!opts) {
-        error_report("there is no %s \"%s\" defined",
-                     list->name, id);
-        return -1;
+    list = qemu_find_opts_err(group, errp);
+    if (list) {
+        opts = qemu_opts_find(list, id);
+        if (!opts) {
+            goto not_found;
+        }
+        qemu_opt_set(opts, arg, str + offset + 1, errp);
+        return;
     }
 
-    if (!qemu_opt_set(opts, arg, str + offset + 1, &local_err)) {
-        error_report_err(local_err);
-        return -1;
-    }
-    return 0;
+    return;
+
+not_found:
+    error_setg(errp, "there is no %s \"%s\" defined", group, id);
 }
 
 static void user_register_global_props(void)
@@ -2678,8 +2768,7 @@ void qemu_init(int argc, char **argv, char **envp)
                 }
                 break;
             case QEMU_OPTION_set:
-                if (qemu_set_option(optarg) != 0)
-                    exit(1);
+                qemu_set_option(optarg, &error_fatal);
                 break;
             case QEMU_OPTION_global:
                 if (qemu_global_option(optarg) != 0)
@@ -3313,7 +3402,7 @@ void qemu_init(int argc, char **argv, char **envp)
                 qemu_plugin_opt_parse(optarg, &plugin_list);
                 break;
             case QEMU_OPTION_readconfig:
-                qemu_read_config_file(optarg, qemu_config_do_parse, &error_fatal);
+                qemu_read_config_file(optarg, qemu_parse_config_group, &error_fatal);
                 break;
             case QEMU_OPTION_spice:
                 olist = qemu_find_opts_err("spice", NULL);
diff --git a/tests/test-keyval.c b/tests/test-keyval.c
index 19f664f535..2353c707bf 100644
--- a/tests/test-keyval.c
+++ b/tests/test-keyval.c
@@ -737,6 +737,41 @@ static void test_keyval_visit_any(void)
     visit_free(v);
 }
 
+static void test_keyval_merge_success(void)
+{
+    QDict *old = keyval_parse("opt1=abc,opt2.sub1=def,opt2.sub2=ghi,opt3=xyz",
+                              NULL, NULL, &error_abort);
+    QDict *new = keyval_parse("opt1=ABC,opt2.sub2=GHI,opt2.sub3=JKL",
+                              NULL, NULL, &error_abort);
+    QDict *combined = keyval_parse("opt1=ABC,opt2.sub1=def,opt2.sub2=GHI,opt2.sub3=JKL,opt3=xyz",
+                                   NULL, NULL, &error_abort);
+    Error *err = NULL;
+
+    keyval_merge(old, new, &err);
+    g_assert(!err);
+    g_assert(qobject_is_equal(QOBJECT(combined), QOBJECT(old)));
+    qobject_unref(old);
+    qobject_unref(new);
+    qobject_unref(combined);
+}
+
+static void test_keyval_merge_conflict(void)
+{
+    QDict *old = keyval_parse("opt2.sub1=def,opt2.sub2=ghi",
+                              NULL, NULL, &error_abort);
+    QDict *new = keyval_parse("opt2=ABC",
+                              NULL, NULL, &error_abort);
+    Error *err = NULL;
+
+    keyval_merge(new, old, &err);
+    error_free_or_abort(&err);
+    keyval_merge(old, new, &err);
+    error_free_or_abort(&err);
+
+    qobject_unref(old);
+    qobject_unref(new);
+}
+
 int main(int argc, char *argv[])
 {
     g_test_init(&argc, &argv, NULL);
@@ -750,6 +785,8 @@ int main(int argc, char *argv[])
     g_test_add_func("/keyval/visit/optional", test_keyval_visit_optional);
     g_test_add_func("/keyval/visit/alternate", test_keyval_visit_alternate);
     g_test_add_func("/keyval/visit/any", test_keyval_visit_any);
+    g_test_add_func("/keyval/merge/success", test_keyval_merge_success);
+    g_test_add_func("/keyval/merge/conflict", test_keyval_merge_conflict);
     g_test_run();
     return 0;
 }
diff --git a/util/keyval.c b/util/keyval.c
index 1d4ca12129..7c886dd402 100644
--- a/util/keyval.c
+++ b/util/keyval.c
@@ -315,6 +315,42 @@ static char *reassemble_key(GSList *key)
     return g_string_free(s, FALSE);
 }
 
+/* Merge two dictionaries.  */
+static void keyval_do_merge(QDict *old, const QDict *new, GString *str, Error **errp)
+{
+    size_t save_len = str->len;
+    const QDictEntry *ent;
+    QObject *old_value;
+
+    for (ent = qdict_first(new); ent; ent = qdict_next(new, ent)) {
+        old_value = qdict_get(old, ent->key);
+        if (old_value && qobject_type(old_value) != qobject_type(ent->value)) {
+            error_setg(errp, "Parameter '%s%s' used inconsistently", str->str, ent->key);
+            return;
+        }
+        if (!old_value || qobject_type(ent->value) != QTYPE_QDICT) {
+            qobject_ref(ent->value);
+            qdict_put_obj(old, ent->key, ent->value);
+            continue;
+        }
+
+        /* Merge sub-dictionaries.  */
+        g_string_append(str, ent->key);
+        g_string_append_c(str, '.');
+        keyval_do_merge(qobject_to(QDict, old_value),
+                        qobject_to(QDict, ent->value),
+                        str, errp);
+        g_string_truncate(str, save_len);
+    }
+}
+
+void keyval_merge(QDict *old, const QDict *new, Error **errp)
+{
+    GString *str = g_string_new("");
+    keyval_do_merge(old, new, str, errp);
+    g_string_free(str, TRUE);
+}
+
 /*
  * Listify @cur recursively.
  * Replace QDicts whose keys are all valid list indexes by QLists.
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 16/25] qom: do not modify QDict argument in user_creatable_add_dict
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (14 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 15/25] vl: plumb keyval-based options into -set and -readconfig Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-18 16:31 ` [PATCH 17/25] qemu-io: use keyval for -object parsing Paolo Bonzini
                   ` (9 subsequent siblings)
  25 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

-object will process its QDicts in two steps, first for the "early" objects and
then for the "late" objects.  If qom-type is removed by the "early" pass, the
late pass fails.  So just create a shallow copy of the QDict in
user_creatable_add_dict.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/qom/object_interfaces.h |  2 +-
 qom/object_interfaces.c         | 11 +++++++----
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
index abb23eaea3..ed0d7d663b 100644
--- a/include/qom/object_interfaces.h
+++ b/include/qom/object_interfaces.h
@@ -102,7 +102,7 @@ Object *user_creatable_add_type(const char *type, const char *id,
  *
  * Returns: %true on success, %false on failure.
  */
-bool user_creatable_add_dict(QDict *qdict, bool keyval, Error **errp);
+bool user_creatable_add_dict(const QDict *qdict, bool keyval, Error **errp);
 
 /**
  * user_creatable_add_opts:
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index 97bf88908e..fbbf5e8ad3 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -105,24 +105,25 @@ out:
     return obj;
 }
 
-bool user_creatable_add_dict(QDict *qdict, bool keyval, Error **errp)
+bool user_creatable_add_dict(const QDict *dict, bool keyval, Error **errp)
 {
     Visitor *v;
-    Object *obj;
+    Object *obj = NULL;
+    QDict *qdict = qdict_clone_shallow(dict);
     g_autofree char *type = NULL;
     g_autofree char *id = NULL;
 
     type = g_strdup(qdict_get_try_str(qdict, "qom-type"));
     if (!type) {
         error_setg(errp, QERR_MISSING_PARAMETER, "qom-type");
-        return false;
+        goto out;
     }
     qdict_del(qdict, "qom-type");
 
     id = g_strdup(qdict_get_try_str(qdict, "id"));
     if (!id) {
         error_setg(errp, QERR_MISSING_PARAMETER, "id");
-        return false;
+        goto out;
     }
     qdict_del(qdict, "id");
 
@@ -134,6 +135,8 @@ bool user_creatable_add_dict(QDict *qdict, bool keyval, Error **errp)
     obj = user_creatable_add_type(type, id, qdict, v, errp);
     visit_free(v);
     object_unref(obj);
+out:
+    qobject_unref(qdict);
     return !!obj;
 }
 
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 17/25] qemu-io: use keyval for -object parsing
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (15 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 16/25] qom: do not modify QDict argument in user_creatable_add_dict Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-18 16:31 ` [PATCH 18/25] qemu-nbd: " Paolo Bonzini
                   ` (8 subsequent siblings)
  25 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Enable creation of object with non-scalar properties.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 qemu-io.c | 42 +++++++++++++-----------------------------
 1 file changed, 13 insertions(+), 29 deletions(-)

diff --git a/qemu-io.c b/qemu-io.c
index ac88d8bd40..306086f767 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -477,23 +477,6 @@ enum {
     OPTION_IMAGE_OPTS = 257,
 };
 
-static QemuOptsList qemu_object_opts = {
-    .name = "object",
-    .implied_opt_name = "qom-type",
-    .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
-    .desc = {
-        { }
-    },
-};
-
-static bool qemu_io_object_print_help(const char *type, QemuOpts *opts)
-{
-    if (user_creatable_print_help(type, opts)) {
-        exit(0);
-    }
-    return true;
-}
-
 static QemuOptsList file_opts = {
     .name = "file",
     .implied_opt_name = "file",
@@ -550,7 +533,6 @@ int main(int argc, char **argv)
     qcrypto_init(&error_fatal);
 
     module_call_init(MODULE_INIT_QOM);
-    qemu_add_opts(&qemu_object_opts);
     qemu_add_opts(&qemu_trace_opts);
     bdrv_init();
 
@@ -612,14 +594,20 @@ int main(int argc, char **argv)
         case 'U':
             force_share = true;
             break;
-        case OPTION_OBJECT: {
-            QemuOpts *qopts;
-            qopts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                            optarg, true);
-            if (!qopts) {
-                exit(1);
+        case OPTION_OBJECT:
+            {
+                QDict *args;
+                bool help;
+
+                args = keyval_parse(optarg, "qom-type", &help, &error_fatal);
+                if (help) {
+                    user_creatable_print_help_from_qdict(args);
+                    exit(EXIT_SUCCESS);
+                }
+                user_creatable_add_dict(args, true, &error_fatal);
+                qobject_unref(args);
+                break;
             }
-        }   break;
         case OPTION_IMAGE_OPTS:
             imageOpts = true;
             break;
@@ -644,10 +632,6 @@ int main(int argc, char **argv)
         exit(1);
     }
 
-    qemu_opts_foreach(&qemu_object_opts,
-                      user_creatable_add_opts_foreach,
-                      qemu_io_object_print_help, &error_fatal);
-
     if (!trace_init_backends()) {
         exit(1);
     }
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 18/25] qemu-nbd: use keyval for -object parsing
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (16 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 17/25] qemu-io: use keyval for -object parsing Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-18 16:31 ` [PATCH 19/25] qemu-img: " Paolo Bonzini
                   ` (7 subsequent siblings)
  25 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Enable creation of object with non-scalar properties.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 qemu-nbd.c | 42 +++++++++++++-----------------------------
 1 file changed, 13 insertions(+), 29 deletions(-)

diff --git a/qemu-nbd.c b/qemu-nbd.c
index a7075c5419..b4bd21a21e 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -407,23 +407,6 @@ static QemuOptsList file_opts = {
     },
 };
 
-static QemuOptsList qemu_object_opts = {
-    .name = "object",
-    .implied_opt_name = "qom-type",
-    .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
-    .desc = {
-        { }
-    },
-};
-
-static bool qemu_nbd_object_print_help(const char *type, QemuOpts *opts)
-{
-    if (user_creatable_print_help(type, opts)) {
-        exit(0);
-    }
-    return true;
-}
-
 
 static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, bool list,
                                           Error **errp)
@@ -599,7 +582,6 @@ int main(int argc, char **argv)
     qcrypto_init(&error_fatal);
 
     module_call_init(MODULE_INIT_QOM);
-    qemu_add_opts(&qemu_object_opts);
     qemu_add_opts(&qemu_trace_opts);
     qemu_init_exec_dir(argv[0]);
 
@@ -752,14 +734,20 @@ int main(int argc, char **argv)
         case '?':
             error_report("Try `%s --help' for more information.", argv[0]);
             exit(EXIT_FAILURE);
-        case QEMU_NBD_OPT_OBJECT: {
-            QemuOpts *opts;
-            opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                           optarg, true);
-            if (!opts) {
-                exit(EXIT_FAILURE);
+        case QEMU_NBD_OPT_OBJECT:
+            {
+                QDict *args;
+                bool help;
+
+                args = keyval_parse(optarg, "qom-type", &help, &error_fatal);
+                if (help) {
+                    user_creatable_print_help_from_qdict(args);
+                    exit(EXIT_SUCCESS);
+                }
+                user_creatable_add_dict(args, true, &error_fatal);
+                qobject_unref(args);
+                break;
             }
-        }   break;
         case QEMU_NBD_OPT_TLSCREDS:
             tlscredsid = optarg;
             break;
@@ -807,10 +795,6 @@ int main(int argc, char **argv)
         export_name = "";
     }
 
-    qemu_opts_foreach(&qemu_object_opts,
-                      user_creatable_add_opts_foreach,
-                      qemu_nbd_object_print_help, &error_fatal);
-
     if (!trace_init_backends()) {
         exit(1);
     }
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 19/25] qemu-img: use keyval for -object parsing
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (17 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 18/25] qemu-nbd: " Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-18 16:31 ` [PATCH 20/25] qemu: " Paolo Bonzini
                   ` (6 subsequent siblings)
  25 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Enable creation of object with non-scalar properties.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 qemu-img.c | 258 +++++++++++------------------------------------------
 1 file changed, 52 insertions(+), 206 deletions(-)

diff --git a/qemu-img.c b/qemu-img.c
index 8597d069af..639a7b0256 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -226,21 +226,27 @@ static void QEMU_NORETURN help(void)
     exit(EXIT_SUCCESS);
 }
 
-static QemuOptsList qemu_object_opts = {
-    .name = "object",
-    .implied_opt_name = "qom-type",
-    .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
-    .desc = {
-        { }
-    },
-};
-
-static bool qemu_img_object_print_help(const char *type, QemuOpts *opts)
+static void qemu_img_object_parse(const char *optarg, int exit_code)
 {
-    if (user_creatable_print_help(type, opts)) {
-        exit(0);
+    QDict *args;
+    bool help;
+    Error *local_error = NULL;
+
+    args = keyval_parse(optarg, "qom-type", &help, &local_error);
+    if (local_error) {
+        error_report_err(local_error);
+        exit(exit_code);
     }
-    return true;
+    if (help) {
+        user_creatable_print_help_from_qdict(args);
+        exit(EXIT_SUCCESS);
+    }
+    user_creatable_add_dict(args, true, &local_error);
+    if (local_error) {
+        error_report_err(local_error);
+        exit(exit_code);
+    }
+    qobject_unref(args);
 }
 
 /*
@@ -566,14 +572,9 @@ static int img_create(int argc, char **argv)
         case 'u':
             flags |= BDRV_O_NO_BACKING;
             break;
-        case OPTION_OBJECT: {
-            QemuOpts *opts;
-            opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                           optarg, true);
-            if (!opts) {
-                goto fail;
-            }
-        }   break;
+        case OPTION_OBJECT:
+            qemu_img_object_parse(optarg, 1);
+            break;
         }
     }
 
@@ -589,12 +590,6 @@ static int img_create(int argc, char **argv)
     }
     optind++;
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        goto fail;
-    }
-
     /* Get image size, if specified */
     if (optind < argc) {
         int64_t sval;
@@ -804,14 +799,9 @@ static int img_check(int argc, char **argv)
         case 'U':
             force_share = true;
             break;
-        case OPTION_OBJECT: {
-            QemuOpts *opts;
-            opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                           optarg, true);
-            if (!opts) {
-                return 1;
-            }
-        }   break;
+        case OPTION_OBJECT:
+            qemu_img_object_parse(optarg, 1);
+            break;
         case OPTION_IMAGE_OPTS:
             image_opts = true;
             break;
@@ -831,12 +821,6 @@ static int img_check(int argc, char **argv)
         return 1;
     }
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        return 1;
-    }
-
     ret = bdrv_parse_cache_mode(cache, &flags, &writethrough);
     if (ret < 0) {
         error_report("Invalid source cache option: %s", cache);
@@ -1034,14 +1018,9 @@ static int img_commit(int argc, char **argv)
                 return 1;
             }
             break;
-        case OPTION_OBJECT: {
-            QemuOpts *opts;
-            opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                           optarg, true);
-            if (!opts) {
-                return 1;
-            }
-        }   break;
+        case OPTION_OBJECT:
+            qemu_img_object_parse(optarg, 1);
+            break;
         case OPTION_IMAGE_OPTS:
             image_opts = true;
             break;
@@ -1058,12 +1037,6 @@ static int img_commit(int argc, char **argv)
     }
     filename = argv[optind++];
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        return 1;
-    }
-
     flags = BDRV_O_RDWR | BDRV_O_UNMAP;
     ret = bdrv_parse_cache_mode(cache, &flags, &writethrough);
     if (ret < 0) {
@@ -1423,15 +1396,9 @@ static int img_compare(int argc, char **argv)
         case 'U':
             force_share = true;
             break;
-        case OPTION_OBJECT: {
-            QemuOpts *opts;
-            opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                           optarg, true);
-            if (!opts) {
-                ret = 2;
-                goto out4;
-            }
-        }   break;
+        case OPTION_OBJECT:
+            qemu_img_object_parse(optarg, 2);
+            break;
         case OPTION_IMAGE_OPTS:
             image_opts = true;
             break;
@@ -1450,13 +1417,6 @@ static int img_compare(int argc, char **argv)
     filename1 = argv[optind++];
     filename2 = argv[optind++];
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        ret = 2;
-        goto out4;
-    }
-
     /* Initialize before goto out */
     qemu_progress_init(progress, 2.0);
 
@@ -1641,7 +1601,6 @@ out2:
     blk_unref(blk1);
 out3:
     qemu_progress_end();
-out4:
     return ret;
 }
 
@@ -2342,15 +2301,9 @@ static int img_convert(int argc, char **argv)
                 goto fail_getopt;
             }
             break;
-        case OPTION_OBJECT: {
-            QemuOpts *object_opts;
-            object_opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                                  optarg, true);
-            if (!object_opts) {
-                goto fail_getopt;
-            }
+        case OPTION_OBJECT:
+            qemu_img_object_parse(optarg, 1);
             break;
-        }
         case OPTION_IMAGE_OPTS:
             image_opts = true;
             break;
@@ -2378,12 +2331,6 @@ static int img_convert(int argc, char **argv)
         out_fmt = "raw";
     }
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        goto fail_getopt;
-    }
-
     if (s.compressed && s.copy_range) {
         error_report("Cannot enable copy offloading when -c is used");
         goto fail_getopt;
@@ -2975,14 +2922,9 @@ static int img_info(int argc, char **argv)
         case OPTION_BACKING_CHAIN:
             chain = true;
             break;
-        case OPTION_OBJECT: {
-            QemuOpts *opts;
-            opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                           optarg, true);
-            if (!opts) {
-                return 1;
-            }
-        }   break;
+        case OPTION_OBJECT:
+            qemu_img_object_parse(optarg, 1);
+            break;
         case OPTION_IMAGE_OPTS:
             image_opts = true;
             break;
@@ -3002,12 +2944,6 @@ static int img_info(int argc, char **argv)
         return 1;
     }
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        return 1;
-    }
-
     list = collect_image_info_list(image_opts, filename, fmt, chain,
                                    force_share);
     if (!list) {
@@ -3217,14 +3153,9 @@ static int img_map(int argc, char **argv)
                 return 1;
             }
             break;
-        case OPTION_OBJECT: {
-            QemuOpts *opts;
-            opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                           optarg, true);
-            if (!opts) {
-                return 1;
-            }
-        }   break;
+        case OPTION_OBJECT:
+            qemu_img_object_parse(optarg, 1);
+            break;
         case OPTION_IMAGE_OPTS:
             image_opts = true;
             break;
@@ -3244,12 +3175,6 @@ static int img_map(int argc, char **argv)
         return 1;
     }
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        return 1;
-    }
-
     blk = img_open(image_opts, filename, fmt, 0, false, false, force_share);
     if (!blk) {
         return 1;
@@ -3388,14 +3313,9 @@ static int img_snapshot(int argc, char **argv)
         case 'U':
             force_share = true;
             break;
-        case OPTION_OBJECT: {
-            QemuOpts *opts;
-            opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                           optarg, true);
-            if (!opts) {
-                return 1;
-            }
-        }   break;
+        case OPTION_OBJECT:
+            qemu_img_object_parse(optarg, 1);
+            break;
         case OPTION_IMAGE_OPTS:
             image_opts = true;
             break;
@@ -3407,12 +3327,6 @@ static int img_snapshot(int argc, char **argv)
     }
     filename = argv[optind++];
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        return 1;
-    }
-
     /* Open the image */
     blk = img_open(image_opts, filename, NULL, bdrv_oflags, false, quiet,
                    force_share);
@@ -3546,14 +3460,9 @@ static int img_rebase(int argc, char **argv)
         case 'q':
             quiet = true;
             break;
-        case OPTION_OBJECT: {
-            QemuOpts *opts;
-            opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                           optarg, true);
-            if (!opts) {
-                return 1;
-            }
-        }   break;
+        case OPTION_OBJECT:
+            qemu_img_object_parse(optarg, 1);
+            break;
         case OPTION_IMAGE_OPTS:
             image_opts = true;
             break;
@@ -3575,12 +3484,6 @@ static int img_rebase(int argc, char **argv)
     }
     filename = argv[optind++];
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        return 1;
-    }
-
     qemu_progress_init(progress, 2.0);
     qemu_progress_print(0, 100);
 
@@ -3971,14 +3874,9 @@ static int img_resize(int argc, char **argv)
         case 'q':
             quiet = true;
             break;
-        case OPTION_OBJECT: {
-            QemuOpts *opts;
-            opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                           optarg, true);
-            if (!opts) {
-                return 1;
-            }
-        }   break;
+        case OPTION_OBJECT:
+            qemu_img_object_parse(optarg, 1);
+            break;
         case OPTION_IMAGE_OPTS:
             image_opts = true;
             break;
@@ -4000,12 +3898,6 @@ static int img_resize(int argc, char **argv)
     }
     filename = argv[optind++];
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        return 1;
-    }
-
     /* Choose grow, shrink, or absolute resize mode */
     switch (size[0]) {
     case '+':
@@ -4185,12 +4077,7 @@ static int img_amend(int argc, char **argv)
             quiet = true;
             break;
         case OPTION_OBJECT:
-            opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                           optarg, true);
-            if (!opts) {
-                ret = -1;
-                goto out_no_progress;
-            }
+            qemu_img_object_parse(optarg, 1);
             break;
         case OPTION_IMAGE_OPTS:
             image_opts = true;
@@ -4205,13 +4092,6 @@ static int img_amend(int argc, char **argv)
         error_exit("Must specify options (-o)");
     }
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        ret = -1;
-        goto out_no_progress;
-    }
-
     if (quiet) {
         progress = false;
     }
@@ -4670,7 +4550,6 @@ static int img_bitmap(int argc, char **argv)
 {
     Error *err = NULL;
     int c, ret = 1;
-    QemuOpts *opts = NULL;
     const char *fmt = NULL, *src_fmt = NULL, *src_filename = NULL;
     const char *filename, *bitmap;
     BlockBackend *blk = NULL, *src = NULL;
@@ -4764,10 +4643,7 @@ static int img_bitmap(int argc, char **argv)
             merge = true;
             break;
         case OPTION_OBJECT:
-            opts = qemu_opts_parse_noisily(&qemu_object_opts, optarg, true);
-            if (!opts) {
-                goto out;
-            }
+            qemu_img_object_parse(optarg, 1);
             break;
         case OPTION_IMAGE_OPTS:
             image_opts = true;
@@ -4775,12 +4651,6 @@ static int img_bitmap(int argc, char **argv)
         }
     }
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        goto out;
-    }
-
     if (QSIMPLEQ_EMPTY(&actions)) {
         error_report("Need at least one of --add, --remove, --clear, "
                      "--enable, --disable, or --merge");
@@ -4876,7 +4746,6 @@ static int img_bitmap(int argc, char **argv)
  out:
     blk_unref(src);
     blk_unref(blk);
-    qemu_opts_del(opts);
     return ret;
 }
 
@@ -5038,10 +4907,7 @@ static int img_dd(int argc, char **argv)
             force_share = true;
             break;
         case OPTION_OBJECT:
-            if (!qemu_opts_parse_noisily(&qemu_object_opts, optarg, true)) {
-                ret = -1;
-                goto out;
-            }
+            qemu_img_object_parse(optarg, 1);
             break;
         case OPTION_IMAGE_OPTS:
             image_opts = true;
@@ -5088,13 +4954,6 @@ static int img_dd(int argc, char **argv)
         goto out;
     }
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        ret = -1;
-        goto out;
-    }
-
     blk1 = img_open(image_opts, in.filename, fmt, 0, false, false,
                     force_share);
 
@@ -5270,7 +5129,6 @@ static int img_measure(int argc, char **argv)
     char *snapshot_name = NULL;
     bool force_share = false;
     QemuOpts *opts = NULL;
-    QemuOpts *object_opts = NULL;
     QemuOpts *sn_opts = NULL;
     QemuOptsList *create_opts = NULL;
     bool image_opts = false;
@@ -5315,11 +5173,7 @@ static int img_measure(int argc, char **argv)
             force_share = true;
             break;
         case OPTION_OBJECT:
-            object_opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                                  optarg, true);
-            if (!object_opts) {
-                goto out;
-            }
+            qemu_img_object_parse(optarg, 1);
             break;
         case OPTION_IMAGE_OPTS:
             image_opts = true;
@@ -5349,12 +5203,6 @@ static int img_measure(int argc, char **argv)
         }
     }
 
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          qemu_img_object_print_help, &error_fatal)) {
-        goto out;
-    }
-
     if (argc - optind > 1) {
         error_report("At most one filename argument is allowed.");
         goto out;
@@ -5442,7 +5290,6 @@ static int img_measure(int argc, char **argv)
 
 out:
     qapi_free_BlockMeasureInfo(info);
-    qemu_opts_del(object_opts);
     qemu_opts_del(opts);
     qemu_opts_del(sn_opts);
     qemu_opts_free(create_opts);
@@ -5494,7 +5341,6 @@ int main(int argc, char **argv)
         error_exit("Not enough arguments");
     }
 
-    qemu_add_opts(&qemu_object_opts);
     qemu_add_opts(&qemu_source_opts);
     qemu_add_opts(&qemu_trace_opts);
 
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 20/25] qemu: use keyval for -object parsing
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (18 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 19/25] qemu-img: " Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-18 16:31 ` [PATCH 21/25] storage-daemon: do not register the "object" group with QemuOpts Paolo Bonzini
                   ` (5 subsequent siblings)
  25 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/qom/object_interfaces.h | 65 +---------------------------
 qom/object_interfaces.c         | 75 ---------------------------------
 softmmu/vl.c                    | 75 +++++++++++++++++++++------------
 3 files changed, 48 insertions(+), 167 deletions(-)

diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
index ed0d7d663b..1caa4fca57 100644
--- a/include/qom/object_interfaces.h
+++ b/include/qom/object_interfaces.h
@@ -104,51 +104,6 @@ Object *user_creatable_add_type(const char *type, const char *id,
  */
 bool user_creatable_add_dict(const QDict *qdict, bool keyval, Error **errp);
 
-/**
- * user_creatable_add_opts:
- * @opts: the object definition
- * @errp: if an error occurs, a pointer to an area to store the error
- *
- * Create an instance of the user creatable object whose type
- * is defined in @opts by the 'qom-type' option, placing it
- * in the object composition tree with name provided by the
- * 'id' field. The remaining options in @opts are used to
- * initialize the object properties.
- *
- * Returns: the newly created object or NULL on error
- */
-Object *user_creatable_add_opts(QemuOpts *opts, Error **errp);
-
-
-/**
- * user_creatable_add_opts_predicate:
- * @type: the QOM type to be added
- *
- * A callback function to determine whether an object
- * of type @type should be created. Instances of this
- * callback should be passed to user_creatable_add_opts_foreach
- */
-typedef bool (*user_creatable_add_opts_predicate)(const char *type);
-
-/**
- * user_creatable_add_opts_foreach:
- * @opaque: a user_creatable_add_opts_predicate callback or NULL
- * @opts: options to create
- * @errp: unused
- *
- * An iterator callback to be used in conjunction with
- * the qemu_opts_foreach() method for creating a list of
- * objects from a set of QemuOpts
- *
- * The @opaque parameter can be passed a user_creatable_add_opts_predicate
- * callback to filter which types of object are created during iteration.
- * When it fails, report the error.
- *
- * Returns: 0 on success, -1 when an error was reported.
- */
-int user_creatable_add_opts_foreach(void *opaque,
-                                    QemuOpts *opts, Error **errp);
-
 /**
  * user_creatable_print_types:
  *
@@ -156,30 +111,12 @@ int user_creatable_add_opts_foreach(void *opaque,
  */
 void user_creatable_print_types(void);
 
-/**
- * user_creatable_print_help:
- * @type: the QOM type to be added
- * @opts: options to create
- *
- * Prints help if requested in @type or @opts. Note that if @type is neither
- * "help"/"?" nor a valid user creatable type, no help will be printed
- * regardless of @opts.
- *
- * Returns: true if a help option was found and help was printed, false
- * otherwise.
- */
-bool user_creatable_print_help(const char *type, QemuOpts *opts);
-
 /**
  * user_creatable_print_help_from_qdict:
  * @args: options to create
  *
  * Prints help considering the other options given in @args (if "qom-type" is
- * given and valid, print properties for the type, otherwise print valid types)
- *
- * In contrast to user_creatable_print_help(), this function can't return that
- * no help was requested. It should only be called if we know that help is
- * requested and it will always print some help.
+ * given and valid, print properties for the type, otherwise print valid types).
  */
 void user_creatable_print_help_from_qdict(const QDict *args);
 
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index fbbf5e8ad3..654f717431 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -140,60 +140,6 @@ out:
     return !!obj;
 }
 
-Object *user_creatable_add_opts(QemuOpts *opts, Error **errp)
-{
-    Visitor *v;
-    QDict *pdict;
-    Object *obj;
-    const char *id = qemu_opts_id(opts);
-    char *type = qemu_opt_get_del(opts, "qom-type");
-
-    if (!type) {
-        error_setg(errp, QERR_MISSING_PARAMETER, "qom-type");
-        return NULL;
-    }
-    if (!id) {
-        error_setg(errp, QERR_MISSING_PARAMETER, "id");
-        qemu_opt_set(opts, "qom-type", type, &error_abort);
-        g_free(type);
-        return NULL;
-    }
-
-    qemu_opts_set_id(opts, NULL);
-    pdict = qemu_opts_to_qdict(opts, NULL);
-
-    v = opts_visitor_new(opts);
-    obj = user_creatable_add_type(type, id, pdict, v, errp);
-    visit_free(v);
-
-    qemu_opts_set_id(opts, (char *) id);
-    qemu_opt_set(opts, "qom-type", type, &error_abort);
-    g_free(type);
-    qobject_unref(pdict);
-    return obj;
-}
-
-
-int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp)
-{
-    bool (*type_opt_predicate)(const char *, QemuOpts *) = opaque;
-    Object *obj = NULL;
-    const char *type;
-
-    type = qemu_opt_get(opts, "qom-type");
-    if (type && type_opt_predicate &&
-        !type_opt_predicate(type, opts)) {
-        return 0;
-    }
-
-    obj = user_creatable_add_opts(opts, errp);
-    if (!obj) {
-        return -1;
-    }
-    object_unref(obj);
-    return 0;
-}
-
 char *object_property_help(const char *name, const char *type,
                            QObject *defval, const char *description)
 {
@@ -269,20 +215,6 @@ static bool user_creatable_print_type_properites(const char *type)
     return true;
 }
 
-bool user_creatable_print_help(const char *type, QemuOpts *opts)
-{
-    if (is_help_option(type)) {
-        user_creatable_print_types();
-        return true;
-    }
-
-    if (qemu_opt_has_help_opt(opts)) {
-        return user_creatable_print_type_properites(type);
-    }
-
-    return false;
-}
-
 void user_creatable_print_help_from_qdict(const QDict *args)
 {
     const char *type = qdict_get_try_str(args, "qom-type");
@@ -309,13 +241,6 @@ bool user_creatable_del(const char *id, Error **errp)
         return false;
     }
 
-    /*
-     * if object was defined on the command-line, remove its corresponding
-     * option group entry
-     */
-    qemu_opts_del(qemu_opts_find(qemu_find_opts_err("object", &error_abort),
-                                 id));
-
     object_unparent(obj);
     return true;
 }
diff --git a/softmmu/vl.c b/softmmu/vl.c
index ce0693cdda..f8b28618c9 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -136,6 +136,7 @@ static const char *cpu_option;
 static const char *mem_path;
 static const char *incoming;
 static const char *loadvm;
+static GSList *object_opts_list = NULL;
 static ram_addr_t maxram_size;
 static uint64_t ram_slots;
 static int display_remote;
@@ -308,15 +309,6 @@ static QemuOptsList qemu_add_fd_opts = {
     },
 };
 
-static QemuOptsList qemu_object_opts = {
-    .name = "object",
-    .implied_opt_name = "qom-type",
-    .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
-    .desc = {
-        { }
-    },
-};
-
 static QemuOptsList qemu_tpmdev_opts = {
     .name = "tpmdev",
     .implied_opt_name = "type",
@@ -1687,12 +1679,8 @@ static int machine_set_property(void *opaque,
  * cannot be created here, as it depends on the chardev
  * already existing.
  */
-static bool object_create_early(const char *type, QemuOpts *opts)
+static bool object_create_early(const char *type)
 {
-    if (user_creatable_print_help(type, opts)) {
-        exit(0);
-    }
-
     /*
      * Objects should not be made "delayed" without a reason.  If you
      * add one, state the reason in a comment!
@@ -1784,6 +1772,22 @@ static void qemu_apply_machine_options(void)
     }
 }
 
+static void user_creatable_add_dict_foreach(void *data, void *opaque)
+{
+    bool (*type_opt_predicate)(const char *) = opaque;
+    const QDict *dict = data;
+    const char *type = qdict_get_try_str(dict, "qom-type");
+
+    if (!type) {
+        error_report("Parameter 'qom-type' is missing");
+    }
+    if (type_opt_predicate && !type_opt_predicate(type)) {
+        return;
+    }
+
+    user_creatable_add_dict(dict, true, &error_fatal);
+}
+
 static void qemu_create_early_backends(void)
 {
     MachineClass *machine_class = MACHINE_GET_CLASS(current_machine);
@@ -1810,9 +1814,9 @@ static void qemu_create_early_backends(void)
         exit(1);
     }
 
-    qemu_opts_foreach(qemu_find_opts("object"),
-                      user_creatable_add_opts_foreach,
-                      object_create_early, &error_fatal);
+    g_slist_foreach(object_opts_list,
+                    user_creatable_add_dict_foreach,
+                    object_create_early);
 
     /* spice needs the timers to be initialized by this point */
     /* spice must initialize before audio as it changes the default auiodev */
@@ -1841,9 +1845,9 @@ static void qemu_create_early_backends(void)
  * The remainder of object creation happens after the
  * creation of chardev, fsdev, net clients and device data types.
  */
-static bool object_create_late(const char *type, QemuOpts *opts)
+static bool object_create_late(const char *type)
 {
-    return !object_create_early(type, opts);
+    return !object_create_early(type);
 }
 
 static void qemu_create_late_backends(void)
@@ -1854,9 +1858,9 @@ static void qemu_create_late_backends(void)
 
     net_init_clients(&error_fatal);
 
-    qemu_opts_foreach(qemu_find_opts("object"),
-                      user_creatable_add_opts_foreach,
-                      object_create_late, &error_fatal);
+    g_slist_foreach(object_opts_list,
+                    user_creatable_add_dict_foreach,
+                    object_create_late);
 
     if (tpm_init() < 0) {
         exit(1);
@@ -2063,6 +2067,9 @@ static int global_init_func(void *opaque, QemuOpts *opts, Error **errp)
  */
 static bool is_qemuopts_group(const char *group)
 {
+    if (g_str_equal(group, "object")) {
+        return false;
+    }
     return true;
 }
 
@@ -2072,6 +2079,9 @@ static bool is_qemuopts_group(const char *group)
  */
 static GSList **qemu_config_list(const char *group)
 {
+    if (g_str_equal(group, "object")) {
+        return &object_opts_list;
+    }
     return NULL;
 }
 
@@ -2679,7 +2689,6 @@ void qemu_init(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_smp_opts);
     qemu_add_opts(&qemu_boot_opts);
     qemu_add_opts(&qemu_add_fd_opts);
-    qemu_add_opts(&qemu_object_opts);
     qemu_add_opts(&qemu_tpmdev_opts);
     qemu_add_opts(&qemu_overcommit_opts);
     qemu_add_opts(&qemu_msg_opts);
@@ -3455,12 +3464,18 @@ void qemu_init(int argc, char **argv, char **envp)
 #endif
                 break;
             case QEMU_OPTION_object:
-                opts = qemu_opts_parse_noisily(qemu_find_opts("object"),
-                                               optarg, true);
-                if (!opts) {
-                    exit(1);
+                {
+                    QDict *args;
+                    bool help;
+
+                    args = keyval_parse(optarg, "qom-type", &help, &error_fatal);
+                    if (help) {
+                        user_creatable_print_help_from_qdict(args);
+                        exit(EXIT_SUCCESS);
+                    }
+                    qemu_record_config_group("object", args, &error_abort);
+                    break;
                 }
-                break;
             case QEMU_OPTION_overcommit:
                 opts = qemu_opts_parse_noisily(qemu_find_opts("overcommit"),
                                                optarg, false);
@@ -3504,6 +3519,10 @@ void qemu_init(int argc, char **argv, char **envp)
             }
         }
     }
+
+    /* Cleanup after option parsing loop.  */
+    object_opts_list = g_slist_reverse(object_opts_list);
+
     /*
      * Clear error location left behind by the loop.
      * Best done right after the loop.  Do not insert code here!
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 21/25] storage-daemon: do not register the "object" group with QemuOpts
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (19 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 20/25] qemu: " Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-18 16:31 ` [PATCH 22/25] qom: export more functions for use with non-UserCreatable objects Paolo Bonzini
                   ` (4 subsequent siblings)
  25 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Since qsd uses keyval instead of -object, do not bother
registering qemu_object_opts.  It used to be necessary
for object-del; that requirement is not there anymore
since emulators have switched to keyval.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 storage-daemon/qemu-storage-daemon.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c
index e0c87edbdd..8e891d56f5 100644
--- a/storage-daemon/qemu-storage-daemon.c
+++ b/storage-daemon/qemu-storage-daemon.c
@@ -130,15 +130,6 @@ enum {
 
 extern QemuOptsList qemu_chardev_opts;
 
-static QemuOptsList qemu_object_opts = {
-    .name = "object",
-    .implied_opt_name = "qom-type",
-    .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
-    .desc = {
-        { }
-    },
-};
-
 static void init_qmp_commands(void)
 {
     qmp_init_marshal(&qmp_commands);
@@ -295,7 +286,6 @@ int main(int argc, char *argv[])
 
     module_call_init(MODULE_INIT_QOM);
     module_call_init(MODULE_INIT_TRACE);
-    qemu_add_opts(&qemu_object_opts);
     qemu_add_opts(&qemu_trace_opts);
     qcrypto_init(&error_fatal);
     bdrv_init();
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 22/25] qom: export more functions for use with non-UserCreatable objects
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (20 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 21/25] storage-daemon: do not register the "object" group with QemuOpts Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-18 16:31 ` [PATCH 23/25] vl: switch -M parsing to keyval Paolo Bonzini
                   ` (3 subsequent siblings)
  25 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Machines and accelerators are not user-creatable but they share
similar parsing machinery.  Export functions that will be used
with -machine and -accel in softmmu/vl.c.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/qom/object.h    | 21 +++++++++++++++++
 qom/object_interfaces.c | 51 +++++++++++++++++++++++++++++------------
 2 files changed, 57 insertions(+), 15 deletions(-)

diff --git a/include/qom/object.h b/include/qom/object.h
index d378f13a11..cd27facd8a 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -860,6 +860,27 @@ static void do_qemu_init_ ## type_array(void)                               \
 }                                                                           \
 type_init(do_qemu_init_ ## type_array)
 
+/**
+ * type_print_class_properties:
+ * @type: a QOM class name
+ *
+ * Print the object's class properties to stdout or the monitor.
+ * Return whether an object was found.
+ */
+bool type_print_class_properties(const char *type);
+
+/**
+ * object_set_properties_from_keyval:
+ * @obj: a QOM object
+ * @qdict: a dictionary whose leaf values are strings
+ * @errp: pointer to error object
+ *
+ * For each key in the dictionary, parse the value string and set the
+ * corresponding property in @obj.
+ */
+void object_set_properties_from_keyval(Object *obj, const QDict *qdict,
+				       Error **errp);
+
 /**
  * object_class_dynamic_cast_assert:
  * @klass: The #ObjectClass to attempt to cast.
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index 654f717431..80efea2ca9 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -38,13 +38,45 @@ bool user_creatable_can_be_deleted(UserCreatable *uc)
     }
 }
 
+static void object_set_properties_from_qdict(Object *obj, const QDict *qdict,
+                                             Visitor *v, Error **errp)
+{
+    const QDictEntry *e;
+    Error *local_err = NULL;
+
+    if (!visit_start_struct(v, NULL, NULL, 0, &local_err)) {
+        goto out;
+    }
+    for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
+        if (!object_property_set(obj, e->key, v, &local_err)) {
+            break;
+        }
+    }
+    if (!local_err) {
+        visit_check_struct(v, &local_err);
+    }
+    visit_end_struct(v, NULL);
+
+out:
+    if (local_err) {
+        error_propagate(errp, local_err);
+    }
+}
+
+void object_set_properties_from_keyval(Object *obj, const QDict *qdict,
+                                       Error **errp)
+{
+    Visitor *v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
+    object_set_properties_from_qdict(obj, qdict, v, errp);
+    visit_free(v);
+}
+
 Object *user_creatable_add_type(const char *type, const char *id,
                                 const QDict *qdict,
                                 Visitor *v, Error **errp)
 {
     Object *obj;
     ObjectClass *klass;
-    const QDictEntry *e;
     Error *local_err = NULL;
 
     klass = object_class_by_name(type);
@@ -66,18 +98,7 @@ Object *user_creatable_add_type(const char *type, const char *id,
 
     assert(qdict);
     obj = object_new(type);
-    if (!visit_start_struct(v, NULL, NULL, 0, &local_err)) {
-        goto out;
-    }
-    for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
-        if (!object_property_set(obj, e->key, v, &local_err)) {
-            break;
-        }
-    }
-    if (!local_err) {
-        visit_check_struct(v, &local_err);
-    }
-    visit_end_struct(v, NULL);
+    object_set_properties_from_qdict(obj, qdict, v, &local_err);
     if (local_err) {
         goto out;
     }
@@ -177,7 +198,7 @@ void user_creatable_print_types(void)
     g_slist_free(list);
 }
 
-static bool user_creatable_print_type_properites(const char *type)
+bool type_print_class_properties(const char *type)
 {
     ObjectClass *klass;
     ObjectPropertyIterator iter;
@@ -219,7 +240,7 @@ void user_creatable_print_help_from_qdict(const QDict *args)
 {
     const char *type = qdict_get_try_str(args, "qom-type");
 
-    if (!type || !user_creatable_print_type_properites(type)) {
+    if (!type || !type_print_class_properties(type)) {
         user_creatable_print_types();
     }
 }
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 23/25] vl: switch -M parsing to keyval
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (21 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 22/25] qom: export more functions for use with non-UserCreatable objects Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-18 16:31 ` [PATCH 24/25] qemu-option: remove now-dead code Paolo Bonzini
                   ` (2 subsequent siblings)
  25 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Switch from QemuOpts to keyval.  This enables non-scalar
machine properties.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 softmmu/vl.c | 296 +++++++++++++++++++++++----------------------------
 1 file changed, 132 insertions(+), 164 deletions(-)

diff --git a/softmmu/vl.c b/softmmu/vl.c
index f8b28618c9..ff906d541d 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -136,6 +136,8 @@ static const char *cpu_option;
 static const char *mem_path;
 static const char *incoming;
 static const char *loadvm;
+static const char *accelerators;
+static QDict *machine_opts_dict;
 static GSList *object_opts_list = NULL;
 static ram_addr_t maxram_size;
 static uint64_t ram_slots;
@@ -227,21 +229,6 @@ static QemuOptsList qemu_option_rom_opts = {
     },
 };
 
-static QemuOptsList qemu_machine_opts = {
-    .name = "machine",
-    .implied_opt_name = "type",
-    .merge_lists = true,
-    .head = QTAILQ_HEAD_INITIALIZER(qemu_machine_opts.head),
-    .desc = {
-        /*
-         * no elements => accept any
-         * sanity checking will happen later
-         * when setting machine properties
-         */
-        { }
-    },
-};
-
 static QemuOptsList qemu_accel_opts = {
     .name = "accel",
     .implied_opt_name = "accel",
@@ -481,16 +468,6 @@ static QemuOptsList qemu_action_opts = {
     },
 };
 
-/**
- * Get machine options
- *
- * Returns: machine options (never null).
- */
-static QemuOpts *qemu_get_machine_opts(void)
-{
-    return qemu_find_opts_singleton("machine");
-}
-
 const char *qemu_get_vm_name(void)
 {
     return qemu_name;
@@ -798,33 +775,6 @@ static MachineClass *find_default_machine(GSList *machines)
     return default_machineclass;
 }
 
-static int machine_help_func(QemuOpts *opts, MachineState *machine)
-{
-    ObjectProperty *prop;
-    ObjectPropertyIterator iter;
-
-    if (!qemu_opt_has_help_opt(opts)) {
-        return 0;
-    }
-
-    object_property_iter_init(&iter, OBJECT(machine));
-    while ((prop = object_property_iter_next(&iter))) {
-        if (!prop->set) {
-            continue;
-        }
-
-        printf("%s.%s=%s", MACHINE_GET_CLASS(machine)->name,
-               prop->name, prop->type);
-        if (prop->description) {
-            printf(" (%s)\n", prop->description);
-        } else {
-            printf("\n");
-        }
-    }
-
-    return 1;
-}
-
 static void version(void)
 {
     printf("QEMU emulator version " QEMU_FULL_VERSION "\n"
@@ -1515,33 +1465,28 @@ static gint machine_class_cmp(gconstpointer a, gconstpointer b)
                   object_class_get_name(OBJECT_CLASS(mc1)));
 }
 
-static MachineClass *machine_parse(const char *name, GSList *machines)
+static void machine_help_func(const QDict *qdict)
 {
-    MachineClass *mc;
-    GSList *el;
+    GSList *machines, *el;
+    const char *type = qdict_get_try_str(qdict, "type");
 
-    if (is_help_option(name)) {
-        printf("Supported machines are:\n");
-        machines = g_slist_sort(machines, machine_class_cmp);
-        for (el = machines; el; el = el->next) {
-            MachineClass *mc = el->data;
-            if (mc->alias) {
-                printf("%-20s %s (alias of %s)\n", mc->alias, mc->desc, mc->name);
-            }
-            printf("%-20s %s%s%s\n", mc->name, mc->desc,
-                   mc->is_default ? " (default)" : "",
-                   mc->deprecation_reason ? " (deprecated)" : "");
-        }
-        exit(0);
+    if (type) {
+        type_print_class_properties(type);
+        return;
     }
 
-    mc = find_machine(name, machines);
-    if (!mc) {
-        error_report("unsupported machine type");
-        error_printf("Use -machine help to list supported machines\n");
-        exit(1);
+    printf("Supported machines are:\n");
+    machines = object_class_get_list(TYPE_MACHINE, false);
+    machines = g_slist_sort(machines, machine_class_cmp);
+    for (el = machines; el; el = el->next) {
+        MachineClass *mc = el->data;
+        if (mc->alias) {
+            printf("%-20s %s (alias of %s)\n", mc->alias, mc->desc, mc->name);
+        }
+        printf("%-20s %s%s%s\n", mc->name, mc->desc,
+               mc->is_default ? " (default)" : "",
+               mc->deprecation_reason ? " (deprecated)" : "");
     }
-    return mc;
 }
 
 static const char *pid_file;
@@ -1594,32 +1539,31 @@ static const QEMUOption *lookup_opt(int argc, char **argv,
     return popt;
 }
 
-static MachineClass *select_machine(void)
+static MachineClass *select_machine(QDict *qdict, Error **errp)
 {
+    const char *optarg = qdict_get_try_str(qdict, "type");
     GSList *machines = object_class_get_list(TYPE_MACHINE, false);
-    MachineClass *machine_class = find_default_machine(machines);
-    const char *optarg;
-    QemuOpts *opts;
-    Location loc;
-
-    loc_push_none(&loc);
-
-    opts = qemu_get_machine_opts();
-    qemu_opts_loc_restore(opts);
+    MachineClass *machine_class;
+    Error *local_err = NULL;
 
-    optarg = qemu_opt_get(opts, "type");
     if (optarg) {
-        machine_class = machine_parse(optarg, machines);
-    }
-
-    if (!machine_class) {
-        error_report("No machine specified, and there is no default");
-        error_printf("Use -machine help to list supported machines\n");
-        exit(1);
+        machine_class = find_machine(optarg, machines);
+        qdict_del(qdict, "type");
+        if (!machine_class) {
+            error_setg(&local_err, "unsupported machine type");
+        }
+    } else {
+        machine_class = find_default_machine(machines);
+        if (!machine_class) {
+            error_setg(&local_err, "No machine specified, and there is no default");
+        }
     }
 
-    loc_pop(&loc);
     g_slist_free(machines);
+    if (local_err) {
+        error_append_hint(&local_err, "Use -machine help to list supported machines\n");
+        error_propagate(errp, local_err);
+    }
     return machine_class;
 }
 
@@ -1638,38 +1582,66 @@ static int object_parse_property_opt(Object *obj,
     return 0;
 }
 
-static int machine_set_property(void *opaque,
-                                const char *name, const char *value,
-                                Error **errp)
+/* *Non*recursively replace underscores with dashes in QDict keys.  */
+static void keyval_dashify(QDict *qdict, Error **errp)
 {
-    g_autofree char *qom_name = g_strdup(name);
+    const QDictEntry *ent, *next;
     char *p;
 
-    for (p = qom_name; *p; p++) {
-        if (*p == '_') {
-            *p = '-';
+    for (ent = qdict_first(qdict); ent; ent = next) {
+        g_autofree char *new_key = NULL;
+
+        next = qdict_next(qdict, ent);
+        if (!strchr(ent->key, '_')) {
+            continue;
+        }
+        new_key = g_strdup(ent->key);
+        for (p = new_key; *p; p++) {
+            if (*p == '_') {
+                *p = '-';
+            }
         }
+        if (qdict_haskey(qdict, new_key)) {
+            error_setg(errp, "Conflict between '%s' and '%s'", ent->key, new_key);
+            return;
+        }
+        qobject_ref(ent->value);
+        qdict_put_obj(qdict, new_key, ent->value);
+        qdict_del(qdict, ent->key);
     }
+}
+
+static void qemu_apply_legacy_machine_options(QDict *qdict)
+{
+    const char *value;
+
+    keyval_dashify(qdict, &error_fatal);
 
     /* Legacy options do not correspond to MachineState properties.  */
-    if (g_str_equal(qom_name, "accel")) {
-        return 0;
-    }
-    if (g_str_equal(qom_name, "igd-passthru")) {
-        object_register_sugar_prop(ACCEL_CLASS_NAME("xen"), qom_name, value);
-        return 0;
+    value = qdict_get_try_str(qdict, "accel");
+    if (value) {
+        accelerators = g_strdup(value);
+        qdict_del(qdict, "accel");
     }
-    if (g_str_equal(qom_name, "kvm-shadow-mem")) {
-        object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), qom_name, value);
-        return 0;
+
+    value = qdict_get_try_str(qdict, "igd-passthru");
+    if (value) {
+        object_register_sugar_prop(ACCEL_CLASS_NAME("xen"), "igd-passthru", value);
+        qdict_del(qdict, "igd-passthru");
     }
-    if (g_str_equal(qom_name, "kernel-irqchip")) {
-        object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), qom_name, value);
-        object_register_sugar_prop(ACCEL_CLASS_NAME("whpx"), qom_name, value);
-        return 0;
+
+    value = qdict_get_try_str(qdict, "kvm-shadow-mem");
+    if (value) {
+        object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), "kvm-shadow-mem", value);
+        qdict_del(qdict, "kvm-shadow-mem");
     }
 
-    return object_parse_property_opt(opaque, name, value, "type", errp);
+    value = qdict_get_try_str(qdict, "kernel-irqchip");
+    if (value) {
+        object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), "kernel-irqchip", value);
+        object_register_sugar_prop(ACCEL_CLASS_NAME("whpx"), "kernel-irqchip", value);
+        qdict_del(qdict, "kernel-irqchip");
+    }
 }
 
 /*
@@ -1727,16 +1699,14 @@ static bool object_create_early(const char *type)
     return true;
 }
 
-static void qemu_apply_machine_options(void)
+static void qemu_apply_machine_options(QDict *qdict)
 {
     MachineClass *machine_class = MACHINE_GET_CLASS(current_machine);
-    QemuOpts *machine_opts = qemu_get_machine_opts();
     const char *boot_order = NULL;
     const char *boot_once = NULL;
     QemuOpts *opts;
 
-    qemu_opt_foreach(machine_opts, machine_set_property, current_machine,
-                     &error_fatal);
+    object_set_properties_from_keyval(OBJECT(current_machine), qdict, &error_fatal);
     current_machine->ram_size = ram_size;
     current_machine->maxram_size = maxram_size;
     current_machine->ram_slots = ram_slots;
@@ -1765,10 +1735,8 @@ static void qemu_apply_machine_options(void)
     current_machine->boot_once = boot_once;
 
     if (semihosting_enabled() && !semihosting_get_argc()) {
-        const char *kernel_filename = qemu_opt_get(machine_opts, "kernel");
-        const char *kernel_cmdline = qemu_opt_get(machine_opts, "append") ?: "";
         /* fall back to the -kernel/-append */
-        semihosting_arg_fallback(kernel_filename, kernel_cmdline);
+        semihosting_arg_fallback(current_machine->kernel_filename, current_machine->kernel_cmdline);
     }
 }
 
@@ -1833,8 +1801,7 @@ static void qemu_create_early_backends(void)
 
     /*
      * Note: we need to create audio and block backends before
-     * machine_set_property(), so machine properties can refer to
-     * them.
+     * setting machine properties, so they can be referred to.
      */
     configure_blockdev(&bdo_queue, machine_class, snapshot);
     audio_init_audiodevs();
@@ -2004,16 +1971,14 @@ static void set_memory_options(MachineClass *mc)
     loc_pop(&loc);
 }
 
-static void qemu_create_machine(MachineClass *machine_class)
+static void qemu_create_machine(QDict *qdict)
 {
+    MachineClass *machine_class = select_machine(qdict, &error_fatal);
     object_set_machine_compat_props(machine_class->compat_props);
 
     set_memory_options(machine_class);
 
     current_machine = MACHINE(object_new_with_class(OBJECT_CLASS(machine_class)));
-    if (machine_help_func(qemu_get_machine_opts(), current_machine)) {
-        exit(0);
-    }
     object_property_add_child(object_get_root(), "machine",
                               OBJECT(current_machine));
     object_property_add_child(container_get(OBJECT(current_machine),
@@ -2044,8 +2009,11 @@ static void qemu_create_machine(MachineClass *machine_class)
      * specified either by the configuration file or by the command line.
      */
     if (machine_class->default_machine_opts) {
-        qemu_opts_set_defaults(qemu_find_opts("machine"),
-                               machine_class->default_machine_opts, 0);
+        QDict *default_opts =
+            keyval_parse(machine_class->default_machine_opts, NULL, NULL,
+                         &error_abort);
+        object_set_properties_from_keyval(OBJECT(current_machine), default_opts, &error_abort);
+        qobject_unref(default_opts);
     }
 }
 
@@ -2067,7 +2035,8 @@ static int global_init_func(void *opaque, QemuOpts *opts, Error **errp)
  */
 static bool is_qemuopts_group(const char *group)
 {
-    if (g_str_equal(group, "object")) {
+    if (g_str_equal(group, "object") ||
+        g_str_equal(group, "machine")) {
         return false;
     }
     return true;
@@ -2109,6 +2078,8 @@ static void qemu_record_config_group(const char *group, QDict *dict, Error **err
     p_head = qemu_config_list(group);
     if (p_head) {
         *p_head = g_slist_prepend(*p_head, dict);
+    } else if (g_str_equal(group, "machine")) {
+        keyval_merge(machine_opts_dict, dict, errp);
     } else {
         abort();
     }
@@ -2262,13 +2233,11 @@ static int do_configure_accelerator(void *opaque, QemuOpts *opts, Error **errp)
 
 static void configure_accelerators(const char *progname)
 {
-    const char *accelerators;
     bool init_failed = false;
 
     qemu_opts_foreach(qemu_find_opts("icount"),
                       do_configure_icount, NULL, &error_fatal);
 
-    accelerators = qemu_opt_get(qemu_get_machine_opts(), "accel");
     if (QTAILQ_EMPTY(&qemu_accel_opts.head)) {
         char **accel_list, **tmp;
 
@@ -2356,12 +2325,11 @@ static void create_default_memdev(MachineState *ms, const char *path)
                             &error_fatal);
 }
 
-static void qemu_validate_options(void)
+static void qemu_validate_options(const QDict *machine_opts)
 {
-    QemuOpts *machine_opts = qemu_get_machine_opts();
-    const char *kernel_filename = qemu_opt_get(machine_opts, "kernel");
-    const char *initrd_filename = qemu_opt_get(machine_opts, "initrd");
-    const char *kernel_cmdline = qemu_opt_get(machine_opts, "append");
+    const char *kernel_filename = qdict_get_try_str(machine_opts, "kernel");
+    const char *initrd_filename = qdict_get_try_str(machine_opts, "initrd");
+    const char *kernel_cmdline = qdict_get_try_str(machine_opts, "append");
 
     if (kernel_filename == NULL) {
          if (kernel_cmdline != NULL) {
@@ -2683,7 +2651,6 @@ void qemu_init(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_trace_opts);
     qemu_plugin_add_opts();
     qemu_add_opts(&qemu_option_rom_opts);
-    qemu_add_opts(&qemu_machine_opts);
     qemu_add_opts(&qemu_accel_opts);
     qemu_add_opts(&qemu_mem_opts);
     qemu_add_opts(&qemu_smp_opts);
@@ -2723,6 +2690,7 @@ void qemu_init(int argc, char **argv, char **envp)
         }
     }
 
+    machine_opts_dict = qdict_new();
     if (userconfig) {
         qemu_read_default_config_file(&error_fatal);
     }
@@ -2812,8 +2780,7 @@ void qemu_init(int argc, char **argv, char **envp)
                 parse_display(optarg);
                 break;
             case QEMU_OPTION_nographic:
-                olist = qemu_find_opts("machine");
-                qemu_opts_parse_noisily(olist, "graphics=off", false);
+                qdict_put_str(machine_opts_dict, "graphics", "off");
                 nographic = true;
                 dpy.type = DISPLAY_TYPE_NONE;
                 break;
@@ -2837,16 +2804,16 @@ void qemu_init(int argc, char **argv, char **envp)
                 }
                 break;
             case QEMU_OPTION_kernel:
-                qemu_opts_set(qemu_find_opts("machine"), "kernel", optarg, &error_abort);
+                qdict_put_str(machine_opts_dict, "kernel", optarg);
                 break;
             case QEMU_OPTION_initrd:
-                qemu_opts_set(qemu_find_opts("machine"), "initrd", optarg, &error_abort);
+                qdict_put_str(machine_opts_dict, "initrd", optarg);
                 break;
             case QEMU_OPTION_append:
-                qemu_opts_set(qemu_find_opts("machine"), "append", optarg, &error_abort);
+                qdict_put_str(machine_opts_dict, "append", optarg);
                 break;
             case QEMU_OPTION_dtb:
-                qemu_opts_set(qemu_find_opts("machine"), "dtb", optarg, &error_abort);
+                qdict_put_str(machine_opts_dict, "dtb", optarg);
                 break;
             case QEMU_OPTION_cdrom:
                 drive_add(IF_DEFAULT, 2, optarg, CDROM_OPTS);
@@ -2956,7 +2923,7 @@ void qemu_init(int argc, char **argv, char **envp)
                 }
                 break;
             case QEMU_OPTION_bios:
-                qemu_opts_set(qemu_find_opts("machine"), "firmware", optarg, &error_abort);
+                qdict_put_str(machine_opts_dict, "firmware", optarg);
                 break;
             case QEMU_OPTION_singlestep:
                 singlestep = 1;
@@ -3225,17 +3192,20 @@ void qemu_init(int argc, char **argv, char **envp)
                 preconfig_requested = true;
                 break;
             case QEMU_OPTION_enable_kvm:
-                olist = qemu_find_opts("machine");
-                qemu_opts_parse_noisily(olist, "accel=kvm", false);
+                qdict_put_str(machine_opts_dict, "accel", "kvm");
                 break;
             case QEMU_OPTION_M:
             case QEMU_OPTION_machine:
-                olist = qemu_find_opts("machine");
-                opts = qemu_opts_parse_noisily(olist, optarg, true);
-                if (!opts) {
-                    exit(1);
+                {
+                    bool help;
+
+                    keyval_parse_into(machine_opts_dict, optarg, "type", &help, &error_fatal);
+                    if (help) {
+                        machine_help_func(machine_opts_dict);
+                        exit(EXIT_SUCCESS);
+                    }
+                    break;
                 }
-                break;
             case QEMU_OPTION_accel:
                 accel_opts = qemu_opts_parse_noisily(qemu_find_opts("accel"),
                                                      optarg, true);
@@ -3262,14 +3232,12 @@ void qemu_init(int argc, char **argv, char **envp)
                 }
                 break;
             case QEMU_OPTION_usb:
-                olist = qemu_find_opts("machine");
-                qemu_opts_parse_noisily(olist, "usb=on", false);
+                qdict_put_str(machine_opts_dict, "usb", "on");
                 break;
             case QEMU_OPTION_usbdevice:
                 error_report("'-usbdevice' is deprecated, please use "
                              "'-device usb-...' instead");
-                olist = qemu_find_opts("machine");
-                qemu_opts_parse_noisily(olist, "usb=on", false);
+                qdict_put_str(machine_opts_dict, "usb", "on");
                 add_device_config(DEV_USB, optarg);
                 break;
             case QEMU_OPTION_device:
@@ -3288,12 +3256,10 @@ void qemu_init(int argc, char **argv, char **envp)
                 vnc_parse(optarg, &error_fatal);
                 break;
             case QEMU_OPTION_no_acpi:
-                olist = qemu_find_opts("machine");
-                qemu_opts_parse_noisily(olist, "acpi=off", false);
+                qdict_put_str(machine_opts_dict, "acpi", "off");
                 break;
             case QEMU_OPTION_no_hpet:
-                olist = qemu_find_opts("machine");
-                qemu_opts_parse_noisily(olist, "hpet=off", false);
+                qdict_put_str(machine_opts_dict, "hpet", "off");
                 break;
             case QEMU_OPTION_no_reboot:
                 olist = qemu_find_opts("action");
@@ -3529,7 +3495,7 @@ void qemu_init(int argc, char **argv, char **envp)
      */
     loc_set_none();
 
-    qemu_validate_options();
+    qemu_validate_options(machine_opts_dict);
     qemu_process_sugar_options();
 
     /*
@@ -3549,7 +3515,7 @@ void qemu_init(int argc, char **argv, char **envp)
 
     configure_rtc(qemu_find_opts_singleton("rtc"));
 
-    qemu_create_machine(select_machine());
+    qemu_create_machine(machine_opts_dict);
 
     suspend_mux_open();
 
@@ -3557,12 +3523,14 @@ void qemu_init(int argc, char **argv, char **envp)
     qemu_create_default_devices();
     qemu_create_early_backends();
 
-    qemu_apply_machine_options();
+    qemu_apply_legacy_machine_options(machine_opts_dict);
+    qemu_apply_machine_options(machine_opts_dict);
+    qobject_unref(machine_opts_dict);
     phase_advance(PHASE_MACHINE_CREATED);
 
     /*
      * Note: uses machine properties such as kernel-irqchip, must run
-     * after machine_set_property().
+     * after qemu_apply_machine_options.
      */
     configure_accelerators(argv[0]);
     phase_advance(PHASE_ACCEL_CREATED);
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 24/25] qemu-option: remove now-dead code
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (22 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 23/25] vl: switch -M parsing to keyval Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-18 16:31 ` [PATCH 25/25] vl: switch -accel parsing to keyval Paolo Bonzini
  2021-01-18 17:18 ` [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing no-reply
  25 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

-M was the sole user of qemu_opts_set and qemu_opts_set_defaults,
remove them and the arguments that they used.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/qemu/option.h  |  3 ---
 tests/test-qemu-opts.c | 35 -----------------------------
 util/qemu-option.c     | 51 +++++++++---------------------------------
 3 files changed, 10 insertions(+), 79 deletions(-)

diff --git a/include/qemu/option.h b/include/qemu/option.h
index fffb03d848..306bf07575 100644
--- a/include/qemu/option.h
+++ b/include/qemu/option.h
@@ -119,7 +119,6 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
                            int fail_if_exists, Error **errp);
 void qemu_opts_reset(QemuOptsList *list);
 void qemu_opts_loc_restore(QemuOpts *opts);
-bool qemu_opts_set(QemuOptsList *list, const char *name, const char *value, Error **errp);
 const char *qemu_opts_id(QemuOpts *opts);
 void qemu_opts_set_id(QemuOpts *opts, char *id);
 void qemu_opts_del(QemuOpts *opts);
@@ -130,8 +129,6 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
                                   bool permit_abbrev);
 QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params,
                           bool permit_abbrev, Error **errp);
-void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
-                            int permit_abbrev);
 QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict,
                                Error **errp);
 QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict,
diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c
index 8bbb17b1c7..0239876e36 100644
--- a/tests/test-qemu-opts.c
+++ b/tests/test-qemu-opts.c
@@ -410,40 +410,6 @@ static void test_qemu_opts_reset(void)
     g_assert(opts == NULL);
 }
 
-static void test_qemu_opts_set(void)
-{
-    QemuOptsList *list;
-    QemuOpts *opts;
-    const char *opt;
-
-    list = qemu_find_opts("opts_list_04");
-    g_assert(list != NULL);
-    g_assert(QTAILQ_EMPTY(&list->head));
-    g_assert_cmpstr(list->name, ==, "opts_list_04");
-
-    /* should not find anything at this point */
-    opts = qemu_opts_find(list, NULL);
-    g_assert(opts == NULL);
-
-    /* implicitly create opts and set str3 value */
-    qemu_opts_set(list, "str3", "value", &error_abort);
-    g_assert(!QTAILQ_EMPTY(&list->head));
-
-    /* get the just created opts */
-    opts = qemu_opts_find(list, NULL);
-    g_assert(opts != NULL);
-
-    /* check the str3 value */
-    opt = qemu_opt_get(opts, "str3");
-    g_assert_cmpstr(opt, ==, "value");
-
-    qemu_opts_del(opts);
-
-    /* should not find anything at this point */
-    opts = qemu_opts_find(list, NULL);
-    g_assert(opts == NULL);
-}
-
 static int opts_count_iter(void *opaque, const char *name, const char *value,
                            Error **errp)
 {
@@ -1030,7 +996,6 @@ int main(int argc, char *argv[])
     g_test_add_func("/qemu-opts/opt_get_size", test_qemu_opt_get_size);
     g_test_add_func("/qemu-opts/opt_unset", test_qemu_opt_unset);
     g_test_add_func("/qemu-opts/opts_reset", test_qemu_opts_reset);
-    g_test_add_func("/qemu-opts/opts_set", test_qemu_opts_set);
     g_test_add_func("/qemu-opts/opts_parse/general", test_opts_parse);
     g_test_add_func("/qemu-opts/opts_parse/bool", test_opts_parse_bool);
     g_test_add_func("/qemu-opts/opts_parse/number", test_opts_parse_number);
diff --git a/util/qemu-option.c b/util/qemu-option.c
index afba08d92e..94a6bea767 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -479,19 +479,14 @@ int qemu_opt_unset(QemuOpts *opts, const char *name)
     }
 }
 
-static QemuOpt *opt_create(QemuOpts *opts, const char *name, char *value,
-                           bool prepend)
+static QemuOpt *opt_create(QemuOpts *opts, const char *name, char *value)
 {
     QemuOpt *opt = g_malloc0(sizeof(*opt));
 
     opt->name = g_strdup(name);
     opt->str = value;
     opt->opts = opts;
-    if (prepend) {
-        QTAILQ_INSERT_HEAD(&opts->head, opt, next);
-    } else {
-        QTAILQ_INSERT_TAIL(&opts->head, opt, next);
-    }
+    QTAILQ_INSERT_TAIL(&opts->head, opt, next);
 
     return opt;
 }
@@ -518,7 +513,7 @@ static bool opt_validate(QemuOpt *opt, Error **errp)
 bool qemu_opt_set(QemuOpts *opts, const char *name, const char *value,
                   Error **errp)
 {
-    QemuOpt *opt = opt_create(opts, name, g_strdup(value), false);
+    QemuOpt *opt = opt_create(opts, name, g_strdup(value));
 
     if (!opt_validate(opt, errp)) {
         qemu_opt_del(opt);
@@ -662,15 +657,6 @@ void qemu_opts_loc_restore(QemuOpts *opts)
     loc_restore(&opts->loc);
 }
 
-bool qemu_opts_set(QemuOptsList *list, const char *name, const char *value, Error **errp)
-{
-    QemuOpts *opts;
-
-    assert(list->merge_lists);
-    opts = qemu_opts_create(list, NULL, 0, &error_abort);
-    return qemu_opt_set(opts, name, value, errp);
-}
-
 const char *qemu_opts_id(QemuOpts *opts)
 {
     return opts->id;
@@ -807,7 +793,7 @@ static const char *get_opt_name_value(const char *params,
 }
 
 static bool opts_do_parse(QemuOpts *opts, const char *params,
-                          const char *firstname, bool prepend,
+                          const char *firstname,
                           bool warn_on_flag, bool *help_wanted, Error **errp)
 {
     char *option, *value;
@@ -829,7 +815,7 @@ static bool opts_do_parse(QemuOpts *opts, const char *params,
             continue;
         }
 
-        opt = opt_create(opts, option, value, prepend);
+        opt = opt_create(opts, option, value);
         g_free(option);
         if (!opt_validate(opt, errp)) {
             qemu_opt_del(opt);
@@ -885,11 +871,11 @@ bool has_help_option(const char *params)
 bool qemu_opts_do_parse(QemuOpts *opts, const char *params,
                        const char *firstname, Error **errp)
 {
-    return opts_do_parse(opts, params, firstname, false, false, NULL, errp);
+    return opts_do_parse(opts, params, firstname, false, NULL, errp);
 }
 
 static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
-                            bool permit_abbrev, bool defaults,
+                            bool permit_abbrev,
                             bool warn_on_flag, bool *help_wanted, Error **errp)
 {
     const char *firstname;
@@ -899,21 +885,13 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
     assert(!permit_abbrev || list->implied_opt_name);
     firstname = permit_abbrev ? list->implied_opt_name : NULL;
 
-    /*
-     * This code doesn't work for defaults && !list->merge_lists: when
-     * params has no id=, and list has an element with !opts->id, it
-     * appends a new element instead of returning the existing opts.
-     * However, we got no use for this case.  Guard against possible
-     * (if unlikely) future misuse:
-     */
-    assert(!defaults || list->merge_lists);
     opts = qemu_opts_create(list, id, !list->merge_lists, errp);
     g_free(id);
     if (opts == NULL) {
         return NULL;
     }
 
-    if (!opts_do_parse(opts, params, firstname, defaults,
+    if (!opts_do_parse(opts, params, firstname,
                        warn_on_flag, help_wanted, errp)) {
         qemu_opts_del(opts);
         return NULL;
@@ -932,7 +910,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
 QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params,
                           bool permit_abbrev, Error **errp)
 {
-    return opts_parse(list, params, permit_abbrev, false, false, NULL, errp);
+    return opts_parse(list, params, permit_abbrev, false, NULL, errp);
 }
 
 /**
@@ -950,7 +928,7 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
     QemuOpts *opts;
     bool help_wanted = false;
 
-    opts = opts_parse(list, params, permit_abbrev, false, true,
+    opts = opts_parse(list, params, permit_abbrev, true,
                       opts_accepts_any(list) ? NULL : &help_wanted,
                       &err);
     if (!opts) {
@@ -964,15 +942,6 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
     return opts;
 }
 
-void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
-                            int permit_abbrev)
-{
-    QemuOpts *opts;
-
-    opts = opts_parse(list, params, permit_abbrev, true, false, NULL, NULL);
-    assert(opts);
-}
-
 static bool qemu_opts_from_qdict_entry(QemuOpts *opts,
                                        const QDictEntry *entry,
                                        Error **errp)
-- 
2.26.2




^ permalink raw reply related	[flat|nested] 67+ messages in thread

* [PATCH 25/25] vl: switch -accel parsing to keyval
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (23 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 24/25] qemu-option: remove now-dead code Paolo Bonzini
@ 2021-01-18 16:31 ` Paolo Bonzini
  2021-01-18 17:18 ` [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing no-reply
  25 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-18 16:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, imammedo, armbru

Switch from QemuOpts to keyval.  This enables compound options
for accelerators.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 accel/accel.c          |   6 ++
 include/sysemu/accel.h |   1 +
 softmmu/vl.c           | 134 ++++++++++++++++++-----------------------
 3 files changed, 67 insertions(+), 74 deletions(-)

diff --git a/accel/accel.c b/accel/accel.c
index cb555e3b06..f7fdc2f5a8 100644
--- a/accel/accel.c
+++ b/accel/accel.c
@@ -46,6 +46,12 @@ AccelClass *accel_find(const char *opt_name)
     return ac;
 }
 
+bool accel_print_class_properties(const char *opt_name)
+{
+    g_autofree char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name);
+    return type_print_class_properties(class_name);
+}
+
 int accel_init_machine(AccelState *accel, MachineState *ms)
 {
     AccelClass *acc = ACCEL_GET_CLASS(accel);
diff --git a/include/sysemu/accel.h b/include/sysemu/accel.h
index e08b8ab8fa..737db49d21 100644
--- a/include/sysemu/accel.h
+++ b/include/sysemu/accel.h
@@ -67,6 +67,7 @@ typedef struct AccelClass {
     OBJECT_GET_CLASS(AccelClass, (obj), TYPE_ACCEL)
 
 AccelClass *accel_find(const char *opt_name);
+bool accel_print_class_properties(const char *opt_name);
 int accel_init_machine(AccelState *accel, MachineState *ms);
 
 /* Called just before os_setup_post (ie just before drop OS privs) */
diff --git a/softmmu/vl.c b/softmmu/vl.c
index ff906d541d..b87ab08ea6 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -139,6 +139,7 @@ static const char *loadvm;
 static const char *accelerators;
 static QDict *machine_opts_dict;
 static GSList *object_opts_list = NULL;
+static GSList *accel_opts_list = NULL;
 static ram_addr_t maxram_size;
 static uint64_t ram_slots;
 static int display_remote;
@@ -229,20 +230,6 @@ static QemuOptsList qemu_option_rom_opts = {
     },
 };
 
-static QemuOptsList qemu_accel_opts = {
-    .name = "accel",
-    .implied_opt_name = "accel",
-    .head = QTAILQ_HEAD_INITIALIZER(qemu_accel_opts.head),
-    .desc = {
-        /*
-         * no elements => accept any
-         * sanity checking will happen later
-         * when setting accelerator properties
-         */
-        { }
-    },
-};
-
 static QemuOptsList qemu_boot_opts = {
     .name = "boot-opts",
     .implied_opt_name = "order",
@@ -1567,21 +1554,6 @@ static MachineClass *select_machine(QDict *qdict, Error **errp)
     return machine_class;
 }
 
-static int object_parse_property_opt(Object *obj,
-                                     const char *name, const char *value,
-                                     const char *skip, Error **errp)
-{
-    if (g_str_equal(name, skip)) {
-        return 0;
-    }
-
-    if (!object_property_parse(obj, name, value, errp)) {
-        return -1;
-    }
-
-    return 0;
-}
-
 /* *Non*recursively replace underscores with dashes in QDict keys.  */
 static void keyval_dashify(QDict *qdict, Error **errp)
 {
@@ -2036,7 +2008,8 @@ static int global_init_func(void *opaque, QemuOpts *opts, Error **errp)
 static bool is_qemuopts_group(const char *group)
 {
     if (g_str_equal(group, "object") ||
-        g_str_equal(group, "machine")) {
+        g_str_equal(group, "machine") ||
+        g_str_equal(group, "accel")) {
         return false;
     }
     return true;
@@ -2050,6 +2023,8 @@ static GSList **qemu_config_list(const char *group)
 {
     if (g_str_equal(group, "object")) {
         return &object_opts_list;
+    } else if (g_str_equal(group, "accel")) {
+        return &accel_opts_list;
     }
     return NULL;
 }
@@ -2188,22 +2163,20 @@ static int do_configure_icount(void *opaque, QemuOpts *opts, Error **errp)
     return 0;
 }
 
-static int accelerator_set_property(void *opaque,
-                                const char *name, const char *value,
-                                Error **errp)
-{
-    return object_parse_property_opt(opaque, name, value, "accel", errp);
-}
-
-static int do_configure_accelerator(void *opaque, QemuOpts *opts, Error **errp)
+static void do_configure_accelerator(void *data, void *opaque)
 {
     bool *p_init_failed = opaque;
-    const char *acc = qemu_opt_get(opts, "accel");
+    QDict *qdict = data;
+    const char *acc = qdict_get_try_str(qdict, "accel");
     AccelClass *ac = accel_find(acc);
     AccelState *accel;
     int ret;
     bool qtest_with_kvm;
 
+    if (current_accel()) {
+        return;
+    }
+
     qtest_with_kvm = g_str_equal(acc, "kvm") && qtest_chrdev != NULL;
 
     if (!ac) {
@@ -2211,24 +2184,20 @@ static int do_configure_accelerator(void *opaque, QemuOpts *opts, Error **errp)
         if (!qtest_with_kvm) {
             error_report("invalid accelerator %s", acc);
         }
-        return 0;
+        return;
     }
     accel = ACCEL(object_new_with_class(OBJECT_CLASS(ac)));
     object_apply_compat_props(OBJECT(accel));
-    qemu_opt_foreach(opts, accelerator_set_property,
-                     accel,
-                     &error_fatal);
+    qdict_del(qdict, "accel");
+    object_set_properties_from_keyval(OBJECT(accel), qdict, &error_fatal);
 
     ret = accel_init_machine(accel, current_machine);
     if (ret < 0) {
         *p_init_failed = true;
         if (!qtest_with_kvm || ret != -ENOENT) {
-            error_report("failed to initialize %s: %s", acc, strerror(-ret));
+            error_report("failed to initialize %s: %s", ac->name, strerror(-ret));
         }
-        return 0;
     }
-
-    return 1;
 }
 
 static void configure_accelerators(const char *progname)
@@ -2238,7 +2207,7 @@ static void configure_accelerators(const char *progname)
     qemu_opts_foreach(qemu_find_opts("icount"),
                       do_configure_icount, NULL, &error_fatal);
 
-    if (QTAILQ_EMPTY(&qemu_accel_opts.head)) {
+    if (!accel_opts_list) {
         char **accel_list, **tmp;
 
         if (accelerators == NULL) {
@@ -2271,7 +2240,9 @@ static void configure_accelerators(const char *progname)
              * such as "-machine accel=tcg,,thread=single".
              */
             if (accel_find(*tmp)) {
-                qemu_opts_parse_noisily(qemu_find_opts("accel"), *tmp, true);
+                QDict *qdict = qdict_new();
+                qdict_put_str(qdict, "accel", *tmp);
+                accel_opts_list = g_slist_prepend(accel_opts_list, qdict);
             } else {
                 init_failed = true;
                 error_report("invalid accelerator %s", *tmp);
@@ -2285,8 +2256,12 @@ static void configure_accelerators(const char *progname)
         }
     }
 
-    if (!qemu_opts_foreach(qemu_find_opts("accel"),
-                           do_configure_accelerator, &init_failed, &error_fatal)) {
+    accel_opts_list = g_slist_reverse(accel_opts_list);
+    g_slist_foreach(accel_opts_list,
+                    do_configure_accelerator,
+                    &init_failed);
+
+    if (!current_accel()) {
         if (!init_failed) {
             error_report("no accelerator found");
         }
@@ -2304,6 +2279,27 @@ static void configure_accelerators(const char *progname)
     }
 }
 
+static void list_accelerators(void)
+{
+    printf("Accelerators supported in QEMU binary:\n");
+    GSList *el, *accel_list = object_class_get_list(TYPE_ACCEL,
+                                                    false);
+    for (el = accel_list; el; el = el->next) {
+        gchar *typename = g_strdup(object_class_get_name(
+                                   OBJECT_CLASS(el->data)));
+        /* omit qtest which is used for tests only */
+        if (g_strcmp0(typename, ACCEL_CLASS_NAME("qtest")) &&
+            g_str_has_suffix(typename, ACCEL_CLASS_SUFFIX)) {
+            gchar **optname = g_strsplit(typename,
+                                         ACCEL_CLASS_SUFFIX, 0);
+            printf("%s\n", optname[0]);
+            g_strfreev(optname);
+        }
+        g_free(typename);
+    }
+    g_slist_free(accel_list);
+}
+
 static void create_default_memdev(MachineState *ms, const char *path)
 {
     Object *obj;
@@ -2627,7 +2623,7 @@ void qmp_x_exit_preconfig(Error **errp)
 void qemu_init(int argc, char **argv, char **envp)
 {
     QemuOpts *opts;
-    QemuOpts *icount_opts = NULL, *accel_opts = NULL;
+    QemuOpts *icount_opts = NULL;
     QemuOptsList *olist;
     int optind;
     const char *optarg;
@@ -2651,7 +2647,6 @@ void qemu_init(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_trace_opts);
     qemu_plugin_add_opts();
     qemu_add_opts(&qemu_option_rom_opts);
-    qemu_add_opts(&qemu_accel_opts);
     qemu_add_opts(&qemu_mem_opts);
     qemu_add_opts(&qemu_smp_opts);
     qemu_add_opts(&qemu_boot_opts);
@@ -3207,30 +3202,21 @@ void qemu_init(int argc, char **argv, char **envp)
                     break;
                 }
             case QEMU_OPTION_accel:
-                accel_opts = qemu_opts_parse_noisily(qemu_find_opts("accel"),
-                                                     optarg, true);
-                optarg = qemu_opt_get(accel_opts, "accel");
-                if (!optarg || is_help_option(optarg)) {
-                    printf("Accelerators supported in QEMU binary:\n");
-                    GSList *el, *accel_list = object_class_get_list(TYPE_ACCEL,
-                                                                    false);
-                    for (el = accel_list; el; el = el->next) {
-                        gchar *typename = g_strdup(object_class_get_name(
-                                                   OBJECT_CLASS(el->data)));
-                        /* omit qtest which is used for tests only */
-                        if (g_strcmp0(typename, ACCEL_CLASS_NAME("qtest")) &&
-                            g_str_has_suffix(typename, ACCEL_CLASS_SUFFIX)) {
-                            gchar **optname = g_strsplit(typename,
-                                                         ACCEL_CLASS_SUFFIX, 0);
-                            printf("%s\n", optname[0]);
-                            g_strfreev(optname);
+                {
+                    QDict *args;
+                    bool help;
+
+                    args = keyval_parse(optarg, "accel", &help, &error_fatal);
+                    if (help) {
+                        const char *type = qdict_get_try_str(args, "accel");
+                        if (!type || !accel_print_class_properties(type)) {
+                            list_accelerators();
                         }
-                        g_free(typename);
+                        exit(0);
                     }
-                    g_slist_free(accel_list);
-                    exit(0);
+                    qemu_record_config_group("accel", args, &error_abort);
+                    break;
                 }
-                break;
             case QEMU_OPTION_usb:
                 qdict_put_str(machine_opts_dict, "usb", "on");
                 break;
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 67+ messages in thread

* Re: [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing
  2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
                   ` (24 preceding siblings ...)
  2021-01-18 16:31 ` [PATCH 25/25] vl: switch -accel parsing to keyval Paolo Bonzini
@ 2021-01-18 17:18 ` no-reply
  25 siblings, 0 replies; 67+ messages in thread
From: no-reply @ 2021-01-18 17:18 UTC (permalink / raw)
  To: pbonzini; +Cc: kwolf, imammedo, qemu-devel, armbru

Patchew URL: https://patchew.org/QEMU/20210118163113.780171-1-pbonzini@redhat.com/



Hi,

This series seems to have some coding style problems. See output below for
more information:

Type: series
Message-id: 20210118163113.780171-1-pbonzini@redhat.com
Subject: [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing

=== TEST SCRIPT BEGIN ===
#!/bin/bash
git rev-parse base > /dev/null || exit 0
git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram
./scripts/checkpatch.pl --mailback base..
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
From https://github.com/patchew-project/qemu
 * [new tag]         patchew/20210118163113.780171-1-pbonzini@redhat.com -> patchew/20210118163113.780171-1-pbonzini@redhat.com
Switched to a new branch 'test'
ec26fa4 vl: switch -accel parsing to keyval
b9750c0 qemu-option: remove now-dead code
0ec053d vl: switch -M parsing to keyval
2bfede0 qom: export more functions for use with non-UserCreatable objects
2c8420d storage-daemon: do not register the "object" group with QemuOpts
6072877 qemu: use keyval for -object parsing
d597674 qemu-img: use keyval for -object parsing
4bf11fa qemu-nbd: use keyval for -object parsing
85a5e5a qemu-io: use keyval for -object parsing
9a44901 qom: do not modify QDict argument in user_creatable_add_dict
193c6a2 vl: plumb keyval-based options into -set and -readconfig
31579e0 qemu-config: parse configuration files to a QDict
bc20068 qemu-option: support accept-any QemuOptsList in qemu_opts_absorb_qdict
ca390c3 qemu-config: add error propagation to qemu_config_parse
59b951f remove -writeconfig
fa45a78 hmp: special case help options for object_add
2561b79 qom: use qemu_printf to print help for user-creatable objects
0cab5f9 hmp: replace "O" parser with keyval
bd6519f keyval: introduce keyval_parse_into
d3fc628 tests: convert check-qom-proplist to keyval
437fd40 keyval: simplify keyval_parse_one
ffc5547 keyval: accept escaped commas in implied option
8a251bf qemu-option: warn for short-form boolean options
c2d9889 qemu-option: move help handling to get_opt_name_value
e067ad6 qemu-option: clean up id vs. list->merge_lists

=== OUTPUT BEGIN ===
1/25 Checking commit e067ad67f18f (qemu-option: clean up id vs. list->merge_lists)
2/25 Checking commit c2d98893d095 (qemu-option: move help handling to get_opt_name_value)
3/25 Checking commit 8a251bf52737 (qemu-option: warn for short-form boolean options)
WARNING: line over 80 characters
#82: FILE: util/qemu-option.c:787:
+                warn_report("short-form boolean option '%s%s' deprecated", prefix, *name);

WARNING: line over 80 characters
#101: FILE: util/qemu-option.c:818:
+        p = get_opt_name_value(p, firstname, warn_on_flag, help_wanted, &option, &value);

total: 0 errors, 2 warnings, 127 lines checked

Patch 3/25 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
4/25 Checking commit ffc55478b1cb (keyval: accept escaped commas in implied option)
5/25 Checking commit 437fd4000b35 (keyval: simplify keyval_parse_one)
6/25 Checking commit d3fc62875f6a (tests: convert check-qom-proplist to keyval)
7/25 Checking commit bd6519ffd2d1 (keyval: introduce keyval_parse_into)
WARNING: line over 80 characters
#25: FILE: include/qemu/option.h:150:
+QDict *keyval_parse_into(QDict *qdict, const char *params, const char *implied_key,

WARNING: line over 80 characters
#44: FILE: util/keyval.c:442:
+QDict *keyval_parse_into(QDict *qdict, const char *params, const char *implied_key,

total: 0 errors, 2 warnings, 75 lines checked

Patch 7/25 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
8/25 Checking commit 0cab5f97ab3d (hmp: replace "O" parser with keyval)
9/25 Checking commit 2561b79c783e (qom: use qemu_printf to print help for user-creatable objects)
10/25 Checking commit fa45a78a9d8f (hmp: special case help options for object_add)
11/25 Checking commit 59b951f9b110 (remove -writeconfig)
12/25 Checking commit ca390c3ae7df (qemu-config: add error propagation to qemu_config_parse)
WARNING: line over 80 characters
#43: FILE: include/qemu/config-file.h:13:
+int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp);

WARNING: line over 80 characters
#119: FILE: util/qemu-config.c:311:
+int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp)

total: 0 errors, 2 warnings, 141 lines checked

Patch 12/25 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
13/25 Checking commit bc2006827446 (qemu-option: support accept-any QemuOptsList in qemu_opts_absorb_qdict)
14/25 Checking commit 31579e030854 (qemu-config: parse configuration files to a QDict)
WARNING: line over 80 characters
#28: FILE: include/qemu/config-file.h:4:
+typedef void QEMUConfigCB(const char *group, QDict *qdict, void *opaque, Error **errp);

WARNING: line over 80 characters
#38: FILE: include/qemu/config-file.h:17:
+void qemu_config_do_parse(const char *group, QDict *qdict, void *opaque, Error **errp);

WARNING: line over 80 characters
#62: FILE: softmmu/vl.c:3316:
+                qemu_read_config_file(optarg, qemu_config_do_parse, &error_fatal);

WARNING: line over 80 characters
#170: FILE: util/qemu-config.c:384:
+void qemu_config_do_parse(const char *group, QDict *qdict, void *opaque, Error **errp)

WARNING: line over 80 characters
#192: FILE: util/qemu-config.c:406:
+int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp)

total: 0 errors, 5 warnings, 170 lines checked

Patch 14/25 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
15/25 Checking commit 193c6a2cb171 (vl: plumb keyval-based options into -set and -readconfig)
WARNING: line over 80 characters
#115: FILE: softmmu/vl.c:2095:
+static void qemu_record_config_group(const char *group, QDict *dict, Error **errp)

WARNING: line over 80 characters
#127: FILE: softmmu/vl.c:2107:
+static void qemu_set_qdict_option(QDict *dict, const char *key, const char *value,

WARNING: line over 80 characters
#152: FILE: softmmu/vl.c:2132:
+            error_setg(errp, "Lists cannot be at top level of a configuration section");

WARNING: line over 80 characters
#250: FILE: softmmu/vl.c:3405:
+                qemu_read_config_file(optarg, qemu_parse_config_group, &error_fatal);

ERROR: line over 90 characters
#268: FILE: tests/test-keyval.c:746:
+    QDict *combined = keyval_parse("opt1=ABC,opt2.sub1=def,opt2.sub2=GHI,opt2.sub3=JKL,opt3=xyz",

WARNING: line over 80 characters
#318: FILE: util/keyval.c:319:
+static void keyval_do_merge(QDict *old, const QDict *new, GString *str, Error **errp)

WARNING: line over 80 characters
#327: FILE: util/keyval.c:328:
+            error_setg(errp, "Parameter '%s%s' used inconsistently", str->str, ent->key);

total: 1 errors, 6 warnings, 293 lines checked

Patch 15/25 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

16/25 Checking commit 9a449013c568 (qom: do not modify QDict argument in user_creatable_add_dict)
17/25 Checking commit 85a5e5a4aeee (qemu-io: use keyval for -object parsing)
18/25 Checking commit 4bf11facae8c (qemu-nbd: use keyval for -object parsing)
19/25 Checking commit d597674d23d7 (qemu-img: use keyval for -object parsing)
20/25 Checking commit 6072877b6226 (qemu: use keyval for -object parsing)
ERROR: do not initialise statics to 0 or NULL
#211: FILE: softmmu/vl.c:139:
+static GSList *object_opts_list = NULL;

WARNING: line over 80 characters
#346: FILE: softmmu/vl.c:3471:
+                    args = keyval_parse(optarg, "qom-type", &help, &error_fatal);

total: 1 errors, 1 warnings, 324 lines checked

Patch 20/25 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

21/25 Checking commit 2c8420d34e62 (storage-daemon: do not register the "object" group with QemuOpts)
22/25 Checking commit 2bfede05a80b (qom: export more functions for use with non-UserCreatable objects)
ERROR: code indent should never use tabs
#45: FILE: include/qom/object.h:882:
+^I^I^I^I       Error **errp);$

total: 1 errors, 0 warnings, 108 lines checked

Patch 22/25 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

23/25 Checking commit 0ec053de9e5e (vl: switch -M parsing to keyval)
WARNING: line over 80 characters
#187: FILE: softmmu/vl.c:1558:
+            error_setg(&local_err, "No machine specified, and there is no default");

WARNING: line over 80 characters
#200: FILE: softmmu/vl.c:1564:
+        error_append_hint(&local_err, "Use -machine help to list supported machines\n");

WARNING: line over 80 characters
#237: FILE: softmmu/vl.c:1605:
+            error_setg(errp, "Conflict between '%s' and '%s'", ent->key, new_key);

WARNING: line over 80 characters
#277: FILE: softmmu/vl.c:1629:
+        object_register_sugar_prop(ACCEL_CLASS_NAME("xen"), "igd-passthru", value);

WARNING: line over 80 characters
#283: FILE: softmmu/vl.c:1635:
+        object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), "kvm-shadow-mem", value);

WARNING: line over 80 characters
#289: FILE: softmmu/vl.c:1641:
+        object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), "kernel-irqchip", value);

WARNING: line over 80 characters
#290: FILE: softmmu/vl.c:1642:
+        object_register_sugar_prop(ACCEL_CLASS_NAME("whpx"), "kernel-irqchip", value);

WARNING: line over 80 characters
#311: FILE: softmmu/vl.c:1709:
+    object_set_properties_from_keyval(OBJECT(current_machine), qdict, &error_fatal);

ERROR: line over 90 characters
#323: FILE: softmmu/vl.c:1739:
+        semihosting_arg_fallback(current_machine->kernel_filename, current_machine->kernel_cmdline);

ERROR: line over 90 characters
#365: FILE: softmmu/vl.c:2015:
+        object_set_properties_from_keyval(OBJECT(current_machine), default_opts, &error_abort);

ERROR: line over 90 characters
#493: FILE: softmmu/vl.c:3202:
+                    keyval_parse_into(machine_opts_dict, optarg, "type", &help, &error_fatal);

total: 3 errors, 8 warnings, 523 lines checked

Patch 23/25 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

24/25 Checking commit b9750c0ad970 (qemu-option: remove now-dead code)
25/25 Checking commit ec26fa447263 (vl: switch -accel parsing to keyval)
WARNING: line over 80 characters
#29: FILE: accel/accel.c:51:
+    g_autofree char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name);

ERROR: do not initialise statics to 0 or NULL
#56: FILE: softmmu/vl.c:142:
+static GSList *accel_opts_list = NULL;

WARNING: line over 80 characters
#172: FILE: softmmu/vl.c:2198:
+            error_report("failed to initialize %s: %s", ac->name, strerror(-ret));

total: 1 errors, 2 warnings, 257 lines checked

Patch 25/25 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

=== OUTPUT END ===

Test command exited with code: 1


The full log is available at
http://patchew.org/logs/20210118163113.780171-1-pbonzini@redhat.com/testing.checkpatch/?type=message.
---
Email generated automatically by Patchew [https://patchew.org/].
Please send your feedback to patchew-devel@redhat.com

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 01/25] qemu-option: clean up id vs. list->merge_lists
  2021-01-18 16:30 ` [PATCH 01/25] qemu-option: clean up id vs. list->merge_lists Paolo Bonzini
@ 2021-01-19 12:33   ` Kevin Wolf
  2021-01-19 13:58   ` Markus Armbruster
  1 sibling, 0 replies; 67+ messages in thread
From: Kevin Wolf @ 2021-01-19 12:33 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: imammedo, qemu-devel, armbru

Am 18.01.2021 um 17:30 hat Paolo Bonzini geschrieben:
> Looking at all merge-lists QemuOptsList, here is how they access their
> QemuOpts:
> 
> reopen_opts in qemu-io-cmds.c ("qemu-img reopen -o")
> 	qemu_opts_find(&reopen_opts, NULL)
> 
> empty_opts in qemu-io.c ("qemu-io open -o")
> 	qemu_opts_find(&empty_opts, NULL)
> 
> qemu_rtc_opts ("-rtc")
> 	qemu_find_opts_singleton("rtc")
> 
> qemu_machine_opts ("-M")
> 	qemu_find_opts_singleton("machine")
> 
> qemu_boot_opts ("-boot")
> 	QTAILQ_FIRST(&qemu_find_opts("bootopts")->head)
> 
> qemu_name_opts ("-name")
> 	qemu_opts_foreach->parse_name
> 	parse_name does not use id
> 
> qemu_mem_opts ("-m")
> 	qemu_find_opts_singleton("memory")
> 
> qemu_icount_opts ("-icount")
> 	qemu_opts_foreach->do_configuree_icount
> 	do_configure_icount->icount_configure
> 	icount_configure does not use id

qemu_action_opts has been added since the last version of this series.
It's fine, too (gets converted to a QAPI type that doesn't have an "id"
field), but you may want to update the commit message.

With this:
Reviewed-by: Kevin Wolf <kwolf@redhat.com>



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 01/25] qemu-option: clean up id vs. list->merge_lists
  2021-01-18 16:30 ` [PATCH 01/25] qemu-option: clean up id vs. list->merge_lists Paolo Bonzini
  2021-01-19 12:33   ` Kevin Wolf
@ 2021-01-19 13:58   ` Markus Armbruster
  2021-01-19 14:20     ` Paolo Bonzini
  1 sibling, 1 reply; 67+ messages in thread
From: Markus Armbruster @ 2021-01-19 13:58 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> Looking at all merge-lists QemuOptsList

outside tests/

>                                          here is how they access their
> QemuOpts:
>
> reopen_opts in qemu-io-cmds.c ("qemu-img reopen -o")
> 	qemu_opts_find(&reopen_opts, NULL)
>
> empty_opts in qemu-io.c ("qemu-io open -o")
> 	qemu_opts_find(&empty_opts, NULL)
>
> qemu_rtc_opts ("-rtc")
> 	qemu_find_opts_singleton("rtc")
>
> qemu_machine_opts ("-M")
> 	qemu_find_opts_singleton("machine")

Gone since your commit 4988a4ea4d "vl: switch -M parsing to keyval".
Did it change behavior when multiple -M are given, with various id=?  I
didn't look at the patch back then, and I'm not going to look at it
now.  Feel free to ignore the question.

>
> qemu_boot_opts ("-boot")
> 	QTAILQ_FIRST(&qemu_find_opts("bootopts")->head)

That one's in hw/nvram/fw_cfg.c and hw/s390x/ipl.c.  However,
softmmu/vl.c uses

        qemu_opts_find(qemu_find_opts("boot-opts"), NULL);

If the user passes multiple -boot with different ID, we merge the ones
with same ID, and then vl.c gets the (merged) one without ID, but the
other two get the (merged) one that contains the first -boot.  All three
silently ignore the ones they don't get.  Awesomely weird.  I'd call it
a bug.

Test case:

    $ upstream-qemu -S -display none -boot strict=on,id=id -boot strict=off

vl.c uses strict=off, but fw_cfg.c uses strinct=on,id=id.

Outlawing "id" with .merge_lists like this patch does turns the cases
where the two methods yield different results into errors.  A bug fix of
sorts.  Should the commit message should cover it?

I believe we could use qemu_find_opts_singleton() in all three spots.
Not this patch's job.

> qemu_name_opts ("-name")
> 	qemu_opts_foreach->parse_name

Funny way to explain qemu_opts_foreach(qemu_name_opts, parse_name, ...)

> 	parse_name does not use id

We first use .merge-lists = true to merge all -name with the same ID,
then we iterate over all the merges.  Combined effect is "merge ignoring
ID".

> qemu_mem_opts ("-m")
> 	qemu_find_opts_singleton("memory")
>
> qemu_icount_opts ("-icount")
> 	qemu_opts_foreach->do_configuree_icount
> 	do_configure_icount->icount_configure
> 	icount_configure does not use id

Just like qemu_name_opts.  My comments apply, plus one:
s/do_configuree_icount/do_configure_icount/.

A recent addition is missing:

  qemu_action_opts ("-action")

Again, just like qemu_name_opts; my comments apply, plus one aside: this
should not use QemuOpts at all.  Use of qmp_marshal_FOO() is an
anti-pattern.  Needs cleanup.  Not in this patch, and probably not even
in this series.

> qemu_smp_opts ("-smp")
> 	qemu_opts_find(qemu_find_opts("smp-opts"), NULL)

That's one access in vl.c.  There's another one that uses
qemu_find_opts_singleton().  Okay, I think.

> qemu_spice_opts ("-spice")
> 	QTAILQ_FIRST(&qemu_spice_opts.head)

There's also machine_opts in qemu-config.c, but that's only to make
query-command-line-options lie backward-compatibly.

> i.e. they don't need an id.  Sometimes its presence is ignored
> (e.g. when using qemu_opts_foreach), sometimes all the options
> with the id are skipped,

(when using qemu_find_opts_singleton() or qemu_opts_find(list, NULL))

>                          sometimes only the first option on the
> command line is considered.

(when using QTAILQ_FIRST)

> command line is considered.  With this patch we just forbid id
> on merge-lists QemuOptsLists; if the command line still works,
> it has the same semantics as before.

It can break working (if weird) command lines, such as ones relying on
"merge ignoring ID" behavior of -name, -icount, -action.  Okay.

> qemu_opts_create's fail_if_exists parameter is now unnecessary:
>
> - it is unused if id is NULL
>
> - opts_parse only passes false if reached from qemu_opts_set_defaults,
> in which case this patch enforces that id must be NULL
>
> - other callers that can pass a non-NULL id always set it to true
>
> Assert that it is true in the only case where "fail_if_exists" matters,
> i.e. "id && !lists->merge_lists".  This means that if an id is present,
> duplicates are always forbidden, which was already the status quo.

Hmm.

If ->merge_lists, id= is forbidden, and all (id-less) opts are merged
into one.

Else, if id= is specified, it must be unique, i.e. no prior opts with
the same id.

Else, we don't check for prior opts without id.

There's at most one opts with a certain id, but there could be any
number without id.  Is this what we want?

> Discounting the case that aborts as it's not user-controlled (it's
> "just" a matter of inspecting qemu_opts_create callers), the paths
> through qemu_opts_create can be summarized as:
>
> - merge_lists = true: singleton opts with NULL id; non-NULL id fails
>
> - merge_lists = false: always return new opts; non-NULL id fails if dup

This renders the qemu_opts_foreach() silly.  Cleanup is in order, not
necessarily in this patch.

> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  util/qemu-option.c | 27 ++++++++++++++-------------
>  1 file changed, 14 insertions(+), 13 deletions(-)
>
> diff --git a/util/qemu-option.c b/util/qemu-option.c
> index c88e159f18..91f4120ce1 100644
> --- a/util/qemu-option.c
> +++ b/util/qemu-option.c
> @@ -619,7 +619,17 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
>  {
>      QemuOpts *opts = NULL;
>  
> -    if (id) {
> +    if (list->merge_lists) {
> +        if (id) {
> +            error_setg(errp, QERR_INVALID_PARAMETER, "id");
> +            return NULL;
> +        }
> +        opts = qemu_opts_find(list, NULL);
> +        if (opts) {
> +            return opts;
> +        }
> +    } else if (id) {
> +        assert(fail_if_exists);
>          if (!id_wellformed(id)) {
>              error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id",
>                         "an identifier");
> @@ -629,17 +639,8 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
>          }
>          opts = qemu_opts_find(list, id);
>          if (opts != NULL) {
> -            if (fail_if_exists && !list->merge_lists) {
> -                error_setg(errp, "Duplicate ID '%s' for %s", id, list->name);
> -                return NULL;
> -            } else {
> -                return opts;
> -            }
> -        }
> -    } else if (list->merge_lists) {
> -        opts = qemu_opts_find(list, NULL);
> -        if (opts) {
> -            return opts;
> +            error_setg(errp, "Duplicate ID '%s' for %s", id, list->name);
> +            return NULL;
>          }
>      }
>      opts = g_malloc0(sizeof(*opts));
> @@ -893,7 +894,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
>       * (if unlikely) future misuse:
>       */
>      assert(!defaults || list->merge_lists);
> -    opts = qemu_opts_create(list, id, !defaults, errp);
> +    opts = qemu_opts_create(list, id, !list->merge_lists, errp);
>      g_free(id);
>      if (opts == NULL) {
>          return NULL;

My dread of QemuOpts has been refreshed to its nominal level.



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 01/25] qemu-option: clean up id vs. list->merge_lists
  2021-01-19 13:58   ` Markus Armbruster
@ 2021-01-19 14:20     ` Paolo Bonzini
  2021-01-20  8:03       ` Markus Armbruster
  0 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-19 14:20 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 19/01/21 14:58, Markus Armbruster wrote:
>> qemu_machine_opts ("-M")
>> 	qemu_find_opts_singleton("machine")
> 
> Gone since your commit 4988a4ea4d "vl: switch -M parsing to keyval".

Which is part of this series and not yet included in QEMU. Hence the 
commit message talks about it in the present tense.

> If the user passes multiple -boot with different ID, we merge the ones
> with same ID, and then vl.c gets the (merged) one without ID, but the
> other two get the (merged) one that contains the first -boot.  All three
> silently ignore the ones they don't get.  Awesomely weird.  I'd call it
> a bug.
> 
> Test case:
> 
>      $ upstream-qemu -S -display none -boot strict=on,id=id -boot strict=off
> 
> vl.c uses strict=off, but fw_cfg.c uses strinct=on,id=id.
> 
> Outlawing "id" with .merge_lists like this patch does turns the cases
> where the two methods yield different results into errors.  A bug fix of
> sorts.  Should the commit message should cover it?

Yeah, I can add that.

> [qemu_action_opts]
> should not use QemuOpts at all.  Use of qmp_marshal_FOO() is an
> anti-pattern.  Needs cleanup.  Not in this patch, and probably not even
> in this series.

--verbose needed.  Why is it an anti-pattern?  I found it a clever way 
to avoid code duplication. :)  Doesn't matter for this series, anyway.

>> command line is considered.  With this patch we just forbid id
>> on merge-lists QemuOptsLists; if the command line still works,
>> it has the same semantics as before.
> 
> It can break working (if weird) command lines, such as ones relying on
> "merge ignoring ID" behavior of -name, -icount, -action.  Okay.

Right, I wrote that down as a feature.  The important thing is keeping 
things the same if they still work.

> [If !lists->merge_lists], if id= is specified, it must be unique,
> i.e. no prior opts with the same id.
> 
> Else, we don't check for prior opts without id.
> 
> There's at most one opts with a certain id, but there could be any
> number without id.  Is this what we want?

Yes, positively.  Example: "qemu-system-x86_64 -device foo -device bar".

>> Discounting the case that aborts as it's not user-controlled (it's
>> "just" a matter of inspecting qemu_opts_create callers), the paths
>> through qemu_opts_create can be summarized as:
>>
>> - merge_lists = true: singleton opts with NULL id; non-NULL id fails
>>
>> - merge_lists = false: always return new opts; non-NULL id fails if dup
> 
> This renders the qemu_opts_foreach() silly.  Cleanup is in order, not
> necessarily in this patch.

Agreed.  This one is already tricky enough (though I like the outcome).

Paolo



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 02/25] qemu-option: move help handling to get_opt_name_value
  2021-01-18 16:30 ` [PATCH 02/25] qemu-option: move help handling to get_opt_name_value Paolo Bonzini
@ 2021-01-19 15:10   ` Markus Armbruster
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Armbruster @ 2021-01-19 15:10 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> Right now, help options are parsed normally and then checked
> specially in opt_validate, but only if coming from
> qemu_opts_parse_noisily.  has_help_option does the check on its own.
>
> opt_validate() has two callers: qemu_opt_set(), which passes null and is
> therefore unaffected, and opts_do_parse(), which is affected.
>
> opts_do_parse() is called by qemu_opts_do_parse(), which passes null and
> is therefore unaffected, and opts_parse().
>
> opts_parse() is called by qemu_opts_parse() and qemu_opts_set_defaults(),
> which pass null and are therefore unaffected, and
> qemu_opts_parse_noisily().
>
> Move the check from opt_validate to the parsing workhorse of QemuOpts,
> get_opt_name_value.  This will come in handy in the next patch, which
> will raise a warning for "-object memory-backend-ram,share" ("flag" option
> with no =on/=off part) but not for "-object memory-backend-ram,help".
>
> As a result:
>
> - opts_parse and opts_do_parse do not return an error anymore
>   when help is requested; qemu_opts_parse_noisily does not have
>   to work around that anymore.
>
> - various crazy ways to request help are not recognized anymore:
>   - "help=..."
>   - "nohelp" (sugar for "help=off")
>   - "?=..."
>   - "no?" (sugar for "?=off")
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  util/qemu-option.c | 38 +++++++++++++++++++++++---------------
>  1 file changed, 23 insertions(+), 15 deletions(-)
>
> diff --git a/util/qemu-option.c b/util/qemu-option.c
> index 91f4120ce1..5f27d4369d 100644
> --- a/util/qemu-option.c
> +++ b/util/qemu-option.c
> @@ -496,8 +496,7 @@ static QemuOpt *opt_create(QemuOpts *opts, const char *name, char *value,
>      return opt;
>  }
>  
> -static bool opt_validate(QemuOpt *opt, bool *help_wanted,
> -                         Error **errp)
> +static bool opt_validate(QemuOpt *opt, Error **errp)
>  {
>      const QemuOptDesc *desc;
>      const QemuOptsList *list = opt->opts->list;
> @@ -505,9 +504,6 @@ static bool opt_validate(QemuOpt *opt, bool *help_wanted,
>      desc = find_desc_by_name(list->desc, opt->name);
>      if (!desc && !opts_accepts_any(list)) {
>          error_setg(errp, QERR_INVALID_PARAMETER, opt->name);
> -        if (help_wanted && is_help_option(opt->name)) {
> -            *help_wanted = true;
> -        }
>          return false;
>      }
>  
> @@ -524,7 +520,7 @@ bool qemu_opt_set(QemuOpts *opts, const char *name, const char *value,
>  {
>      QemuOpt *opt = opt_create(opts, name, g_strdup(value), false);
>  
> -    if (!opt_validate(opt, NULL, errp)) {
> +    if (!opt_validate(opt, errp)) {
>          qemu_opt_del(opt);
>          return false;
>      }
> @@ -760,10 +756,12 @@ void qemu_opts_print(QemuOpts *opts, const char *separator)
>  
>  static const char *get_opt_name_value(const char *params,
>                                        const char *firstname,
> +                                      bool *help_wanted,
>                                        char **name, char **value)
>  {
>      const char *p;
>      size_t len;
> +    bool is_help = false;
>  
>      len = strcspn(params, "=,");
>      if (params[len] != '=') {
> @@ -780,6 +778,7 @@ static const char *get_opt_name_value(const char *params,
>                  *value = g_strdup("off");
>              } else {
>                  *value = g_strdup("on");
> +                is_help = is_help_option(*name);
>              }
>          }
>      } else {
> @@ -791,6 +790,9 @@ static const char *get_opt_name_value(const char *params,
>      }
>  
>      assert(!*p || *p == ',');
> +    if (help_wanted && is_help) {
> +        *help_wanted = true;

Note [1] for later: we only ever set *help_wanted to true.

> +    }
>      if (*p == ',') {
>          p++;
>      }
> @@ -806,7 +808,12 @@ static bool opts_do_parse(QemuOpts *opts, const char *params,
>      QemuOpt *opt;
>  
>      for (p = params; *p;) {
> -        p = get_opt_name_value(p, firstname, &option, &value);
> +        p = get_opt_name_value(p, firstname, help_wanted, &option, &value);
> +        if (help_wanted && *help_wanted) {
> +            g_free(option);
> +            g_free(value);
> +            return false;
> +        }
>          firstname = NULL;
>  
>          if (!strcmp(option, "id")) {
> @@ -817,7 +824,7 @@ static bool opts_do_parse(QemuOpts *opts, const char *params,
>  
>          opt = opt_create(opts, option, value, prepend);
>          g_free(option);
> -        if (!opt_validate(opt, help_wanted, errp)) {
> +        if (!opt_validate(opt, errp)) {
>              qemu_opt_del(opt);
>              return false;
>          }
> @@ -832,7 +839,7 @@ static char *opts_parse_id(const char *params)
>      char *name, *value;
>  
>      for (p = params; *p;) {
> -        p = get_opt_name_value(p, NULL, &name, &value);
> +        p = get_opt_name_value(p, NULL, NULL, &name, &value);
>          if (!strcmp(name, "id")) {
>              g_free(name);
>              return value;
> @@ -848,11 +855,10 @@ bool has_help_option(const char *params)
>  {
>      const char *p;
>      char *name, *value;
> -    bool ret;
> +    bool ret = false;

This initializer is required due to [1].
>  
>      for (p = params; *p;) {
> -        p = get_opt_name_value(p, NULL, &name, &value);
> -        ret = is_help_option(name);
> +        p = get_opt_name_value(p, NULL, &ret, &name, &value);
>          g_free(name);
>          g_free(value);
>          if (ret) {

Works, but I'd prefer get_opt_name_value() to always set *help_wanted
when help_wanted isn't null.  Up to you.

> @@ -937,11 +943,13 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
>      QemuOpts *opts;
>      bool help_wanted = false;
>  
> -    opts = opts_parse(list, params, permit_abbrev, false, &help_wanted, &err);
> -    if (err) {
> +    opts = opts_parse(list, params, permit_abbrev, false,
> +                      opts_accepts_any(list) ? NULL : &help_wanted,
> +                      &err);
> +    if (!opts) {
> +        assert(!!err + !!help_wanted == 1);

Either err or help_wanted.  This is logical inequality.  I'd write it as

           assert(!err != !help_wanted);

or maybe as

           assert(!err ^ !help_wanted);

But your artistic license applies.

>          if (help_wanted) {
>              qemu_opts_print_help(list, true);
> -            error_free(err);
>          } else {
>              error_report_err(err);
>          }

Before the patch, we recognize help requests only if they aren't
captured by a (foolishly named) parameter name.

Afterwards, we recognize only sane help requests regardless of
parameters.  In other words:

  - various crazy ways to request help are not recognized anymore:
    - "help=..."
    - "nohelp" (sugar for "help=off")
    - "?=..."
    - "no?" (sugar for "?=off")

  - "help" is recognized as help request even if there is a (foolishly
    named) parameter "help".  No such parameters exist.

Let's add the last item to the commit message, too.

Preferably with the commit message amended:
Reviewed-by: Markus Armbruster <armbru@redhat.com>



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 03/25] qemu-option: warn for short-form boolean options
  2021-01-18 16:30 ` [PATCH 03/25] qemu-option: warn for short-form boolean options Paolo Bonzini
@ 2021-01-19 15:56   ` Markus Armbruster
  2021-01-19 17:04     ` Paolo Bonzini
  0 siblings, 1 reply; 67+ messages in thread
From: Markus Armbruster @ 2021-01-19 15:56 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel, armbru

Paolo Bonzini <pbonzini@redhat.com> writes:

> Options such as "server" or "nowait", that are commonly found in -chardev,
> are sugar for "server=on" and "wait=off".  This is quite surprising and
> also does not have any notion of typing attached.  It is even possible to
> do "-device e1000,noid" and get a device with "id=off".
>
> Deprecate it and print a warning when it is encountered.  In general,
> this short form for boolean options only seems to be in wide use for
> -chardev and -spice.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  docs/system/deprecated.rst |  6 ++++++
>  tests/test-qemu-opts.c     |  2 +-
>  util/qemu-option.c         | 29 ++++++++++++++++++-----------
>  3 files changed, 25 insertions(+), 12 deletions(-)
>
> diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst
> index e20bfcb17a..e71faefbe5 100644
> --- a/docs/system/deprecated.rst
> +++ b/docs/system/deprecated.rst
> @@ -127,6 +127,12 @@ Drives with interface types other than ``if=none`` are for onboard
>  devices.  It is possible to use drives the board doesn't pick up with
>  -device.  This usage is now deprecated.  Use ``if=none`` instead.
>  
> +Short-form boolean options (since 5.2)

6.0

> +''''''''''''''''''''''''''''''''''''''
> +
> +Boolean options such as ``share=on``/``share=off`` can be written
> +in short form as ``share`` and ``noshare``.  This is deprecated
> +and will cause a warning.
>  
>  QEMU Machine Protocol (QMP) commands
>  ------------------------------------
> diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c
> index 2aab831d10..8bbb17b1c7 100644
> --- a/tests/test-qemu-opts.c
> +++ b/tests/test-qemu-opts.c
> @@ -515,7 +515,7 @@ static void test_opts_parse(void)
>      error_free_or_abort(&err);
>      g_assert(!opts);
>  
> -    /* Implied value */
> +    /* Implied value (qemu_opts_parse warns but accepts it) */
>      opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=",
>                             false, &error_abort);
>      g_assert_cmpuint(opts_count(opts), ==, 3);
> diff --git a/util/qemu-option.c b/util/qemu-option.c
> index 5f27d4369d..40564a12eb 100644
> --- a/util/qemu-option.c
> +++ b/util/qemu-option.c
> @@ -756,10 +756,12 @@ void qemu_opts_print(QemuOpts *opts, const char *separator)
>  
>  static const char *get_opt_name_value(const char *params,
>                                        const char *firstname,
> +                                      bool warn_on_flag,
>                                        bool *help_wanted,
>                                        char **name, char **value)
>  {
>      const char *p;
> +    const char *prefix = "";
>      size_t len;
>      bool is_help = false;
>  
> @@ -776,10 +778,15 @@ static const char *get_opt_name_value(const char *params,
>              if (strncmp(*name, "no", 2) == 0) {
>                  memmove(*name, *name + 2, strlen(*name + 2) + 1);
>                  *value = g_strdup("off");
> +                prefix = "no";
>              } else {
>                  *value = g_strdup("on");
>                  is_help = is_help_option(*name);
>              }
> +            if (!is_help && warn_on_flag) {
> +                warn_report("short-form boolean option '%s%s' deprecated", prefix, *name);
> +                error_printf("Please use %s=%s instead\n", *name, *value);
> +            }

If @warn_on_flag, we warn except for "help" and "?".  The exception
applies regardless of @help_wanted.  Shouldn't we except *only*
recognized help requests?

>          }
>      } else {
>          /* found "foo=bar,more" */
> @@ -801,14 +808,14 @@ static const char *get_opt_name_value(const char *params,
>  
>  static bool opts_do_parse(QemuOpts *opts, const char *params,
>                            const char *firstname, bool prepend,
> -                          bool *help_wanted, Error **errp)
> +                          bool warn_on_flag, bool *help_wanted, Error **errp)
>  {
>      char *option, *value;
>      const char *p;
>      QemuOpt *opt;
>  
>      for (p = params; *p;) {
> -        p = get_opt_name_value(p, firstname, help_wanted, &option, &value);
> +        p = get_opt_name_value(p, firstname, warn_on_flag, help_wanted, &option, &value);

Long line.  Break it before the three output arguments.

>          if (help_wanted && *help_wanted) {
>              g_free(option);
>              g_free(value);
> @@ -839,7 +846,7 @@ static char *opts_parse_id(const char *params)
>      char *name, *value;
>  
>      for (p = params; *p;) {
> -        p = get_opt_name_value(p, NULL, NULL, &name, &value);
> +        p = get_opt_name_value(p, NULL, false, NULL, &name, &value);
>          if (!strcmp(name, "id")) {
>              g_free(name);
>              return value;
> @@ -858,7 +865,7 @@ bool has_help_option(const char *params)
>      bool ret = false;
>  
>      for (p = params; *p;) {
> -        p = get_opt_name_value(p, NULL, &ret, &name, &value);
> +        p = get_opt_name_value(p, NULL, false, &ret, &name, &value);
>          g_free(name);
>          g_free(value);
>          if (ret) {
> @@ -878,12 +885,12 @@ bool has_help_option(const char *params)
>  bool qemu_opts_do_parse(QemuOpts *opts, const char *params,
>                         const char *firstname, Error **errp)
>  {
> -    return opts_do_parse(opts, params, firstname, false, NULL, errp);
> +    return opts_do_parse(opts, params, firstname, false, false, NULL, errp);
>  }
>  
>  static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
>                              bool permit_abbrev, bool defaults,
> -                            bool *help_wanted, Error **errp)
> +                            bool warn_on_flag, bool *help_wanted, Error **errp)
>  {
>      const char *firstname;
>      char *id = opts_parse_id(params);
> @@ -906,8 +913,8 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
>          return NULL;
>      }
>  
> -    if (!opts_do_parse(opts, params, firstname, defaults, help_wanted,
> -                       errp)) {
> +    if (!opts_do_parse(opts, params, firstname, defaults,
> +                       warn_on_flag, help_wanted, errp)) {
>          qemu_opts_del(opts);
>          return NULL;
>      }
> @@ -925,7 +932,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
>  QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params,
>                            bool permit_abbrev, Error **errp)
>  {
> -    return opts_parse(list, params, permit_abbrev, false, NULL, errp);
> +    return opts_parse(list, params, permit_abbrev, false, false, NULL, errp);
>  }
>  
>  /**
> @@ -943,7 +950,7 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
>      QemuOpts *opts;
>      bool help_wanted = false;
>  
> -    opts = opts_parse(list, params, permit_abbrev, false,
> +    opts = opts_parse(list, params, permit_abbrev, false, true,
>                        opts_accepts_any(list) ? NULL : &help_wanted,
>                        &err);
>      if (!opts) {

This function now warns, except for "help" and "?".  The exception
applies even when we treat "help" and "?" as sugar for "help=on" and
"?=on" because opts_accepts_any().

It is the only spot that enables the warning.

Does all user input flow through qemu_opts_parse_noisily()?

> @@ -962,7 +969,7 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
>  {
>      QemuOpts *opts;
>  
> -    opts = opts_parse(list, params, permit_abbrev, true, NULL, NULL);
> +    opts = opts_parse(list, params, permit_abbrev, true, false, NULL, NULL);
>      assert(opts);
>  }



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 03/25] qemu-option: warn for short-form boolean options
  2021-01-19 15:56   ` Markus Armbruster
@ 2021-01-19 17:04     ` Paolo Bonzini
  2021-01-20  8:42       ` Markus Armbruster
  0 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-19 17:04 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 19/01/21 16:56, Markus Armbruster wrote:
>> +            if (!is_help && warn_on_flag) {
>> +                warn_report("short-form boolean option '%s%s' deprecated", prefix, *name);
>> +                error_printf("Please use %s=%s instead\n", *name, *value);
>> +            }
> 
> If @warn_on_flag, we warn except for "help" and "?".  The exception
> applies regardless of @help_wanted.  Shouldn't we except*only*
> recognized help requests?

Suggesting "help=yes" would be worse.

>>
>> -    opts = opts_parse(list, params, permit_abbrev, false,
>> +    opts = opts_parse(list, params, permit_abbrev, false, true,
>>                        opts_accepts_any(list) ? NULL : &help_wanted,
>>                        &err);
>>      if (!opts) {
> 
> This function now warns, except for "help" and "?".  The exception
> applies even when we treat "help" and "?" as sugar for "help=on" and
> "?=on" because opts_accepts_any().
> 
> It is the only spot that enables the warning.
> 
> Does all user input flow through qemu_opts_parse_noisily()?
> 

I was going to say yes, but -vnc (and worse, the QMP version of "change 
vnc") is parsed by qemu_opts_parse() via ui/vnc.c (besides being used by 
lots of tests).  -vnc has several boolean options, and though Libvirt 
only uses "sasl" it does so in the short form.

My solution would be to deprecate the QMP "change vnc" command, and 
postpone switching -vnc to qemu_opts_parse_noisily to 6.2.

The main reason to warn for short-form boolean options, is to block them 
for command line options that are switched to keyval[1].  Adding a 
warning does not necessarily imply removing in two releases.

Paolo

[1] This series already does that for -M, -accel and -object.  This 
means that applying this series would change the command line 
incompatibly without a two-release deprecation.  It's up for discussion 
whether to do so, or delay the application of those patches to 6.2.  It 
would be a pity to hold the dependent changes for effectively a year, 
but it's not a big deal.



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 01/25] qemu-option: clean up id vs. list->merge_lists
  2021-01-19 14:20     ` Paolo Bonzini
@ 2021-01-20  8:03       ` Markus Armbruster
  2021-01-20 12:37         ` Paolo Bonzini
  0 siblings, 1 reply; 67+ messages in thread
From: Markus Armbruster @ 2021-01-20  8:03 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> On 19/01/21 14:58, Markus Armbruster wrote:
>>> qemu_machine_opts ("-M")
>>> 	qemu_find_opts_singleton("machine")
>> 
>> Gone since your commit 4988a4ea4d "vl: switch -M parsing to keyval".
>
> Which is part of this series and not yet included in QEMU. Hence the 
> commit message talks about it in the present tense.

I got confused.

>> If the user passes multiple -boot with different ID, we merge the ones
>> with same ID, and then vl.c gets the (merged) one without ID, but the
>> other two get the (merged) one that contains the first -boot.  All three
>> silently ignore the ones they don't get.  Awesomely weird.  I'd call it
>> a bug.
>> 
>> Test case:
>> 
>>      $ upstream-qemu -S -display none -boot strict=on,id=id -boot strict=off
>> 
>> vl.c uses strict=off, but fw_cfg.c uses strinct=on,id=id.
>> 
>> Outlawing "id" with .merge_lists like this patch does turns the cases
>> where the two methods yield different results into errors.  A bug fix of
>> sorts.  Should the commit message should cover it?
>
> Yeah, I can add that.

Thanks!

>> [qemu_action_opts]
>> should not use QemuOpts at all.  Use of qmp_marshal_FOO() is an
>> anti-pattern.  Needs cleanup.  Not in this patch, and probably not even
>> in this series.
>
> --verbose needed.  Why is it an anti-pattern?  I found it a clever way 
> to avoid code duplication. :)  Doesn't matter for this series, anyway.

Because it's a clever way to do something that should not be done :)

-action wraps around QMP command set-action.  This means we need to
parse -action's argument into set-action's argument type.  That's a QAPI
type.  It's anonymous in the schema, and q_obj_set_action_arg in C.

As implemented, the parsing takes a detour through QemuOpts:

        char *optarg
             |
             |  opts = qemu_opts_parse_noisily(optarg);
             v
       QemuOpts *opts
             |
             |  qdict = qemu_opts_to_qdict(opts, NULL);
             v
        QDict *qdict
             |
             |  in qmp_marshal_set_action(qdict):
             |  v = qobject_input_visitor_new_str(qdict);
             |  visit_type_q_obj_set_action_arg_members(v, &arg, errp);
             v
  q_obj_set_action_arg arg

qmp_marshal_set_action() then passes the members of
q_obj_set_action_arg() to qmp_set_action().

The detour should be avoided, because QemuOpts should be avoided.  Using
the appropriate visitor, we get:

        char *optarg
             |
             |  v = qobject_input_visitor_new_str(string, NULL, errp)
             |  visit_type_q_obj_set_action_arg(v, NULL, &arg, errp);
             v
  q_obj_set_action_arg arg

except visit_type_q_obj_set_action_arg() doesn't exist, because the QAPI
type is anonymous.  So give it a name:

    { 'struct: 'Action',
      'data': { '*reboot': 'RebootAction',
                '*shutdown': 'ShutdownAction',
                '*panic': 'PanicAction',
                '*watchdog': 'WatchdogAction' } }

    { 'command': 'set-action',
      'data': 'Action',
      'allow-preconfig': true }

        char *optarg
             |
             |  v = qobject_input_visitor_new_str(string, NULL, errp)
             |  visit_type_Action(v, NULL, &arg, errp);
             v
         Action act

To avoid having to pass the members of Action to qmp_set_action(), throw
in 'boxed': true, so you can simply call qmp_set_action(&act, errp).

Aside: I'd like to have this QAPI/CLI boilerplate generated, like we
generate the QAPI/QMP boilerplate.  Not today; QAPI-land is busy with
John's static typing work.

>>> command line is considered.  With this patch we just forbid id
>>> on merge-lists QemuOptsLists; if the command line still works,
>>> it has the same semantics as before.
>> 
>> It can break working (if weird) command lines, such as ones relying on
>> "merge ignoring ID" behavior of -name, -icount, -action.  Okay.
>
> Right, I wrote that down as a feature.  The important thing is keeping 
> things the same if they still work.

Yes.

>> [If !lists->merge_lists], if id= is specified, it must be unique,
>> i.e. no prior opts with the same id.
>> 
>> Else, we don't check for prior opts without id.
>> 
>> There's at most one opts with a certain id, but there could be any
>> number without id.  Is this what we want?
>
> Yes, positively.  Example: "qemu-system-x86_64 -device foo -device bar".

D'oh!  QemuOpts left me no brain capacity for remembering the simplest
things %-}

>>> Discounting the case that aborts as it's not user-controlled (it's
>>> "just" a matter of inspecting qemu_opts_create callers), the paths
>>> through qemu_opts_create can be summarized as:
>>>
>>> - merge_lists = true: singleton opts with NULL id; non-NULL id fails
>>>
>>> - merge_lists = false: always return new opts; non-NULL id fails if dup
>> 
>> This renders the qemu_opts_foreach() silly.  Cleanup is in order, not
>> necessarily in this patch.
>
> Agreed.  This one is already tricky enough (though I like the outcome).

Me too.



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 03/25] qemu-option: warn for short-form boolean options
  2021-01-19 17:04     ` Paolo Bonzini
@ 2021-01-20  8:42       ` Markus Armbruster
  2021-01-20 12:40         ` Paolo Bonzini
  0 siblings, 1 reply; 67+ messages in thread
From: Markus Armbruster @ 2021-01-20  8:42 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> On 19/01/21 16:56, Markus Armbruster wrote:
>>> +            if (!is_help && warn_on_flag) {
>>> +                warn_report("short-form boolean option '%s%s' deprecated", prefix, *name);
>>> +                error_printf("Please use %s=%s instead\n", *name, *value);
>>> +            }
>> 
>> If @warn_on_flag, we warn except for "help" and "?".  The exception
>> applies regardless of @help_wanted.  Shouldn't we except*only*
>> recognized help requests?
>
> Suggesting "help=yes" would be worse.

Would it?

get_opt_name_value() parses one parameter from params into *name and
*value.  if help_wanted && is_help, it additionally sets *help_wanted to
true.  is_help is true when the parameter is "help" or "?".

How could a parameter "help" be handled?

get_opt_name_value() will set

    *name = g_strdup("help");
    *value = g_strdup("on");

If help_wanted, additionally:

    *help_wanted = true;

Callers that pass non-null help_wanted can do whatever they want with
that.  The actual callers do honor the help request.  The deprecation
warning obviously needs to be suppressed for them.

Callers that pass null help_wanted will treat this just like any other
parameter.  Use of the boolean sugar is just as deprecated for this
parameter as it is for all the others.  Suppressing the deprecation
warning feels wrong.

The alternative is to *outlaw* parameters "help" and "?" in QemuOpts.
I'd be cool with that.

>>> -    opts = opts_parse(list, params, permit_abbrev, false,
>>> +    opts = opts_parse(list, params, permit_abbrev, false, true,
>>>                        opts_accepts_any(list) ? NULL : &help_wanted,
>>>                        &err);
>>>      if (!opts) {
>> 
>> This function now warns, except for "help" and "?".  The exception
>> applies even when we treat "help" and "?" as sugar for "help=on" and
>> "?=on" because opts_accepts_any().
>> 
>> It is the only spot that enables the warning.
>> 
>> Does all user input flow through qemu_opts_parse_noisily()?
>> 
>
> I was going to say yes, but -vnc (and worse, the QMP version of "change 
> vnc") is parsed by qemu_opts_parse() via ui/vnc.c (besides being used by 
> lots of tests).  -vnc has several boolean options, and though Libvirt 
> only uses "sasl" it does so in the short form.
>
> My solution would be to deprecate the QMP "change vnc" command, and 
> postpone switching -vnc to qemu_opts_parse_noisily to 6.2.

QMP command 'change' was deprecated long ago, in 2.5.0 (commit
24fb41330, in 2015).  This predated appendix "Deprecated features"
(which has since become docs/system/deprecated.rst), and remained
missing there until I corrected it in commit 6d570ca10 (v4.2.0).

> The main reason to warn for short-form boolean options, is to block them 
> for command line options that are switched to keyval[1].  Adding a 
> warning does not necessarily imply removing in two releases.

Understand.

> Paolo
>
> [1] This series already does that for -M, -accel and -object.  This 
> means that applying this series would change the command line 
> incompatibly without a two-release deprecation.  It's up for discussion 
> whether to do so, or delay the application of those patches to 6.2.  It 
> would be a pity to hold the dependent changes for effectively a year, 
> but it's not a big deal.

Concur.



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 01/25] qemu-option: clean up id vs. list->merge_lists
  2021-01-20  8:03       ` Markus Armbruster
@ 2021-01-20 12:37         ` Paolo Bonzini
  2021-01-20 12:50           ` Markus Armbruster
  0 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-20 12:37 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 20/01/21 09:03, Markus Armbruster wrote:
> The detour should be avoided, because QemuOpts should be avoided.  Using
> the appropriate visitor, we get:
> 
>          char *optarg
>               |
>               |  v = qobject_input_visitor_new_str(string, NULL, errp)
>               |  visit_type_q_obj_set_action_arg(v, NULL, &arg, errp);
>               v
>    q_obj_set_action_arg arg
> 
> except visit_type_q_obj_set_action_arg() doesn't exist, because the QAPI
> type is anonymous.  So give it a name:
> 
>      { 'struct: 'Action',
>        'data': { '*reboot': 'RebootAction',
>                  '*shutdown': 'ShutdownAction',
>                  '*panic': 'PanicAction',
>                  '*watchdog': 'WatchdogAction' } }
> 
>      { 'command': 'set-action',
>        'data': 'Action',
>        'allow-preconfig': true }
> 
>          char *optarg
>               |
>               |  v = qobject_input_visitor_new_str(string, NULL, errp)
>               |  visit_type_Action(v, NULL, &arg, errp);
>               v
>           Action act
> 
> To avoid having to pass the members of Action to qmp_set_action(), throw
> in 'boxed': true, so you can simply call qmp_set_action(&act, errp).

Ok, so the idea of a 1:1 CLI<->QMP mapping is good, the implementation 
is bad.  The reason it was done with QemuOpts was mostly to have 
"-action help" for free.  Something to remember when working on 
autogenerated boilerplate.

Paolo



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 03/25] qemu-option: warn for short-form boolean options
  2021-01-20  8:42       ` Markus Armbruster
@ 2021-01-20 12:40         ` Paolo Bonzini
  2021-01-20 12:59           ` Markus Armbruster
  0 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-20 12:40 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 20/01/21 09:42, Markus Armbruster wrote:
> The alternative is to *outlaw* parameters "help" and "?" in QemuOpts.
> I'd be cool with that. >
>> My solution would be to deprecate the QMP "change vnc" command, and
>> postpone switching -vnc to qemu_opts_parse_noisily to 6.2.
> 
> QMP command 'change' was deprecated long ago, in 2.5.0 (commit
> 24fb41330, in 2015).  This predated appendix "Deprecated features"
> (which has since become docs/system/deprecated.rst), and remained
> missing there until I corrected it in commit 6d570ca10 (v4.2.0).

Removal patch coming then, together with switching vnc_parse to 
qemu_opts_parse_noisily.

That would restrict qemu_opts_parse to tests, and implicitly outlaw 
parameters "help" and "?".  The other problem would be solved, albeit a 
bit indirectly.

>> The main reason to warn for short-form boolean options, is to block them
>> for command line options that are switched to keyval[1].  Adding a
>> warning does not necessarily imply removing in two releases.
> 
> Understand.
> 
>> [1] This series already does that for -M, -accel and -object.  This
>> means that applying this series would change the command line
>> incompatibly without a two-release deprecation.  It's up for discussion
>> whether to do so, or delay the application of those patches to 6.2.  It
>> would be a pity to hold the dependent changes for effectively a year,
>> but it's not a big deal.
> 
> Concur.

Verbose please. :)  Do you think we should delay the conversion of 
-M/-accel/-object to keyval until 6.2?

Paolo



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 01/25] qemu-option: clean up id vs. list->merge_lists
  2021-01-20 12:37         ` Paolo Bonzini
@ 2021-01-20 12:50           ` Markus Armbruster
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Armbruster @ 2021-01-20 12:50 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> On 20/01/21 09:03, Markus Armbruster wrote:
>> The detour should be avoided, because QemuOpts should be avoided.  Using
>> the appropriate visitor, we get:
>>          char *optarg
>>               |
>>               |  v = qobject_input_visitor_new_str(string, NULL, errp)
>>               |  visit_type_q_obj_set_action_arg(v, NULL, &arg, errp);
>>               v
>>    q_obj_set_action_arg arg
>> except visit_type_q_obj_set_action_arg() doesn't exist, because the
>> QAPI
>> type is anonymous.  So give it a name:
>>      { 'struct: 'Action',
>>        'data': { '*reboot': 'RebootAction',
>>                  '*shutdown': 'ShutdownAction',
>>                  '*panic': 'PanicAction',
>>                  '*watchdog': 'WatchdogAction' } }
>>      { 'command': 'set-action',
>>        'data': 'Action',
>>        'allow-preconfig': true }
>>          char *optarg
>>               |
>>               |  v = qobject_input_visitor_new_str(string, NULL, errp)
>>               |  visit_type_Action(v, NULL, &arg, errp);
>>               v
>>           Action act
>> To avoid having to pass the members of Action to qmp_set_action(),
>> throw
>> in 'boxed': true, so you can simply call qmp_set_action(&act, errp).
>
> Ok, so the idea of a 1:1 CLI<->QMP mapping is good, the implementation
> is bad.  The reason it was done with QemuOpts was mostly to have 
> "-action help" for free.  Something to remember when working on
> autogenerated boilerplate.

Yes, help is another gap we still need to bridge.



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 03/25] qemu-option: warn for short-form boolean options
  2021-01-20 12:40         ` Paolo Bonzini
@ 2021-01-20 12:59           ` Markus Armbruster
  2021-01-20 14:05             ` Paolo Bonzini
  0 siblings, 1 reply; 67+ messages in thread
From: Markus Armbruster @ 2021-01-20 12:59 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> On 20/01/21 09:42, Markus Armbruster wrote:
>> The alternative is to *outlaw* parameters "help" and "?" in QemuOpts.
>> I'd be cool with that. >
>>> My solution would be to deprecate the QMP "change vnc" command, and
>>> postpone switching -vnc to qemu_opts_parse_noisily to 6.2.
>> 
>> QMP command 'change' was deprecated long ago, in 2.5.0 (commit
>> 24fb41330, in 2015).  This predated appendix "Deprecated features"
>> (which has since become docs/system/deprecated.rst), and remained
>> missing there until I corrected it in commit 6d570ca10 (v4.2.0).
>
> Removal patch coming then, together with switching vnc_parse to 
> qemu_opts_parse_noisily.
>
> That would restrict qemu_opts_parse to tests, and implicitly outlaw 
> parameters "help" and "?".  The other problem would be solved, albeit a 
> bit indirectly.

Please remember to mention the outlaws in a commit message.

>>> The main reason to warn for short-form boolean options, is to block them
>>> for command line options that are switched to keyval[1].  Adding a
>>> warning does not necessarily imply removing in two releases.
>> 
>> Understand.
>> 
>>> [1] This series already does that for -M, -accel and -object.  This
>>> means that applying this series would change the command line
>>> incompatibly without a two-release deprecation.  It's up for discussion
>>> whether to do so, or delay the application of those patches to 6.2.  It
>>> would be a pity to hold the dependent changes for effectively a year,
>>> but it's not a big deal.
>> 
>> Concur.
>
> Verbose please. :)  Do you think we should delay the conversion of 
> -M/-accel/-object to keyval until 6.2?

I concurred with "it's up for discussion".  I'm happy do discuss, of
course.

Delaying reduces (but does not eliminate) the risk of breaking stuff
that uses the sugar.  Is that worth the loss of momentum?  Hard to say.

I think we can ignore non-boolean parameters, because if you're using
the sugar with those, you're kind of begging for some punishment :)

What are the boolean parameters, and is the *any* evidence of use with
the sugar in the wild?



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 03/25] qemu-option: warn for short-form boolean options
  2021-01-20 12:59           ` Markus Armbruster
@ 2021-01-20 14:05             ` Paolo Bonzini
  0 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-20 14:05 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 20/01/21 13:59, Markus Armbruster wrote:
>> Verbose please.   Do you think we should delay the conversion of 
>> -M/-accel/-object to keyval until 6.2?
> 
> I concurred with "it's up for discussion".  I'm happy do discuss, of
> course.

> What are the boolean parameters, and is there *any* evidence of use with
> the sugar in the wild?

There is no evidence for -machine, -accel or -object.

Notably (and for Libvirt only):

- '-object' takes a detour through JSON, and uses sometimes =on/=off or 
  sometimes =yes/=no.

- '-accel' is not used at all

- '-machine' does not use short-form boolean options by sheer luck.

The only known usage in Libvirt, for instance, is for "-chardev" and "-vnc".

Paolo



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 04/25] keyval: accept escaped commas in implied option
  2021-01-18 16:30 ` [PATCH 04/25] keyval: accept escaped commas in implied option Paolo Bonzini
@ 2021-01-21 12:58   ` Markus Armbruster
  2021-01-22  8:39   ` Markus Armbruster
  1 sibling, 0 replies; 67+ messages in thread
From: Markus Armbruster @ 2021-01-21 12:58 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> This is used with the weirdly-named device "SUNFD,fdtwo":

"SUNW,fdtwo"

Suggest "with weirdly-named devices such as "SUNW,fdtwo:", because we've
got more weirdos.

>   $ qemu-system-sparc -device SUNW,,fdtwo,help
>   SUNW,fdtwo options:
>     drive=<str>            - Node name or ID of a block device to use as a backend
>     fallback=<FdcDriveType> - FDC drive type, 144/288/120/none/auto (default: "144")
>     ...
>
> Therefore, accepting it is a preparatory step towards keyval-ifying
> -device and the device_add monitor command.

It's a preparatory step, but is it a necessary one?  More on that below.

>                                              In general, however, this
> unexpected wart of the keyval syntax leads to suboptimal errors compared
> to QemuOpts:
>
>   $ ./qemu-system-x86_64 -object foo,,bar,id=obj
>   qemu-system-x86_64: -object foo,,bar,id=obj: invalid object type: foo,bar
>   $ storage-daemon/qemu-storage-daemon --object foo,,bar,id=obj
>   qemu-storage-daemon: Invalid parameter ''

This is a second, independent argument supporting your patch.

As I remarked in reply to a prior post as "[PATCH 1/2] keyval: accept
escaped commas in implied option", the suboptimal errors could be
improved in a less invasive way.  Your way has a distinct advantage,
though: a working patch.

A third argument you've put forward elsewhere, but modestly left out
here: nicer code.  I'll get back to it after looking at the followup
cleanup in the next patch.

Either one argument could justify the patch, I think.

I'm this explicit to avoid the impression that the critique of the first
argument that comes next is me trying to find a reason to shoot down
your patch.

I don't think -device *needs* to accept anti-social device names.

We have a few devices with anti-social names, but none of them works
with -device, except in a help request.

We don't have to keep requests for human-readable help backwards
compible.

Anti-social device names are a usability issue with or without this
patch, with or without keyvalified -device.  The patch ensures the
sugared form of the help request continues to work after keyvalification
(the unsugared from is unaffected).  You could argue that loss of the
sugared form is a usability regression.  Maybe.  But usability is *poor*
in any case.  If we really cared for it, we'd get rid of the anti-social
names.

My point is: we're sitting in a hole, and the commit message starts with
"we need to dig a bit deeper to keep us comfortable".

My first preference: get rid of the anti-social names, drop the first
argument from the commit message, and let the patch rest on the other
two.

Second preference: rephrase the commit message along the lines of "This
is a step towards keyval-ifying -device without fixing the anti-social
device names first, and without breaking backward compatibility for help
requests".

> To implement this, the flow of the parser is changed to first unescape
> everything up to the next comma or equal sign.  This is done in a
> new function keyval_fetch_string for both the key and value part.
> Keys therefore are now parsed in unescaped form, but this makes no
> difference in practice because a comma is an invalid character for a
> QAPI name.  Thus keys with a comma in them are rejected anyway, as
> demonstrated by the new testcase.
>
> As a side effect of the new code, parse errors are slightly improved as
> well: "Invalid parameter ''" becomes "Expected parameter before '='"
> when keyval is fed a string starting with an equal sign.
>
> The slightly baroque interface of keyval_fetch_string lets me keep the
> key parsing loop mostly untouched.  It is simplified in the next patch,
> however.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

I'll now look at the next patch, then get back to this one.



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 04/25] keyval: accept escaped commas in implied option
  2021-01-18 16:30 ` [PATCH 04/25] keyval: accept escaped commas in implied option Paolo Bonzini
  2021-01-21 12:58   ` Markus Armbruster
@ 2021-01-22  8:39   ` Markus Armbruster
  1 sibling, 0 replies; 67+ messages in thread
From: Markus Armbruster @ 2021-01-22  8:39 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

[...]
> diff --git a/util/keyval.c b/util/keyval.c
> index be34928813..eb9b9c55ec 100644
> --- a/util/keyval.c
> +++ b/util/keyval.c
> @@ -16,8 +16,8 @@
>   *   key-vals     = [ key-val { ',' key-val } [ ',' ] ]
>   *   key-val      = key '=' val | help
>   *   key          = key-fragment { '.' key-fragment }
> - *   key-fragment = / [^=,.]+ /
> - *   val          = { / [^,]+ / | ',,' }
> + *   key-fragment = { / [^=,.] / | ',,' }
> + *   val          = { / [^,] / | ',,' }
>   *   help         = 'help' | '?'
>   *
>   * Semantics defined by reduction to JSON:
> @@ -78,13 +78,13 @@
>   * Alternative syntax for use with an implied key:
>   *
>   *   key-vals     = [ key-val-1st { ',' key-val } [ ',' ] ]
> - *   key-val-1st  = val-no-key | key-val
> - *   val-no-key   = / [^=,]+ / - help
> + *   key-val-1st  = (val-no-key - help) | key-val
> + *   val-no-key   = { / [^=,] / | ',,' }

I finally remembered why I made val-no-key non-empty: to avoid amiguity.

Before the patch, "" can only be parsed as empty key-vals.  Results in
an empty QDict.

Afterwards, the grammar is ambiguous: "" can also be parsed as empty
val-no-key, reduced via key-val-1st to non-empty key-vals.  Results in a
QDict with one entry mapping the implied key to "".

I'm a bit concerned I similarly forgot something that made me avoid ',,'
escapes in val-no-key.

Even if we brushed off the ambiguous grammar issue (and we should not!),
desugaring "" into "implied=" feels unwise, and ",k=v" into
"implied=,k=v" only slightly less so.

Let's keep val-no-key non-empty.

Ripple effect...  I made val-no-key match key (almost):

    val-no-key   = / [^=,]+ /

    key          = key-fragment { '.' key-fragment }
    key-fragment = / [^=,.]+ /

The only difference is val-no-key accepts consecutive '.'.

Commit 8bf12c4f75 "keyval: Parse help options" muddied the waters a bit
by adding '- help' to val-no-key.

Your commit moves it to key-val-1st (good).  It also permits empty
key-fragment, and thus consecutive '.' (good because it makes val-no-key
match key exactly, but also possibly confusing because key-fragment
can't actually be empty due to the "Key-fragments must be valid QAPI
names or consist only of decimal digits" condition).  Okay.

It also changed both val-no-key and key to accept empty.  We need to
keep *both* non-empty.

Your change to val is not wrong, but I prefer the old version, because
it's closer to how the code works.

>   *
>   * where val-no-key is syntactic sugar for implied-key=val-no-key.
>   *
> - * Note that you can't use the sugared form when the value contains
> - * '=' or ','.
> + * Note that you can't use the sugared form when the value is empty

You can with your grammar change, unless the code doesn't match the
grammar.  Which would be a bug.

> + * or contains '='.
>   */
[...]

I apologize for sitting on this patch for so long.  Something felt
wrong, but I couldn't put a finger on it.  Now I can at least for the
empty val-no-key part.



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 05/25] keyval: simplify keyval_parse_one
  2021-01-18 16:30 ` [PATCH 05/25] keyval: simplify keyval_parse_one Paolo Bonzini
@ 2021-01-22 13:48   ` Markus Armbruster
  2021-01-22 15:00     ` Paolo Bonzini
  0 siblings, 1 reply; 67+ messages in thread
From: Markus Armbruster @ 2021-01-22 13:48 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> Now that the key is NULL terminated, we can remove some of the contortions
> that were done to operate on possibly '='-terminated strings in
> keyval_parse_one.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Alright, I'm now ready to discuss the third argument: nicer code.

I think there is improvement, and I think it comes mostly from working
on a copy.  Could be done without the syntax change.

I feel further improvement is possible, but that's no reason to delay
this series.


Second argument: better error messages.  We discussed that in review of
a prior post, but you've since improved your error reporting further, so
let's have another look.  I'm testing with

    $ qemu-storage-daemon --nbd $ARG

because that one doesn't have an implied key, which permits slightly
simpler $ARG.

* Empty key

  --nbd ,

    master:       Invalid parameter ''
    your patch:   Expected parameter before ','

    Improvement.

  --nbd key=val,=,fob=

    master:       Invalid parameter ''
    your patch:   Expected parameter before '=,fob='

    Improvement.

* Empty key fragment

  --nbd key..=

    master:       Invalid parameter 'key..'
    your patch:   same

    Could use some love.  Not your patch's fault.

  --nbd .key=

    master:       Invalid parameter '..key'
    your patch:   same

    Likweise.

  If I omit the '=', your patch's message changes to

                  No implicit parameter name for value 'key..'

  I consider that worse than before, because it's talking about
  something outside the user's control (lack of an implict parameter
  name) where it should instead tell the user what needs fixing in the
  input.

* Missing value

  --nbd key

    master:       Expected '=' after parameter 'key'
    your patch:   No implicit parameter name for value 'key'

  Same criticism as above.

* Invalid key fragment

  --nbd _=

    master:       Invalid parameter '_'
    your patch:   same

  --nbd key.1a.b=

    master:       Invalid parameter 'key.1a.b'
    your patch:   same

    Could perhaps use some love.  Not your patch's fault.

  --ndb anti,,social,,key=

    master:       Expected '=' after parameter 'anti'
    your patch:   Invalid parameter 'anti,social,key'

    The new error message shows the *unescaped* string.  Okay.

Your patch also adds an "Expected parameter at end of string" error.
Can you tell me how to trigger it?

I think there is improvement, and I think it could also be done without
the syntax change.

There also is one regression: "No implicit parameter name..." is no
good.  Looks fixable to me.

I feel further improvement is possible, but that's no reason to delay
this series.


Now let me put the three arguments together.

Nicer code and better error reporting could be had with or without the
syntax change.  With is a bit easier, because we already have your
patch.

The syntax change is a choice we can make.  I'm reluctant to mess with
the syntax, but if you want the change, I'm not going to block it.

Hmm, bartering opportunity...  May I have your support for me
eliminating anti-social device names in exchange?  ;)

I believe your grammar is ambiguous.  Your code seems to pick the sane
alternative.  If I'm wrong, you need to enlighten me.  If I'm right, you
need to fix your grammar.

> ---
>  util/keyval.c | 27 ++++++++++-----------------
>  1 file changed, 10 insertions(+), 17 deletions(-)
>
> diff --git a/util/keyval.c b/util/keyval.c
> index eb9b9c55ec..e7f708cd1e 100644
> --- a/util/keyval.c
> +++ b/util/keyval.c
> @@ -170,11 +170,10 @@ static QObject *keyval_parse_put(QDict *cur,
>   *
>   * On return:
>   * - either NUL or the separator (comma or equal sign) is returned.
> - * - the length of the string is stored in @len.
>   * - @start is advanced to either the NUL or the first character past the
>   *   separator.
>   */
> -static char keyval_fetch_string(char **start, size_t *len, bool key)
> +static char keyval_fetch_string(char **start, bool key)
>  {
>      char sep;
>      char *p, *unescaped;
> @@ -197,7 +196,6 @@ static char keyval_fetch_string(char **start, size_t *len, bool key)
>      }
>  
>      *unescaped = 0;
> -    *len = unescaped - *start;
>      *start = p;
>      return sep;
>  }
> @@ -219,7 +217,7 @@ static char *keyval_parse_one(QDict *qdict, char *params,
>                                const char *implied_key, bool *help,
>                                Error **errp)
>  {
> -    const char *key, *key_end, *s, *end;
> +    const char *key, *s, *end;
>      const char *val = NULL;
>      char sep;
>      size_t len;
> @@ -229,8 +227,8 @@ static char *keyval_parse_one(QDict *qdict, char *params,
>      QObject *next;
>  
>      key = params;
> -    sep = keyval_fetch_string(&params, &len, true);
> -    if (!len) {
> +    sep = keyval_fetch_string(&params, true);
> +    if (!*key) {
>          if (sep) {
>              error_setg(errp, "Expected parameter before '%c%s'", sep, params);
>          } else {
> @@ -247,13 +245,11 @@ static char *keyval_parse_one(QDict *qdict, char *params,
>              /* Desugar implied key */
>              val = key;
>              key = implied_key;
> -            len = strlen(implied_key);
>          } else {
>              error_setg(errp, "No implicit parameter name for value '%s'", key);
>              return NULL;
>          }
>      }
> -    key_end = key + len;
>  
>      /*
>       * Loop over key fragments: @s points to current fragment, it
> @@ -269,24 +265,21 @@ static char *keyval_parse_one(QDict *qdict, char *params,
>              ret = parse_qapi_name(s, false);
>              len = ret < 0 ? 0 : ret;
>          }
> -        assert(s + len <= key_end);
> -        if (!len || (s + len < key_end && s[len] != '.')) {
> +        if (!len || (s[len] != '\0' && s[len] != '.')) {
>              assert(key != implied_key);
> -            error_setg(errp, "Invalid parameter '%.*s'",
> -                       (int)(key_end - key), key);
> +            error_setg(errp, "Invalid parameter '%s'", key);
>              return NULL;
>          }
>          if (len >= sizeof(key_in_cur)) {
>              assert(key != implied_key);
>              error_setg(errp, "Parameter%s '%.*s' is too long",
> -                       s != key || s + len != key_end ? " fragment" : "",
> +                       s != key || s[len] == '.' ? " fragment" : "",
>                         (int)len, s);
>              return NULL;
>          }
>  
>          if (s != key) {
> -            next = keyval_parse_put(cur, key_in_cur, NULL,
> -                                    key, s - 1, errp);
> +            next = keyval_parse_put(cur, key_in_cur, NULL, key, s - 1, errp);

Unrelated line join.

>              if (!next) {
>                  return NULL;
>              }
> @@ -301,9 +294,9 @@ static char *keyval_parse_one(QDict *qdict, char *params,
>  
>      if (key != implied_key) {
>          val = params;
> -        keyval_fetch_string(&params, &len, false);
> +        keyval_fetch_string(&params, false);
>      }
> -    if (!keyval_parse_put(cur, key_in_cur, val, key, key_end, errp)) {
> +    if (!keyval_parse_put(cur, key_in_cur, val, key, s - 1, errp)) {
>          return NULL;
>      }
>      return params;



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 06/25] tests: convert check-qom-proplist to keyval
  2021-01-18 16:30 ` [PATCH 06/25] tests: convert check-qom-proplist to keyval Paolo Bonzini
@ 2021-01-22 14:14   ` Markus Armbruster
  2021-01-22 14:38     ` Paolo Bonzini
  2021-01-22 14:48     ` Paolo Bonzini
  0 siblings, 2 replies; 67+ messages in thread
From: Markus Armbruster @ 2021-01-22 14:14 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel, armbru

Paolo Bonzini <pbonzini@redhat.com> writes:

> The command-line creation test is using QemuOpts.  Switch it to keyval,
> since all the -object command line options will follow
> qemu-storage-daemon and do the same.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

$ gdb tests/check-qom-proplist
[...]
(gdb) r
[...]
# random seed: R02S802948119789b5481f33f9842e3b5d1b
1..9
# Start of qom tests
# Start of proplist tests
ok 1 /qom/proplist/createlist
ok 2 /qom/proplist/createv
Unexpected error in find_list() at ../util/qemu-config.c:24:
There is no option group 'object'

Thread 1 "check-qom-propl" received signal SIGABRT, Aborted.
0x00007ffff7b839e5 in raise () from /lib64/libc.so.6
Missing separate debuginfos, use: dnf debuginfo-install glib2-2.64.6-1.fc32.x86_64 pcre-8.44-2.fc32.x86_64
(gdb) bt
#0  0x00007ffff7b839e5 in raise () from /lib64/libc.so.6
#1  0x00007ffff7b6c895 in abort () from /lib64/libc.so.6
#2  0x000055555557fe08 in error_handle_fatal (
    errp=0x5555555aa300 <error_abort>, err=0x5555555b9580)
    at ../util/error.c:40
#3  0x000055555557ff38 in error_setv (errp=0x5555555aa300 <error_abort>, 
    src=0x5555555914f1 "../util/qemu-config.c", line=24, 
    func=0x555555591ad0 <__func__.5> "find_list", 
    err_class=ERROR_CLASS_GENERIC_ERROR, 
    fmt=0x5555555914d3 "There is no option group '%s'", ap=0x7fffffffd690, 
    suffix=0x0) at ../util/error.c:73
#4  0x0000555555580116 in error_setg_internal (
    errp=0x5555555aa300 <error_abort>, 
    src=0x5555555914f1 "../util/qemu-config.c", line=24, 
    func=0x555555591ad0 <__func__.5> "find_list", 
    fmt=0x5555555914d3 "There is no option group '%s'") at ../util/error.c:97
#5  0x000055555556fdb4 in find_list (lists=0x5555555aa060 <vm_config_groups>, 
    group=0x55555558e97a "object", errp=0x5555555aa300 <error_abort>)
    at ../util/qemu-config.c:24
#6  0x0000555555570426 in qemu_find_opts_err (group=0x55555558e97a "object", 
    errp=0x5555555aa300 <error_abort>) at ../util/qemu-config.c:275
#7  0x000055555555f8bd in user_creatable_del (id=0x55555558e233 "dev0", 
    errp=0x5555555aa300 <error_abort>) at ../qom/object_interfaces.c:312
#8  0x000055555555dc8e in test_dummy_createcmdl ()
    at ../tests/check-qom-proplist.c:439
#9  0x00007ffff7ef429e in g_test_run_suite_internal ()
   from /lib64/libglib-2.0.so.0
#10 0x00007ffff7ef409b in g_test_run_suite_internal ()
   from /lib64/libglib-2.0.so.0
#11 0x00007ffff7ef409b in g_test_run_suite_internal ()
   from /lib64/libglib-2.0.so.0
#12 0x00007ffff7ef478a in g_test_run_suite () from /lib64/libglib-2.0.so.0
#13 0x00007ffff7ef47a5 in g_test_run () from /lib64/libglib-2.0.so.0
#14 0x000055555555e98c in main (argc=1, argv=0x7fffffffdcf8)
    at ../tests/check-qom-proplist.c:655

> ---
>  tests/check-qom-proplist.c | 58 +++++++++++++++++++++++++-------------
>  1 file changed, 38 insertions(+), 20 deletions(-)
>
> diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c
> index 1b76581980..8dba26fb3c 100644
> --- a/tests/check-qom-proplist.c
> +++ b/tests/check-qom-proplist.c
> @@ -21,6 +21,8 @@
>  #include "qemu/osdep.h"
>  
>  #include "qapi/error.h"
> +#include "qapi/qmp/qdict.h"
> +#include "qapi/qmp/qobject.h"
>  #include "qom/object.h"

I expected this one to go.

>  #include "qemu/module.h"
>  #include "qemu/option.h"
> @@ -400,42 +402,58 @@ static void test_dummy_createlist(void)
>  
>  static void test_dummy_createcmdl(void)
>  {
> -    QemuOpts *opts;
> +    QDict *qdict;
>      DummyObject *dobj;
>      Error *err = NULL;
> +    bool help;
>      const char *params = TYPE_DUMMY \
>                           ",id=dev0," \
>                           "bv=yes,sv=Hiss hiss hiss,av=platypus";
>  
> -    qemu_add_opts(&qemu_object_opts);
> -    opts = qemu_opts_parse(&qemu_object_opts, params, true, &err);
> +    qdict = keyval_parse(params, "qom-type", &help, &err);

Removes the only use of qemu_object_opts.  You should remove
qemu_object_opts, too.

>      g_assert(err == NULL);
> -    g_assert(opts);
> +    g_assert(qdict);
> +    g_assert(!help);
>  
> -    dobj = DUMMY_OBJECT(user_creatable_add_opts(opts, &err));
> +    g_assert(user_creatable_add_dict(qdict, true, &err));
>      g_assert(err == NULL);
> +    qobject_unref(qdict);
> +
> +    dobj = DUMMY_OBJECT(object_resolve_path_component(object_get_objects_root(),
> +                                                      "dev0"));

Why does user_creatable_add_opts() return the object on success, null on
failure, but user_creatable_add_dict() only a rather less useful bool?

>      g_assert(dobj);
>      g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
>      g_assert(dobj->bv == true);
>      g_assert(dobj->av == DUMMY_PLATYPUS);
>  
> +    qdict = keyval_parse(params, "qom-type", &help, &err);

Why parse again?

> +    g_assert(!user_creatable_add_dict(qdict, true, &err));
> +    g_assert(err);

Use error_free_or_abort(&err) instead, and ...

> +    g_assert(object_resolve_path_component(object_get_objects_root(), "dev0")
> +             == OBJECT(dobj));
> +    qobject_unref(qdict);

... drop the next two lines:

> +    error_free(err);
> +    err = NULL;
> +
> +    qdict = keyval_parse(params, "qom-type", &help, &err);

And again?

>      user_creatable_del("dev0", &error_abort);
> +    g_assert(object_resolve_path_component(object_get_objects_root(), "dev0")
> +             == NULL);
>  
> -    object_unref(OBJECT(dobj));
> -
> -    /*
> -     * cmdline-parsing via qemu_opts_parse() results in a QemuOpts entry
> -     * corresponding to the Object's ID to be added to the QemuOptsList
> -     * for objects. To avoid having this entry conflict with future
> -     * Objects using the same ID (which can happen in cases where
> -     * qemu_opts_parse() is used to parse the object params, such as
> -     * with hmp_object_add() at the time of this comment), we need to
> -     * check for this in user_creatable_del() and remove the QemuOpts if
> -     * it is present.
> -     *
> -     * The below check ensures this works as expected.
> -     */
> -    g_assert_null(qemu_opts_find(&qemu_object_opts, "dev0"));
> +    g_assert(user_creatable_add_dict(qdict, true, &err));

Am I confused, or are you going from two to three creates?  Should this
be in a separate patch?

> +    g_assert(err == NULL);
> +    qobject_unref(qdict);
> +
> +    dobj = DUMMY_OBJECT(object_resolve_path_component(object_get_objects_root(),
> +                                                      "dev0"));
> +    g_assert(dobj);
> +    g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
> +    g_assert(dobj->bv == true);
> +    g_assert(dobj->av == DUMMY_PLATYPUS);
> +    g_assert(object_resolve_path_component(object_get_objects_root(), "dev0")
> +             == OBJECT(dobj));
> +
> +    object_unparent(OBJECT(dobj));
>  }
>  
>  static void test_dummy_badenum(void)



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 07/25] keyval: introduce keyval_parse_into
  2021-01-18 16:30 ` [PATCH 07/25] keyval: introduce keyval_parse_into Paolo Bonzini
@ 2021-01-22 14:22   ` Markus Armbruster
  2021-01-22 14:30     ` Paolo Bonzini
  0 siblings, 1 reply; 67+ messages in thread
From: Markus Armbruster @ 2021-01-22 14:22 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> Allow parsing multiple keyval sequences into the same dictionary.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

I trust later patches will make the need for this obvious.

I guess we could use qdict_join() instead.  Observation, not objection.

> ---
>  include/qemu/option.h |  2 ++
>  util/keyval.c         | 39 ++++++++++++++++++++++++++++++++-------
>  2 files changed, 34 insertions(+), 7 deletions(-)
>
> diff --git a/include/qemu/option.h b/include/qemu/option.h
> index f73e0dc7d9..092e291c37 100644
> --- a/include/qemu/option.h
> +++ b/include/qemu/option.h
> @@ -147,6 +147,8 @@ void qemu_opts_print_help(QemuOptsList *list, bool print_caption);
>  void qemu_opts_free(QemuOptsList *list);
>  QemuOptsList *qemu_opts_append(QemuOptsList *dst, QemuOptsList *list);
>  
> +QDict *keyval_parse_into(QDict *qdict, const char *params, const char *implied_key,
> +                         bool *p_help, Error **errp);
>  QDict *keyval_parse(const char *params, const char *implied_key,
>                      bool *help, Error **errp);
>  
> diff --git a/util/keyval.c b/util/keyval.c
> index e7f708cd1e..1d4ca12129 100644
> --- a/util/keyval.c
> +++ b/util/keyval.c
> @@ -436,13 +436,12 @@ static QObject *keyval_listify(QDict *cur, GSList *key_of_cur, Error **errp)
>   * If @p_help is not NULL, store whether help is requested there.
>   * If @p_help is NULL and help is requested, fail.
>   *
> - * On success, return a dictionary of the parsed keys and values.
> + * On success, return @dict, now filled with the parsed keys and values.
>   * On failure, store an error through @errp and return NULL.

May @dict be modified then?

>   */
> -QDict *keyval_parse(const char *params, const char *implied_key,
> -                    bool *p_help, Error **errp)
> +QDict *keyval_parse_into(QDict *qdict, const char *params, const char *implied_key,
> +                         bool *p_help, Error **errp)
>  {
> -    QDict *qdict = qdict_new();
>      QObject *listified;
>      g_autofree char *dup;
>      char *s;
> @@ -452,7 +451,6 @@ QDict *keyval_parse(const char *params, const char *implied_key,
>      while (*s) {
>          s = keyval_parse_one(qdict, s, implied_key, &help, errp);
>          if (!s) {
> -            qobject_unref(qdict);
>              return NULL;
>          }
>          implied_key = NULL;
> @@ -462,15 +460,42 @@ QDict *keyval_parse(const char *params, const char *implied_key,
>          *p_help = help;
>      } else if (help) {
>          error_setg(errp, "Help is not available for this option");
> -        qobject_unref(qdict);
>          return NULL;
>      }
>  
>      listified = keyval_listify(qdict, NULL, errp);
>      if (!listified) {
> -        qobject_unref(qdict);
>          return NULL;
>      }
>      assert(listified == QOBJECT(qdict));
>      return qdict;
>  }
> +
> +/*
> + * Parse @params in QEMU's traditional KEY=VALUE,... syntax.
> + *
> + * If @implied_key, the first KEY= can be omitted.  @implied_key is
> + * implied then, and VALUE can't be empty or contain ',' or '='.
> + *
> + * A parameter "help" or "?" without a value isn't added to the
> + * resulting dictionary, but instead is interpreted as help request.
> + * All other options are parsed and returned normally so that context
> + * specific help can be printed.
> + *
> + * If @p_help is not NULL, store whether help is requested there.
> + * If @p_help is NULL and help is requested, fail.
> + *
> + * On success, return a dictionary of the parsed keys and values.
> + * On failure, store an error through @errp and return NULL.
> + */
> +QDict *keyval_parse(const char *params, const char *implied_key,
> +                    bool *p_help, Error **errp)
> +{
> +    QDict *qdict = qdict_new();
> +    QDict *ret = keyval_parse_into(qdict, params, implied_key, p_help, errp);
> +
> +    if (!ret) {
> +        qobject_unref(qdict);
> +    }
> +    return ret;
> +}



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 07/25] keyval: introduce keyval_parse_into
  2021-01-22 14:22   ` Markus Armbruster
@ 2021-01-22 14:30     ` Paolo Bonzini
  0 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-22 14:30 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 22/01/21 15:22, Markus Armbruster wrote:
>> + * On success, return @dict, now filled with the parsed keys and values.
>>    * On failure, store an error through @errp and return NULL.
> May @dict be modified then?

Yes, it can.  Good point.

Paolo



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 06/25] tests: convert check-qom-proplist to keyval
  2021-01-22 14:14   ` Markus Armbruster
@ 2021-01-22 14:38     ` Paolo Bonzini
  2021-01-22 14:48     ` Paolo Bonzini
  1 sibling, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-22 14:38 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 22/01/21 15:14, Markus Armbruster wrote:
> ok 2 /qom/proplist/createv
> Unexpected error in find_list() at ../util/qemu-config.c:24:
> There is no option group 'object'

Hmm, maybe a semantic conflict when I rebased.  I'll take a look.

>> +    qdict = keyval_parse(params, "qom-type", &help, &err);
> 
> Why parse again?

I don't remember why I did it that way, but it does seem cleaner. 
Unlike QemuOpts, which are persistent until qemu_opts_del and can be 
retrieved later, keyval's result only survives as long as you want it to 
survive, and are leaked if you don't unref the qdict.  Parsing every 
time the object is created seems more similar to the way the vl.c code 
works.

>> -     * cmdline-parsing via qemu_opts_parse() results in a QemuOpts entry
>> -     * corresponding to the Object's ID to be added to the QemuOptsList
>> -     * for objects. To avoid having this entry conflict with future
>> -     * Objects using the same ID (which can happen in cases where
>> -     * qemu_opts_parse() is used to parse the object params, such as
>> -     * with hmp_object_add() at the time of this comment), we need to
>> -     * check for this in user_creatable_del() and remove the QemuOpts if
>> -     * it is present.
>> -     *
>> -     * The below check ensures this works as expected.
>> -     */
>> -    g_assert_null(qemu_opts_find(&qemu_object_opts, "dev0"));
>> +    g_assert(user_creatable_add_dict(qdict, true, &err));
> Am I confused, or are you going from two to three creates?  Should this
> be in a separate patch?

It's a different way to test for "you can create another object with the 
same id" you had before.  It used to check for NULL qemu_opts_find, now 
it checks directly that the creation succeeds.  I can put it in the 
commit message.

Paolo

>> +    g_assert(err == NULL);
>> +    qobject_unref(qdict);
>> +
>> +    dobj = DUMMY_OBJECT(object_resolve_path_component(object_get_objects_root(),
>> +                                                      "dev0"));
>> +    g_assert(dobj);
>> +    g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
>> +    g_assert(dobj->bv == true);
>> +    g_assert(dobj->av == DUMMY_PLATYPUS);
>> +    g_assert(object_resolve_path_component(object_get_objects_root(), "dev0")
>> +             == OBJECT(dobj));
>> +
>> +    object_unparent(OBJECT(dobj));



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 06/25] tests: convert check-qom-proplist to keyval
  2021-01-22 14:14   ` Markus Armbruster
  2021-01-22 14:38     ` Paolo Bonzini
@ 2021-01-22 14:48     ` Paolo Bonzini
  1 sibling, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-22 14:48 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 22/01/21 15:14, Markus Armbruster wrote:
>> +    dobj = DUMMY_OBJECT(object_resolve_path_component(object_get_objects_root(),
>> +                                                      "dev0"));
> Why does user_creatable_add_opts() return the object on success, null on
> failure, but user_creatable_add_dict() only a rather less useful bool?

I would guess that nobody needs it outside tests (I didn't look at the 
code).

>>       g_assert(dobj);
>>       g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
>>       g_assert(dobj->bv == true);
>>       g_assert(dobj->av == DUMMY_PLATYPUS);
>>   
>> +    qdict = keyval_parse(params, "qom-type", &help, &err);
>
> Why parse again?

Besides the justification of the previous email, user_creatable_add_dict 
modifies its QDict argument, removing the "qom-type" and "id" keys. 
Wart fixed in patch 16.

Paolo



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 05/25] keyval: simplify keyval_parse_one
  2021-01-22 13:48   ` Markus Armbruster
@ 2021-01-22 15:00     ` Paolo Bonzini
  2021-01-22 15:44       ` Markus Armbruster
  0 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-22 15:00 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 22/01/21 14:48, Markus Armbruster wrote:
>    --nbd .key=
> 
>      master:       Invalid parameter '..key'
>      your patch:   same
> 
>      Likweise.
> 
>    If I omit the '=', your patch's message changes to
> 
>                    No implicit parameter name for value 'key..'
> 
>    I consider that worse than before, because it's talking about
>    something outside the user's control (lack of an implict parameter
>    name) where it should instead tell the user what needs fixing in the
>    input.

I think whether it's better or worse depends on the specific erroneous 
command line (think "--nbd /path/to/file.qcow2"), but I can certainly 
change it.

> Your patch also adds an "Expected parameter at end of string" error.
> Can you tell me how to trigger it?

It is meant for "--nbd ''" but it is effectively dead code due to the 
"while (*s)" in the caller.  Possibilities:

1) leave it in as dead code

2) replace it with an assert

3) change the caller to use a do...while in such a way that it triggers 
it (and be careful not to change the grammar).

> I believe your grammar is ambiguous.  Your code seems to pick the sane
> alternative.  If I'm wrong, you need to enlighten me.  If I'm right, you
> need to fix your grammar.

Will do.  Can I change the EBNF to use "+" and "*" for simplicity and 
clarity?

> Hmm, bartering opportunity...  May I have your support for me
> eliminating anti-social device names in exchange?  

I do not mind removing them.  What's the barter exactly like? :)

Paolo



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 05/25] keyval: simplify keyval_parse_one
  2021-01-22 15:00     ` Paolo Bonzini
@ 2021-01-22 15:44       ` Markus Armbruster
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Armbruster @ 2021-01-22 15:44 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> On 22/01/21 14:48, Markus Armbruster wrote:
>>    --nbd .key=
>>      master:       Invalid parameter '..key'
>>      your patch:   same
>>      Likweise.
>>    If I omit the '=', your patch's message changes to
>>                    No implicit parameter name for value 'key..'
>>    I consider that worse than before, because it's talking about
>>    something outside the user's control (lack of an implict parameter
>>    name) where it should instead tell the user what needs fixing in the
>>    input.
>
> I think whether it's better or worse depends on the specific erroneous
> command line (think "--nbd /path/to/file.qcow2"), but I can certainly 
> change it.

Quoting myself: Error messages based on guesses what the user has in
mind can be quite confusing when we guess wrong.  A strictly factual
syntax error style like "I expected FOO instead of BAR here" may not be
great, but has a relatively low risk of being confusing.

>> Your patch also adds an "Expected parameter at end of string" error.
>> Can you tell me how to trigger it?
>
> It is meant for "--nbd ''" but it is effectively dead code due to the
> "while (*s)" in the caller.  Possibilities:
>
> 1) leave it in as dead code

I'd prefer not to.

> 2) replace it with an assert

Works for me.

> 3) change the caller to use a do...while in such a way that it
> triggers it (and be careful not to change the grammar).

Only if it's not too hard, and results in better error messages.

>> I believe your grammar is ambiguous.  Your code seems to pick the sane
>> alternative.  If I'm wrong, you need to enlighten me.  If I'm right, you
>> need to fix your grammar.
>
> Will do.  Can I change the EBNF to use "+" and "*" for simplicity and
> clarity?

I tried to stick to ISO 14977 as described in
<https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form>, less
its foolish ',' for concatenation.  I'm not at all enamored with it.
All I want is something that is widely understood, and preferably not
invented here.  Switch to <https://www.w3.org/TR/xml/#sec-notation>
perhaps?

[...]



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 08/25] hmp: replace "O" parser with keyval
  2021-01-18 16:30 ` [PATCH 08/25] hmp: replace "O" parser with keyval Paolo Bonzini
@ 2021-01-25  9:00   ` Markus Armbruster
  2021-02-26 11:25     ` Paolo Bonzini
  2021-03-01 10:43     ` Markus Armbruster
  0 siblings, 2 replies; 67+ messages in thread
From: Markus Armbruster @ 2021-01-25  9:00 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> HMP is using QemuOpts to parse free-form commands device_add,
> netdev_add and object_add.  However, none of these need QemuOpts
> for validation (these three QemuOptsLists are all of the catch-all
> kind), and keyval is already able to parse into QDict.  So use
> keyval directly, avoiding the detour from
> string to QemuOpts to QDict.
>
> The args_type now stores the implied key.  This arguably makes more
> sense than storing the QemuOptsList name; at least, it _is_ a key
> that might end up in the arguments QDict.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Switching from QemuOpts to keyval changes the accepted language.  We may
change it, because HMP is not a stable interface.  The commit message
should point out the change, though.  Maybe even release notes, not
sure.

Let's recap the differences briefly:

* Boolean sugar: deprecated in QemuOpts, nonexistent in keyval

* QemuOpts accepts a number of more or less crazy corner cases keyval
  rejects: invalid key, long key (silently truncated), first rather than
  last id= wins (unlike other keys), implied key with empty value.

* QemuOpts rejects anti-social ID such as id=666, keyval leaves this to
  the caller, because key "id" is not special in keyval.

  Are these still rejected with your patch?

> ---
>  hmp-commands.hx |  6 +++---
>  monitor/hmp.c   | 20 +++++++++-----------
>  2 files changed, 12 insertions(+), 14 deletions(-)
>
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index 73e0832ea1..6ee746b53e 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -669,7 +669,7 @@ ERST
>  
>      {
>          .name       = "device_add",
> -        .args_type  = "device:O",
> +        .args_type  = "driver:O",
>          .params     = "driver[,prop=value][,...]",
>          .help       = "add device, like -device on the command line",
>          .cmd        = hmp_device_add,
> @@ -1315,7 +1315,7 @@ ERST
>  
>      {
>          .name       = "netdev_add",
> -        .args_type  = "netdev:O",
> +        .args_type  = "type:O",
>          .params     = "[user|tap|socket|vde|bridge|hubport|netmap|vhost-user],id=str[,prop=value][,...]",
>          .help       = "add host network device",
>          .cmd        = hmp_netdev_add,
> @@ -1343,7 +1343,7 @@ ERST
>  
>      {
>          .name       = "object_add",
> -        .args_type  = "object:O",
> +        .args_type  = "qom-type:O",
>          .params     = "[qom-type=]type,id=str[,prop=value][,...]",
>          .help       = "create QOM object",
>          .cmd        = hmp_object_add,
> diff --git a/monitor/hmp.c b/monitor/hmp.c
> index 6c0b33a0b1..d2cb886da5 100644
> --- a/monitor/hmp.c
> +++ b/monitor/hmp.c
> @@ -744,13 +744,9 @@ static QDict *monitor_parse_arguments(Monitor *mon,
>              break;
>          case 'O':
>              {
> -                QemuOptsList *opts_list;
> -                QemuOpts *opts;
> +                Error *errp;
> +                bool help;
>  
> -                opts_list = qemu_find_opts(key);
> -                if (!opts_list || opts_list->desc->name) {
> -                    goto bad_type;
> -                }
>                  while (qemu_isspace(*p)) {
>                      p++;
>                  }
> @@ -760,12 +756,14 @@ static QDict *monitor_parse_arguments(Monitor *mon,
>                  if (get_str(buf, sizeof(buf), &p) < 0) {
>                      goto fail;
>                  }
> -                opts = qemu_opts_parse_noisily(opts_list, buf, true);
> -                if (!opts) {
> -                    goto fail;
> +                keyval_parse_into(qdict, buf, key, &help, &errp);
> +                if (help) {

Uh...

> +                    if (qdict_haskey(qdict, key)) {

If we parsed a value for the implied key (sugared or not),

> +                        qdict_put_bool(qdict, "help", true);

then encode the help request by mapping key "help" to true,

> +                    } else {
> +                        qdict_put_str(qdict, key, "help");

else by mapping the implied key to "help".

> +                    }

Test cases:

* device_add help

  @qdict before the patch:

    {
        "driver": "help"
    }

  No change.

* device_add e1000,help

  @qdict before the patch:

    {
        "driver": "e1000",
        "help": "on"
    }

  Afterwards:

    {
        "driver": "e1000",
        "help": true
    }

  If this is okay, the commit message should explain it.

* device_add help,e1000

    {
        "e1000": "on",
        "driver": "help"
    }

  Afterwards:
  upstream-qemu: ../util/error.c:59: error_setv: Assertion `*errp == NULL' failed.

>                  }
> -                qemu_opts_to_qdict(opts, qdict);
> -                qemu_opts_del(opts);
>              }
>              break;
>          case '/':



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 15/25] vl: plumb keyval-based options into -set and -readconfig
  2021-01-18 16:31 ` [PATCH 15/25] vl: plumb keyval-based options into -set and -readconfig Paolo Bonzini
@ 2021-01-25 11:48   ` Markus Armbruster
  2021-01-25 13:59     ` Paolo Bonzini
  0 siblings, 1 reply; 67+ messages in thread
From: Markus Armbruster @ 2021-01-25 11:48 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> Add generic machinery to support parsing command line options with
> keyval in -set and -readconfig, choosing between QDict and
> QemuOpts as the underlying data structure.
>
> The keyval_merge function is slightly heavyweight as a way to
> do qemu_set_option for QDict-based options, but it will be put
> to further use later to merge entire -readconfig sections together
> with their command-line equivalents.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Watch this:

    $ cat test.cfg
    [machine]
      accel = "kvm"
      usb = "on"
    $ qemu-system-x86_64 -S -display none -readconfig test.cfg
    Aborted (core dumped)

Backtrace:

    #0  0x00007ffff52f19e5 in raise () at /lib64/libc.so.6
    #1  0x00007ffff52da895 in abort () at /lib64/libc.so.6
    #2  0x0000555555c44a77 in qemu_record_config_group
        (group=0x7fffffffd1b0 "machine", dict=0x5555565fb740, errp=0x5555564ffca0 <error_fatal>) at ../softmmu/vl.c:2103
    #3  0x0000555555c44bd8 in qemu_parse_config_group
        (group=0x7fffffffd1b0 "machine", qdict=0x5555565f9640, opaque=0x5555564ff9e0 <vm_config_groups>, errp=0x5555564ffca0 <error_fatal>) at ../softmmu/vl.c:2135
    #4  0x0000555555eda3e6 in qemu_config_foreach
        (fp=0x5555565f3e00, cb=0x555555c44af5 <qemu_parse_config_group>, opaque=0x5555564ff9e0 <vm_config_groups>, fname=0x7fffffffe0dd "test.cfg", errp=0x5555564ffca0 <error_fatal>) at ../util/qemu-config.c:378
    #5  0x0000555555eda5d5 in qemu_read_config_file
        (filename=0x7fffffffe0dd "test.cfg", cb=0x555555c44af5 <qemu_parse_config_group>, errp=0x5555564ffca0 <error_fatal>) at ../util/qemu-config.c:421
    #6  0x0000555555c47d3f in qemu_init
        (argc=6, argv=0x7fffffffdcc8, envp=0x7fffffffdd00) at ../softmmu/vl.c:3405
    #7  0x00005555558234e1 in main
        (argc=6, argv=0x7fffffffdcc8, envp=0x7fffffffdd00) at ../softmmu/main.c:49

Similar result for

    [memory]
      size = "1024"

and

    [chardev "mon0"]
      backend = "stdio"

I didn't look for more.



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 09/25] qom: use qemu_printf to print help for user-creatable objects
  2021-01-18 16:30 ` [PATCH 09/25] qom: use qemu_printf to print help for user-creatable objects Paolo Bonzini
@ 2021-01-25 12:47   ` Markus Armbruster
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Armbruster @ 2021-01-25 12:47 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> This is needed when we add help support for object_add.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Reviewed-by: Markus Armbruster <armbru@redhat.com>



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 10/25] hmp: special case help options for object_add
  2021-01-18 16:30 ` [PATCH 10/25] hmp: special case help options for object_add Paolo Bonzini
@ 2021-01-25 12:48   ` Markus Armbruster
  2021-01-25 12:49     ` Paolo Bonzini
  0 siblings, 1 reply; 67+ messages in thread
From: Markus Armbruster @ 2021-01-25 12:48 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> Fix "object_add help" and "object_add TYPE,help".
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Standard question when I read "Fix $interface" in a commit message: how
exactly is it broken?



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 10/25] hmp: special case help options for object_add
  2021-01-25 12:48   ` Markus Armbruster
@ 2021-01-25 12:49     ` Paolo Bonzini
  2021-01-25 14:02       ` Markus Armbruster
  0 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-25 12:49 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 25/01/21 13:48, Markus Armbruster wrote:
> Paolo Bonzini <pbonzini@redhat.com> writes:
> 
>> Fix "object_add help" and "object_add TYPE,help".
>>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> 
> Standard question when I read "Fix $interface" in a commit message: how
> exactly is it broken?

It doesn't work at all ("Error: parameter 'id' is missing").

Paolo



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 11/25] remove -writeconfig
  2021-01-18 16:30 ` [PATCH 11/25] remove -writeconfig Paolo Bonzini
@ 2021-01-25 12:53   ` Markus Armbruster
  2021-01-25 14:01     ` Paolo Bonzini
  0 siblings, 1 reply; 67+ messages in thread
From: Markus Armbruster @ 2021-01-25 12:53 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> Like -set and -readconfig, it would not really be too hard to
> extend -writeconfig to parsing mechanisms other than QemuOpts.
> However, the uses of -writeconfig are substantially more
> limited, as it is generally easier to write the configuration
> by hand in the first place.  In addition, -writeconfig does
> not even try to detect cases where it prints incorrect
> syntax (for example if values have a quote in them, since
> qemu_config_parse does not support any kind of escaping.
> Just remove it.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

I love the "and how give me a config file for all that" idea, but I
agree our -writeconfig is flawed.  I hope we can bring it back in more
useful shape.

No deprecation grace period?

> ---
>  include/qemu/config-file.h |  1 -
>  qemu-options.hx            | 13 ++----------
>  softmmu/vl.c               | 19 -----------------
>  util/qemu-config.c         | 42 --------------------------------------
>  4 files changed, 2 insertions(+), 73 deletions(-)
>
> diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h
> index 29226107bd..7d26fe3816 100644
> --- a/include/qemu/config-file.h
> +++ b/include/qemu/config-file.h
> @@ -10,7 +10,6 @@ void qemu_add_opts(QemuOptsList *list);
>  void qemu_add_drive_opts(QemuOptsList *list);
>  int qemu_global_option(const char *str);
>  
> -void qemu_config_write(FILE *fp);
>  int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname);
>  
>  int qemu_read_config_file(const char *filename);
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 62791f56d8..7480b6a03f 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -4301,23 +4301,14 @@ SRST
>  ERST
>  
>  DEF("readconfig", HAS_ARG, QEMU_OPTION_readconfig,
> -    "-readconfig <file>\n", QEMU_ARCH_ALL)
> +    "-readconfig <file>\n"
> +    "                read config file\n", QEMU_ARCH_ALL)

Sure this belongs?

>  SRST
>  ``-readconfig file``
>      Read device configuration from file. This approach is useful when
>      you want to spawn QEMU process with many command line options but
>      you don't want to exceed the command line character limit.
>  ERST
> -DEF("writeconfig", HAS_ARG, QEMU_OPTION_writeconfig,
> -    "-writeconfig <file>\n"
> -    "                read/write config file\n", QEMU_ARCH_ALL)
> -SRST
> -``-writeconfig file``
> -    Write device configuration to file. The file can be either filename
> -    to save command line and device configuration into file or dash
> -    ``-``) character to print the output to stdout. This can be later
> -    used as input file for ``-readconfig`` option.
> -ERST
>  
>  DEF("no-user-config", 0, QEMU_OPTION_nouserconfig,
>      "-no-user-config\n"
> diff --git a/softmmu/vl.c b/softmmu/vl.c
> index 7ddf405d76..d34307bf11 100644
> --- a/softmmu/vl.c
> +++ b/softmmu/vl.c
> @@ -3337,25 +3337,6 @@ void qemu_init(int argc, char **argv, char **envp)
>                  }
>                  display_remote++;
>                  break;
> -            case QEMU_OPTION_writeconfig:
> -                {
> -                    FILE *fp;
> -                    if (strcmp(optarg, "-") == 0) {
> -                        fp = stdout;
> -                    } else {
> -                        fp = fopen(optarg, "w");
> -                        if (fp == NULL) {
> -                            error_report("open %s: %s", optarg,
> -                                         strerror(errno));
> -                            exit(1);
> -                        }
> -                    }
> -                    qemu_config_write(fp);
> -                    if (fp != stdout) {
> -                        fclose(fp);
> -                    }
> -                    break;
> -                }
>              case QEMU_OPTION_qtest:
>                  qtest_chrdev = optarg;
>                  break;
> diff --git a/util/qemu-config.c b/util/qemu-config.c
> index e2a700b284..a4a1324c68 100644
> --- a/util/qemu-config.c
> +++ b/util/qemu-config.c
> @@ -307,48 +307,6 @@ void qemu_add_opts(QemuOptsList *list)
>      abort();
>  }
>  
> -struct ConfigWriteData {
> -    QemuOptsList *list;
> -    FILE *fp;
> -};
> -
> -static int config_write_opt(void *opaque, const char *name, const char *value,
> -                            Error **errp)
> -{
> -    struct ConfigWriteData *data = opaque;
> -
> -    fprintf(data->fp, "  %s = \"%s\"\n", name, value);
> -    return 0;
> -}
> -
> -static int config_write_opts(void *opaque, QemuOpts *opts, Error **errp)
> -{
> -    struct ConfigWriteData *data = opaque;
> -    const char *id = qemu_opts_id(opts);
> -
> -    if (id) {
> -        fprintf(data->fp, "[%s \"%s\"]\n", data->list->name, id);
> -    } else {
> -        fprintf(data->fp, "[%s]\n", data->list->name);
> -    }
> -    qemu_opt_foreach(opts, config_write_opt, data, NULL);
> -    fprintf(data->fp, "\n");
> -    return 0;
> -}
> -
> -void qemu_config_write(FILE *fp)
> -{
> -    struct ConfigWriteData data = { .fp = fp };
> -    QemuOptsList **lists = vm_config_groups;
> -    int i;
> -
> -    fprintf(fp, "# qemu config file\n\n");
> -    for (i = 0; lists[i] != NULL; i++) {
> -        data.list = lists[i];
> -        qemu_opts_foreach(data.list, config_write_opts, &data, NULL);
> -    }
> -}
> -
>  /* Returns number of config groups on success, -errno on error */
>  int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
>  {



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 12/25] qemu-config: add error propagation to qemu_config_parse
  2021-01-18 16:31 ` [PATCH 12/25] qemu-config: add error propagation to qemu_config_parse Paolo Bonzini
@ 2021-01-25 13:54   ` Markus Armbruster
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Armbruster @ 2021-01-25 13:54 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> This enables some simplification of vl.c via error_fatal.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  block/blkdebug.c           |  3 +--
>  include/qemu/config-file.h |  4 ++--
>  softmmu/vl.c               | 30 ++++++++++++------------------
>  util/qemu-config.c         | 20 ++++++++++----------
>  4 files changed, 25 insertions(+), 32 deletions(-)
>
> diff --git a/block/blkdebug.c b/block/blkdebug.c
> index 5fe6172da9..7eaa8a28bf 100644
> --- a/block/blkdebug.c
> +++ b/block/blkdebug.c
> @@ -279,9 +279,8 @@ static int read_config(BDRVBlkdebugState *s, const char *filename,
>              return -errno;
>          }
>  
> -        ret = qemu_config_parse(f, config_groups, filename);
> +        ret = qemu_config_parse(f, config_groups, filename, errp);
>          if (ret < 0) {
> -            error_setg(errp, "Could not parse blkdebug config file");
>              goto fail;
>          }
>      }
> diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h
> index 7d26fe3816..da6f4690b7 100644
> --- a/include/qemu/config-file.h
> +++ b/include/qemu/config-file.h
> @@ -10,9 +10,9 @@ void qemu_add_opts(QemuOptsList *list);
>  void qemu_add_drive_opts(QemuOptsList *list);
>  int qemu_global_option(const char *str);
>  
> -int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname);
> +int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp);

Long line.

>  
> -int qemu_read_config_file(const char *filename);
> +int qemu_read_config_file(const char *filename, Error **errp);
>  
>  /* Parse QDict options as a replacement for a config file (allowing multiple
>     enumerated (0..(n-1)) configuration "sections") */
> diff --git a/softmmu/vl.c b/softmmu/vl.c
> index d34307bf11..d991919155 100644
> --- a/softmmu/vl.c
> +++ b/softmmu/vl.c
> @@ -2056,17 +2056,20 @@ static int global_init_func(void *opaque, QemuOpts *opts, Error **errp)
>      return 0;
>  }
>  
> -static int qemu_read_default_config_file(void)
> +static void qemu_read_default_config_file(Error **errp)
>  {
>      int ret;
> +    Error *local_err = NULL;
>      g_autofree char *file = get_relocated_path(CONFIG_QEMU_CONFDIR "/qemu.conf");
>  
> -    ret = qemu_read_config_file(file);
> -    if (ret < 0 && ret != -ENOENT) {
> -        return ret;
> +    ret = qemu_read_config_file(file, &local_err);
> +    if (ret < 0) {
> +        if (ret == -ENOENT) {
> +            error_free(local_err);
> +        } else {
> +            error_propagate(errp, local_err);
> +        }
>      }
> -
> -    return 0;
>  }

Please use ERRP_GUARD() in new code:

   static void qemu_read_default_config_file(Error **errp)
   {
       ERRP_GUARD();
       int ret;
       g_autofree char *file = get_relocated_path(CONFIG_QEMU_CONFDIR
                                                  "/qemu.conf");

       ret = qemu_read_config_file(file, errp);
       if (ret == -ENOENT) {
           error_free(*errp);
           *errp = NULL;
       }
   }

>  
>  static int qemu_set_option(const char *str)
> @@ -2622,9 +2625,7 @@ void qemu_init(int argc, char **argv, char **envp)
>      }
>  
>      if (userconfig) {
> -        if (qemu_read_default_config_file() < 0) {
> -            exit(1);
> -        }
> +        qemu_read_default_config_file(&error_fatal);
>      }
>  
>      /* second pass of option parsing */
> @@ -3312,15 +3313,8 @@ void qemu_init(int argc, char **argv, char **envp)
>                  qemu_plugin_opt_parse(optarg, &plugin_list);
>                  break;
>              case QEMU_OPTION_readconfig:
> -                {
> -                    int ret = qemu_read_config_file(optarg);
> -                    if (ret < 0) {
> -                        error_report("read config %s: %s", optarg,
> -                                     strerror(-ret));
> -                        exit(1);
> -                    }
> -                    break;
> -                }
> +                qemu_read_config_file(optarg, &error_fatal);
> +                break;

More than just code simplifcation: you're deleting an extra error
message.  Test case:

    $ qemu-system-x86_64 -readconfig .
    qemu: error reading file
    qemu: -readconfig .: read config .: Invalid argument

Pretty bad.  With your patch applied:

    qemu-system-x86_64: error reading file

Worse :)

I actually expected this to come out as

    qemu-system-x86_64: -readconfig .: error reading file

That would be an improvement.

The reason for the bad location information is here:

    int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp)
    {
        char line[1024], group[64], id[64], arg[64], value[1024];
        Location loc;
        QemuOptsList *list = NULL;
        Error *local_err = NULL;
        QemuOpts *opts = NULL;
        int res = -EINVAL, lno = 0;
        int count = 0;

        loc_push_none(&loc);
        while (fgets(line, sizeof(line), fp) != NULL) {

If the very first fgets() fails setting @fp's error indicator, ...

            loc_set_file(fname, ++lno);
            [...]
        }
        if (ferror(fp)) {

... we get here with error location "none", and ...

            error_setg(errp, "error reading file");

... use it to report the error (remember, @errp is &error_fatal).

Independently, error_setg_errno() would be nice.  Assuming we're willing
to rely on errno being useful after fgets().

            goto out;
        }
        res = count;
    out:
        loc_pop(&loc);
        return res;
    }

Always, always, always test the error messages.

>              case QEMU_OPTION_spice:
>                  olist = qemu_find_opts_err("spice", NULL);
>                  if (!olist) {
> diff --git a/util/qemu-config.c b/util/qemu-config.c
> index a4a1324c68..d0629f4960 100644
> --- a/util/qemu-config.c
> +++ b/util/qemu-config.c
> @@ -308,7 +308,7 @@ void qemu_add_opts(QemuOptsList *list)
>  }
>  
>  /* Returns number of config groups on success, -errno on error */
> -int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
> +int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp)
>  {
>      char line[1024], group[64], id[64], arg[64], value[1024];
>      Location loc;
> @@ -333,7 +333,7 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
>              /* group with id */
>              list = find_list(lists, group, &local_err);
>              if (local_err) {
> -                error_report_err(local_err);
> +                error_propagate(errp, local_err);
>                  goto out;
>              }

Please avoid error_propagate() where possible:

               list = find_list(lists, group, errp);
               if (!list) {
                   goto out;
               }


>              opts = qemu_opts_create(list, id, 1, NULL);
> @@ -344,7 +344,7 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
>              /* group without id */
>              list = find_list(lists, group, &local_err);
>              if (local_err) {
> -                error_report_err(local_err);
> +                error_propagate(errp, local_err);
>                  goto out;
>              }

Likewise.

>              opts = qemu_opts_create(list, NULL, 0, &error_abort);
> @@ -356,20 +356,19 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
>              sscanf(line, " %63s = \"\"", arg) == 1) {
>              /* arg = value */
>              if (opts == NULL) {
> -                error_report("no group defined");
> +                error_setg(errp, "no group defined");
>                  goto out;
>              }
> -            if (!qemu_opt_set(opts, arg, value, &local_err)) {
> -                error_report_err(local_err);
> +            if (!qemu_opt_set(opts, arg, value, errp)) {
>                  goto out;
>              }
>              continue;
>          }
> -        error_report("parse error");
> +        error_setg(errp, "parse error");

*Ächz*  Not your patch's fault.

>          goto out;
>      }
>      if (ferror(fp)) {
> -        error_report("error reading file");
> +        error_setg(errp, "error reading file");
>          goto out;
>      }
>      res = count;
> @@ -378,16 +377,17 @@ out:
>      return res;
>  }
>  
> -int qemu_read_config_file(const char *filename)
> +int qemu_read_config_file(const char *filename, Error **errp)
>  {
>      FILE *f = fopen(filename, "r");
>      int ret;
>  
>      if (f == NULL) {
> +        error_setg_errno(errp, errno, "Cannot read config file %s", filename);
>          return -errno;
>      }
>  
> -    ret = qemu_config_parse(f, vm_config_groups, filename);
> +    ret = qemu_config_parse(f, vm_config_groups, filename, errp);
>      fclose(f);
>      return ret;
>  }



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 15/25] vl: plumb keyval-based options into -set and -readconfig
  2021-01-25 11:48   ` Markus Armbruster
@ 2021-01-25 13:59     ` Paolo Bonzini
  0 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-25 13:59 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 25/01/21 12:48, Markus Armbruster wrote:
> Paolo Bonzini <pbonzini@redhat.com> writes:
> 
>> Add generic machinery to support parsing command line options with
>> keyval in -set and -readconfig, choosing between QDict and
>> QemuOpts as the underlying data structure.
>>
>> The keyval_merge function is slightly heavyweight as a way to
>> do qemu_set_option for QDict-based options, but it will be put
>> to further use later to merge entire -readconfig sections together
>> with their command-line equivalents.
>>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> 
> Watch this:
> 
>      $ cat test.cfg
>      [machine]
>        accel = "kvm"
>        usb = "on"
>      $ qemu-system-x86_64 -S -display none -readconfig test.cfg
>      Aborted (core dumped)

Probably something that fixes itself later in the series.  I'll check it 
out.

Paolo

> Backtrace:
> 
>      #0  0x00007ffff52f19e5 in raise () at /lib64/libc.so.6
>      #1  0x00007ffff52da895 in abort () at /lib64/libc.so.6
>      #2  0x0000555555c44a77 in qemu_record_config_group
>          (group=0x7fffffffd1b0 "machine", dict=0x5555565fb740, errp=0x5555564ffca0 <error_fatal>) at ../softmmu/vl.c:2103
>      #3  0x0000555555c44bd8 in qemu_parse_config_group
>          (group=0x7fffffffd1b0 "machine", qdict=0x5555565f9640, opaque=0x5555564ff9e0 <vm_config_groups>, errp=0x5555564ffca0 <error_fatal>) at ../softmmu/vl.c:2135
>      #4  0x0000555555eda3e6 in qemu_config_foreach
>          (fp=0x5555565f3e00, cb=0x555555c44af5 <qemu_parse_config_group>, opaque=0x5555564ff9e0 <vm_config_groups>, fname=0x7fffffffe0dd "test.cfg", errp=0x5555564ffca0 <error_fatal>) at ../util/qemu-config.c:378
>      #5  0x0000555555eda5d5 in qemu_read_config_file
>          (filename=0x7fffffffe0dd "test.cfg", cb=0x555555c44af5 <qemu_parse_config_group>, errp=0x5555564ffca0 <error_fatal>) at ../util/qemu-config.c:421
>      #6  0x0000555555c47d3f in qemu_init
>          (argc=6, argv=0x7fffffffdcc8, envp=0x7fffffffdd00) at ../softmmu/vl.c:3405
>      #7  0x00005555558234e1 in main
>          (argc=6, argv=0x7fffffffdcc8, envp=0x7fffffffdd00) at ../softmmu/main.c:49
> 
> Similar result for
> 
>      [memory]
>        size = "1024"
> 
> and
> 
>      [chardev "mon0"]
>        backend = "stdio"
> 
> I didn't look for more.
> 



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 11/25] remove -writeconfig
  2021-01-25 12:53   ` Markus Armbruster
@ 2021-01-25 14:01     ` Paolo Bonzini
  2021-01-25 14:12       ` Daniel P. Berrangé
  0 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-01-25 14:01 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 25/01/21 13:53, Markus Armbruster wrote:
> I love the "and how give me a config file for all that" idea, but I
> agree our -writeconfig is flawed.  I hope we can bring it back in more
> useful shape.
> 
> No deprecation grace period?
> 

That's a decision that we have to take overall once the whole series is 
reviewed, I think.  I have no problem having a grace period:

- the patches aren't 101% ready

- the real conflict magnets have been merged already

- I have a large KVM backlog so I don't mind leaving this aside for a 
few months

Paolo



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 10/25] hmp: special case help options for object_add
  2021-01-25 12:49     ` Paolo Bonzini
@ 2021-01-25 14:02       ` Markus Armbruster
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Armbruster @ 2021-01-25 14:02 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> On 25/01/21 13:48, Markus Armbruster wrote:
>> Paolo Bonzini <pbonzini@redhat.com> writes:
>> 
>>> Fix "object_add help" and "object_add TYPE,help".
>>>
>>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>> Standard question when I read "Fix $interface" in a commit message:
>> how
>> exactly is it broken?
>
> It doesn't work at all ("Error: parameter 'id' is missing").

Adding simple reproducers to commit messages won't hurt a bit, I
promise!

;-P



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 11/25] remove -writeconfig
  2021-01-25 14:01     ` Paolo Bonzini
@ 2021-01-25 14:12       ` Daniel P. Berrangé
  0 siblings, 0 replies; 67+ messages in thread
From: Daniel P. Berrangé @ 2021-01-25 14:12 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, Markus Armbruster, qemu-devel

On Mon, Jan 25, 2021 at 03:01:01PM +0100, Paolo Bonzini wrote:
> On 25/01/21 13:53, Markus Armbruster wrote:
> > I love the "and how give me a config file for all that" idea, but I
> > agree our -writeconfig is flawed.  I hope we can bring it back in more
> > useful shape.
> > 
> > No deprecation grace period?
> > 
> 
> That's a decision that we have to take overall once the whole series is
> reviewed, I think.  I have no problem having a grace period:

I'm normally in strongly pushing for honouring our deprecation policy,
but in almost all past cases we're changing/removing something that is
genuinely used by people in the real world.

I think it is possible to argue that -writeconfig is a special case
becuase its functionality is so limited in scope that its real world
use cases are fairly niche, and is majorly buggy in what it writes
in some cases. IOW we could argue it is too broken + useless to justify
going through the deprecation process.

So overall I'm ambivalent on whether we use deprecation for -writeconfig
or not.

> 
> - the patches aren't 101% ready
> 
> - the real conflict magnets have been merged already
> 
> - I have a large KVM backlog so I don't mind leaving this aside for a few
> months



Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 08/25] hmp: replace "O" parser with keyval
  2021-01-25  9:00   ` Markus Armbruster
@ 2021-02-26 11:25     ` Paolo Bonzini
  2021-03-01 10:14       ` Markus Armbruster
  2021-03-01 10:43     ` Markus Armbruster
  1 sibling, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-02-26 11:25 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 25/01/21 10:00, Markus Armbruster wrote:
> Paolo Bonzini <pbonzini@redhat.com> writes:
> 
>> HMP is using QemuOpts to parse free-form commands device_add,
>> netdev_add and object_add.  However, none of these need QemuOpts
>> for validation (these three QemuOptsLists are all of the catch-all
>> kind), and keyval is already able to parse into QDict.  So use
>> keyval directly, avoiding the detour from
>> string to QemuOpts to QDict.
>>
>> The args_type now stores the implied key.  This arguably makes more
>> sense than storing the QemuOptsList name; at least, it _is_ a key
>> that might end up in the arguments QDict.
>>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> 
> Switching from QemuOpts to keyval changes the accepted language.  We may
> change it, because HMP is not a stable interface.  The commit message
> should point out the change, though.  Maybe even release notes, not
> sure.
> 
> Let's recap the differences briefly:
> 
> * Boolean sugar: deprecated in QemuOpts, nonexistent in keyval
> 
> * QemuOpts accepts a number of more or less crazy corner cases keyval
>    rejects: invalid key, long key (silently truncated), first rather than
>    last id= wins (unlike other keys), implied key with empty value.
> 
> * QemuOpts rejects anti-social ID such as id=666, keyval leaves this to
>    the caller, because key "id" is not special in keyval.
> 
>    Are these still rejected with your patch?

Back here... No, and that's a feature.  There's no reason to reject 
those ids.  However, this shows that Kevin's series to move --object to 
keyval propagates a bug from qemu-storage-daemon to QEMU:

$ storage-daemon/qemu-storage-daemon --object 
authz-simple,id=123/546,identity=abc --chardev stdio,id=foo --monitor 
chardev=foo
 > {'execute':'qmp_capabilities'}
 > {'execute':'qom-list', 'arguments': {'path':'/objects'}}
< {"return": [{"name": "type", "type": "string"}, {"name": "123/546", 
"type": "child<authz-simple>"}]}

Good luck using that object anywhere. :)

> * device_add help,e1000
> 
>      {
>          "e1000": "on",
>          "driver": "help"
>      }
> 
>    Afterwards:
>    upstream-qemu: ../util/error.c:59: error_setv: Assertion `*errp == NULL' failed.

I cannot reproduce it:

$ ./qemu-system-x86_64 -M none -monitor stdio -display none
QEMU 5.2.50 monitor - type 'help' for more information
(qemu) device_add help,e1000
Error: Parameter 'driver' is missing

Paolo



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 08/25] hmp: replace "O" parser with keyval
  2021-02-26 11:25     ` Paolo Bonzini
@ 2021-03-01 10:14       ` Markus Armbruster
  2021-03-01 10:23         ` Paolo Bonzini
  0 siblings, 1 reply; 67+ messages in thread
From: Markus Armbruster @ 2021-03-01 10:14 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> On 25/01/21 10:00, Markus Armbruster wrote:
>> Paolo Bonzini <pbonzini@redhat.com> writes:
>> 
>>> HMP is using QemuOpts to parse free-form commands device_add,
>>> netdev_add and object_add.  However, none of these need QemuOpts
>>> for validation (these three QemuOptsLists are all of the catch-all
>>> kind), and keyval is already able to parse into QDict.  So use
>>> keyval directly, avoiding the detour from
>>> string to QemuOpts to QDict.
>>>
>>> The args_type now stores the implied key.  This arguably makes more
>>> sense than storing the QemuOptsList name; at least, it _is_ a key
>>> that might end up in the arguments QDict.
>>>
>>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>> 
>> Switching from QemuOpts to keyval changes the accepted language.  We may
>> change it, because HMP is not a stable interface.  The commit message
>> should point out the change, though.  Maybe even release notes, not
>> sure.
>> 
>> Let's recap the differences briefly:
>> 
>> * Boolean sugar: deprecated in QemuOpts, nonexistent in keyval
>> 
>> * QemuOpts accepts a number of more or less crazy corner cases keyval
>>    rejects: invalid key, long key (silently truncated), first rather than
>>    last id= wins (unlike other keys), implied key with empty value.
>> 
>> * QemuOpts rejects anti-social ID such as id=666, keyval leaves this to
>>    the caller, because key "id" is not special in keyval.
>> 
>>    Are these still rejected with your patch?
>
> Back here... No, and that's a feature.  There's no reason to reject 
> those ids.  However, this shows that Kevin's series to move --object to 
> keyval propagates a bug from qemu-storage-daemon to QEMU:
>
> $ storage-daemon/qemu-storage-daemon --object 
> authz-simple,id=123/546,identity=abc --chardev stdio,id=foo --monitor 
> chardev=foo
>  > {'execute':'qmp_capabilities'}
>  > {'execute':'qom-list', 'arguments': {'path':'/objects'}}
> < {"return": [{"name": "type", "type": "string"}, {"name": "123/546", 
> "type": "child<authz-simple>"}]}
>
> Good luck using that object anywhere. :)

There is no reason to reject those IDs other than spoiling the fun we're
having with setting traps for our users.

Since QOM is treating '/' specially in paths, and uses IDs as path
components, it should reject '/' in IDs.  Same reasoning as for file
names.

We already restrict IDs to "letters, digits, '-', '.', '_', starting
with a letter" in several places, including QemuOpts.  We should do that
more, not less.

Permitting arbitrary IDs buys us nothing but trouble.

>> * device_add help,e1000
>> 
>>      {
>>          "e1000": "on",
>>          "driver": "help"
>>      }
>> 
>>    Afterwards:
>>    upstream-qemu: ../util/error.c:59: error_setv: Assertion `*errp == NULL' failed.
>
> I cannot reproduce it:
>
> $ ./qemu-system-x86_64 -M none -monitor stdio -display none
> QEMU 5.2.50 monitor - type 'help' for more information
> (qemu) device_add help,e1000
> Error: Parameter 'driver' is missing

I'll double-check and report back.



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 08/25] hmp: replace "O" parser with keyval
  2021-03-01 10:14       ` Markus Armbruster
@ 2021-03-01 10:23         ` Paolo Bonzini
  2021-03-01 13:35           ` Markus Armbruster
  0 siblings, 1 reply; 67+ messages in thread
From: Paolo Bonzini @ 2021-03-01 10:23 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 01/03/21 11:14, Markus Armbruster wrote:
> There is no reason to reject those IDs other than spoiling the fun we're
> having with setting traps for our users.
> 
> Since QOM is treating '/' specially in paths, and uses IDs as path
> components, it should reject '/' in IDs.  Same reasoning as for file
> names.

I agree; however I don't think it buys anything to do that in HMP rather 
than further down the call chain, because in the end there are other 
ways to get "anti-social" ids than HMP or the command line.  I commented 
on Kevin's object-add series about this issue.

Paolo

> We already restrict IDs to "letters, digits, '-', '.', '_', starting
> with a letter" in several places, including QemuOpts.  We should do that
> more, not less.
> 
> Permitting arbitrary IDs buys us nothing but trouble.



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 08/25] hmp: replace "O" parser with keyval
  2021-01-25  9:00   ` Markus Armbruster
  2021-02-26 11:25     ` Paolo Bonzini
@ 2021-03-01 10:43     ` Markus Armbruster
  2021-03-01 11:54       ` Paolo Bonzini
  1 sibling, 1 reply; 67+ messages in thread
From: Markus Armbruster @ 2021-03-01 10:43 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, Paolo Bonzini, qemu-devel, imammedo

Markus Armbruster <armbru@redhat.com> writes:

> Paolo Bonzini <pbonzini@redhat.com> writes:
>
>> HMP is using QemuOpts to parse free-form commands device_add,
>> netdev_add and object_add.  However, none of these need QemuOpts
>> for validation (these three QemuOptsLists are all of the catch-all
>> kind), and keyval is already able to parse into QDict.  So use
>> keyval directly, avoiding the detour from
>> string to QemuOpts to QDict.
>>
>> The args_type now stores the implied key.  This arguably makes more
>> sense than storing the QemuOptsList name; at least, it _is_ a key
>> that might end up in the arguments QDict.
>>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>
> Switching from QemuOpts to keyval changes the accepted language.  We may
> change it, because HMP is not a stable interface.  The commit message
> should point out the change, though.  Maybe even release notes, not
> sure.
>
> Let's recap the differences briefly:
>
> * Boolean sugar: deprecated in QemuOpts, nonexistent in keyval
>
> * QemuOpts accepts a number of more or less crazy corner cases keyval
>   rejects: invalid key, long key (silently truncated), first rather than
>   last id= wins (unlike other keys), implied key with empty value.
>
> * QemuOpts rejects anti-social ID such as id=666, keyval leaves this to
>   the caller, because key "id" is not special in keyval.
>
>   Are these still rejected with your patch?
>
>> ---
>>  hmp-commands.hx |  6 +++---
>>  monitor/hmp.c   | 20 +++++++++-----------
>>  2 files changed, 12 insertions(+), 14 deletions(-)
>>
>> diff --git a/hmp-commands.hx b/hmp-commands.hx
>> index 73e0832ea1..6ee746b53e 100644
>> --- a/hmp-commands.hx
>> +++ b/hmp-commands.hx
>> @@ -669,7 +669,7 @@ ERST
>>  
>>      {
>>          .name       = "device_add",
>> -        .args_type  = "device:O",
>> +        .args_type  = "driver:O",
>>          .params     = "driver[,prop=value][,...]",
>>          .help       = "add device, like -device on the command line",
>>          .cmd        = hmp_device_add,
>> @@ -1315,7 +1315,7 @@ ERST
>>  
>>      {
>>          .name       = "netdev_add",
>> -        .args_type  = "netdev:O",
>> +        .args_type  = "type:O",
>>          .params     = "[user|tap|socket|vde|bridge|hubport|netmap|vhost-user],id=str[,prop=value][,...]",
>>          .help       = "add host network device",
>>          .cmd        = hmp_netdev_add,
>> @@ -1343,7 +1343,7 @@ ERST
>>  
>>      {
>>          .name       = "object_add",
>> -        .args_type  = "object:O",
>> +        .args_type  = "qom-type:O",
>>          .params     = "[qom-type=]type,id=str[,prop=value][,...]",
>>          .help       = "create QOM object",
>>          .cmd        = hmp_object_add,
>> diff --git a/monitor/hmp.c b/monitor/hmp.c
>> index 6c0b33a0b1..d2cb886da5 100644
>> --- a/monitor/hmp.c
>> +++ b/monitor/hmp.c
>> @@ -744,13 +744,9 @@ static QDict *monitor_parse_arguments(Monitor *mon,
>>              break;
>>          case 'O':
>>              {
>> -                QemuOptsList *opts_list;
>> -                QemuOpts *opts;
>> +                Error *errp;

Missing initializer.  This is what causes the assertion failure reported
below.

>> +                bool help;
>>  
>> -                opts_list = qemu_find_opts(key);
>> -                if (!opts_list || opts_list->desc->name) {
>> -                    goto bad_type;
>> -                }
>>                  while (qemu_isspace(*p)) {
>>                      p++;
>>                  }
>> @@ -760,12 +756,14 @@ static QDict *monitor_parse_arguments(Monitor *mon,
>>                  if (get_str(buf, sizeof(buf), &p) < 0) {
>>                      goto fail;
>>                  }
>> -                opts = qemu_opts_parse_noisily(opts_list, buf, true);
>> -                if (!opts) {
>> -                    goto fail;
>> +                keyval_parse_into(qdict, buf, key, &help, &errp);
>> +                if (help) {
>
> Uh...
>
>> +                    if (qdict_haskey(qdict, key)) {
>
> If we parsed a value for the implied key (sugared or not),
>
>> +                        qdict_put_bool(qdict, "help", true);
>
> then encode the help request by mapping key "help" to true,
>
>> +                    } else {
>> +                        qdict_put_str(qdict, key, "help");
>
> else by mapping the implied key to "help".
>
>> +                    }
>
> Test cases:
>
> * device_add help
>
>   @qdict before the patch:
>
>     {
>         "driver": "help"
>     }
>
>   No change.
>
> * device_add e1000,help
>
>   @qdict before the patch:
>
>     {
>         "driver": "e1000",
>         "help": "on"
>     }
>
>   Afterwards:
>
>     {
>         "driver": "e1000",
>         "help": true
>     }
>
>   If this is okay, the commit message should explain it.
>
> * device_add help,e1000
>
>     {
>         "e1000": "on",
>         "driver": "help"
>     }
>
>   Afterwards:
>   upstream-qemu: ../util/error.c:59: error_setv: Assertion `*errp == NULL' failed.

Optimization masks this crash for me.

With proper initialization, I get

    {
        "driver": "help"
    }

instead.  If this change is okay, the commit message should explain it.

>
>>                  }
>> -                qemu_opts_to_qdict(opts, qdict);
>> -                qemu_opts_del(opts);
>>              }
>>              break;
>>          case '/':



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 08/25] hmp: replace "O" parser with keyval
  2021-03-01 10:43     ` Markus Armbruster
@ 2021-03-01 11:54       ` Paolo Bonzini
  0 siblings, 0 replies; 67+ messages in thread
From: Paolo Bonzini @ 2021-03-01 11:54 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: kwolf, imammedo, qemu-devel

On 01/03/21 11:43, Markus Armbruster wrote:
> 
> With proper initialization, I get
> 
>      {
>          "driver": "help"
>      }
> 
> instead.  If this change is okay, the commit message should explain it.

Matches "qemu-system-x86_64 -device help,e1000", so it should be okay.

Paolo



^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH 08/25] hmp: replace "O" parser with keyval
  2021-03-01 10:23         ` Paolo Bonzini
@ 2021-03-01 13:35           ` Markus Armbruster
  0 siblings, 0 replies; 67+ messages in thread
From: Markus Armbruster @ 2021-03-01 13:35 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kwolf, imammedo, qemu-devel

Paolo Bonzini <pbonzini@redhat.com> writes:

> On 01/03/21 11:14, Markus Armbruster wrote:
>> There is no reason to reject those IDs other than spoiling the fun we're
>> having with setting traps for our users.
>> 
>> Since QOM is treating '/' specially in paths, and uses IDs as path
>> components, it should reject '/' in IDs.  Same reasoning as for file
>> names.
>
> I agree; however I don't think it buys anything to do that in HMP rather 
> than further down the call chain, because in the end there are other 
> ways to get "anti-social" ids than HMP or the command line.  I commented 
> on Kevin's object-add series about this issue.

Further down is better provided further down actually does it :)

Anyway, please amend the commit message to describe user-visible
differences.  Doesn't have to be perfectly exhaustive; glossing over the
more eccentric corner cases should be okay.



^ permalink raw reply	[flat|nested] 67+ messages in thread

end of thread, other threads:[~2021-03-01 13:40 UTC | newest]

Thread overview: 67+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-18 16:30 [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing Paolo Bonzini
2021-01-18 16:30 ` [PATCH 01/25] qemu-option: clean up id vs. list->merge_lists Paolo Bonzini
2021-01-19 12:33   ` Kevin Wolf
2021-01-19 13:58   ` Markus Armbruster
2021-01-19 14:20     ` Paolo Bonzini
2021-01-20  8:03       ` Markus Armbruster
2021-01-20 12:37         ` Paolo Bonzini
2021-01-20 12:50           ` Markus Armbruster
2021-01-18 16:30 ` [PATCH 02/25] qemu-option: move help handling to get_opt_name_value Paolo Bonzini
2021-01-19 15:10   ` Markus Armbruster
2021-01-18 16:30 ` [PATCH 03/25] qemu-option: warn for short-form boolean options Paolo Bonzini
2021-01-19 15:56   ` Markus Armbruster
2021-01-19 17:04     ` Paolo Bonzini
2021-01-20  8:42       ` Markus Armbruster
2021-01-20 12:40         ` Paolo Bonzini
2021-01-20 12:59           ` Markus Armbruster
2021-01-20 14:05             ` Paolo Bonzini
2021-01-18 16:30 ` [PATCH 04/25] keyval: accept escaped commas in implied option Paolo Bonzini
2021-01-21 12:58   ` Markus Armbruster
2021-01-22  8:39   ` Markus Armbruster
2021-01-18 16:30 ` [PATCH 05/25] keyval: simplify keyval_parse_one Paolo Bonzini
2021-01-22 13:48   ` Markus Armbruster
2021-01-22 15:00     ` Paolo Bonzini
2021-01-22 15:44       ` Markus Armbruster
2021-01-18 16:30 ` [PATCH 06/25] tests: convert check-qom-proplist to keyval Paolo Bonzini
2021-01-22 14:14   ` Markus Armbruster
2021-01-22 14:38     ` Paolo Bonzini
2021-01-22 14:48     ` Paolo Bonzini
2021-01-18 16:30 ` [PATCH 07/25] keyval: introduce keyval_parse_into Paolo Bonzini
2021-01-22 14:22   ` Markus Armbruster
2021-01-22 14:30     ` Paolo Bonzini
2021-01-18 16:30 ` [PATCH 08/25] hmp: replace "O" parser with keyval Paolo Bonzini
2021-01-25  9:00   ` Markus Armbruster
2021-02-26 11:25     ` Paolo Bonzini
2021-03-01 10:14       ` Markus Armbruster
2021-03-01 10:23         ` Paolo Bonzini
2021-03-01 13:35           ` Markus Armbruster
2021-03-01 10:43     ` Markus Armbruster
2021-03-01 11:54       ` Paolo Bonzini
2021-01-18 16:30 ` [PATCH 09/25] qom: use qemu_printf to print help for user-creatable objects Paolo Bonzini
2021-01-25 12:47   ` Markus Armbruster
2021-01-18 16:30 ` [PATCH 10/25] hmp: special case help options for object_add Paolo Bonzini
2021-01-25 12:48   ` Markus Armbruster
2021-01-25 12:49     ` Paolo Bonzini
2021-01-25 14:02       ` Markus Armbruster
2021-01-18 16:30 ` [PATCH 11/25] remove -writeconfig Paolo Bonzini
2021-01-25 12:53   ` Markus Armbruster
2021-01-25 14:01     ` Paolo Bonzini
2021-01-25 14:12       ` Daniel P. Berrangé
2021-01-18 16:31 ` [PATCH 12/25] qemu-config: add error propagation to qemu_config_parse Paolo Bonzini
2021-01-25 13:54   ` Markus Armbruster
2021-01-18 16:31 ` [PATCH 13/25] qemu-option: support accept-any QemuOptsList in qemu_opts_absorb_qdict Paolo Bonzini
2021-01-18 16:31 ` [PATCH 14/25] qemu-config: parse configuration files to a QDict Paolo Bonzini
2021-01-18 16:31 ` [PATCH 15/25] vl: plumb keyval-based options into -set and -readconfig Paolo Bonzini
2021-01-25 11:48   ` Markus Armbruster
2021-01-25 13:59     ` Paolo Bonzini
2021-01-18 16:31 ` [PATCH 16/25] qom: do not modify QDict argument in user_creatable_add_dict Paolo Bonzini
2021-01-18 16:31 ` [PATCH 17/25] qemu-io: use keyval for -object parsing Paolo Bonzini
2021-01-18 16:31 ` [PATCH 18/25] qemu-nbd: " Paolo Bonzini
2021-01-18 16:31 ` [PATCH 19/25] qemu-img: " Paolo Bonzini
2021-01-18 16:31 ` [PATCH 20/25] qemu: " Paolo Bonzini
2021-01-18 16:31 ` [PATCH 21/25] storage-daemon: do not register the "object" group with QemuOpts Paolo Bonzini
2021-01-18 16:31 ` [PATCH 22/25] qom: export more functions for use with non-UserCreatable objects Paolo Bonzini
2021-01-18 16:31 ` [PATCH 23/25] vl: switch -M parsing to keyval Paolo Bonzini
2021-01-18 16:31 ` [PATCH 24/25] qemu-option: remove now-dead code Paolo Bonzini
2021-01-18 16:31 ` [PATCH 25/25] vl: switch -accel parsing to keyval Paolo Bonzini
2021-01-18 17:18 ` [PATCH 00/25] qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing no-reply

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).