| From a0520e180e6f235b8a3a531b0756e45cbfe36e15 Mon Sep 17 00:00:00 2001 |
| From: Paolo Bonzini <pbonzini@redhat.com> |
| Date: Tue, 18 May 2021 11:40:57 -0400 |
| Subject: [PATCH 3/5] qemu-config: parse configuration files to a QDict |
| |
| 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. |
| |
| This is useful for -readconfig to support keyval-based options; |
| getting a QDict from the parser removes a roundtrip from |
| QDict to QemuOpts and then back to QDict. |
| |
| Unfortunately there is a disadvantage in that semantic errors will |
| point to the last line of the group, because the entries of the QDict |
| do not have a location attached. |
| |
| Cc: Kevin Wolf <kwolf@redhat.com> |
| Cc: Markus Armbruster <armbru@redhat.com> |
| Cc: qemu-stable@nongnu.org |
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> |
| Message-Id: <20210524105752.3318299-2-pbonzini@redhat.com> |
| |
| include/qemu/config-file.h | 7 ++- |
| softmmu/vl.c | 4 +- |
| util/qemu-config.c | 97 ++++++++++++++++++++++++++------------ |
| 3 files changed, 75 insertions(+), 33 deletions(-) |
| |
| diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h |
| index 0500b36..f605423 100644 |
| |
| |
| @@ -1,6 +1,8 @@ |
| #ifndef QEMU_CONFIG_FILE_H |
| #define QEMU_CONFIG_FILE_H |
| |
| +typedef void QEMUConfigCB(const char *group, QDict *qdict, void *opaque, Error **errp); |
| + |
| void qemu_load_module_for_opts(const char *group); |
| QemuOptsList *qemu_find_opts(const char *group); |
| QemuOptsList *qemu_find_opts_err(const char *group, Error **errp); |
| @@ -14,7 +16,10 @@ void qemu_config_write(FILE *fp); |
| 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 4641f08..da5042f 100644 |
| |
| |
| @@ -2119,7 +2119,7 @@ static void qemu_read_default_config_file(Error **errp) |
| int ret; |
| g_autofree char *file = get_relocated_path(CONFIG_QEMU_CONFDIR "/qemu.conf"); |
| |
| - ret = qemu_read_config_file(file, errp); |
| + ret = qemu_read_config_file(file, qemu_config_do_parse, errp); |
| if (ret < 0) { |
| if (ret == -ENOENT) { |
| error_free(*errp); |
| @@ -3385,7 +3385,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 34974c4..f6eaff9 100644 |
| |
| |
| @@ -2,6 +2,7 @@ |
| #include "block/qdict.h" /* for qdict_extract_subqdict() */ |
| #include "qapi/error.h" |
| #include "qapi/qapi-commands-misc.h" |
| +#include "qapi/qmp/qerror.h" |
| #include "qapi/qmp/qdict.h" |
| #include "qapi/qmp/qlist.h" |
| #include "qemu/error-report.h" |
| @@ -351,19 +352,19 @@ void qemu_config_write(FILE *fp) |
| } |
| |
| /* 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; |
| @@ -372,39 +373,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"); |
| @@ -417,11 +418,47 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error * |
| } |
| res = count; |
| out: |
| + if (qdict) { |
| + cb(group, qdict, opaque, errp); |
| + qobject_unref(qdict); |
| + } |
| loc_pop(&loc); |
| 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; |
| + const QDictEntry *unrecognized; |
| + |
| + list = find_list(lists, group, errp); |
| + if (!list) { |
| + return; |
| + } |
| + |
| + opts = qemu_opts_create(list, id, 1, errp); |
| + if (!opts) { |
| + return; |
| + } |
| + if (!qemu_opts_absorb_qdict(opts, qdict, errp)) { |
| + return; |
| + } |
| + unrecognized = qdict_first(qdict); |
| + if (unrecognized) { |
| + error_setg(errp, QERR_INVALID_PARAMETER, unrecognized->key); |
| + qemu_opts_del(opts); |
| + } |
| +} |
| + |
| +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; |
| @@ -431,7 +468,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.31.1 |
| |