diff options
Diffstat (limited to 'util')
| -rw-r--r-- | util/keyval.c | 123 | ||||
| -rw-r--r-- | util/meson.build | 5 | ||||
| -rw-r--r-- | util/qemu-option.c | 51 |
3 files changed, 128 insertions, 51 deletions
diff --git a/util/keyval.c b/util/keyval.c index be34928813..904337c8a1 100644 --- a/util/keyval.c +++ b/util/keyval.c @@ -311,6 +311,86 @@ static char *reassemble_key(GSList *key) } /* + * Recursive worker for keyval_merge. + * + * @str is the path that led to the * current dictionary (to be used for + * error messages). It is modified internally but restored before the + * function returns. + */ +static void keyval_do_merge(QDict *dest, const QDict *merged, GString *str, Error **errp) +{ + size_t save_len = str->len; + const QDictEntry *ent; + QObject *old_value; + + for (ent = qdict_first(merged); ent; ent = qdict_next(merged, ent)) { + old_value = qdict_get(dest, ent->key); + if (old_value) { + if (qobject_type(old_value) != qobject_type(ent->value)) { + error_setg(errp, "Parameter '%s%s' used inconsistently", + str->str, ent->key); + return; + } else if (qobject_type(ent->value) == QTYPE_QDICT) { + /* 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); + continue; + } else if (qobject_type(ent->value) == QTYPE_QLIST) { + /* Append to old list. */ + QList *old = qobject_to(QList, old_value); + QList *new = qobject_to(QList, ent->value); + const QListEntry *item; + QLIST_FOREACH_ENTRY(new, item) { + qobject_ref(item->value); + qlist_append_obj(old, item->value); + } + continue; + } else { + assert(qobject_type(ent->value) == QTYPE_QSTRING); + } + } + + qobject_ref(ent->value); + qdict_put_obj(dest, ent->key, ent->value); + } +} + +/* Merge the @merged dictionary into @dest. + * + * The dictionaries are expected to be returned by the keyval parser, and + * therefore the only expected scalar type is the string. In case the same + * path is present in both @dest and @merged, the semantics are as follows: + * + * - lists are concatenated + * + * - dictionaries are merged recursively + * + * - for scalar values, @merged wins + * + * In case an error is reported, @dest may already have been modified. + * + * This function can be used to implement semantics analogous to QemuOpts's + * .merge_lists = true case, or to implement -set for options backed by QDicts. + * + * Note: while QemuOpts is commonly used so that repeated keys overwrite + * ("last one wins"), it can also be used so that repeated keys build up + * a list. keyval_merge() can only be used when the options' semantics are + * the former, not the latter. + */ +void keyval_merge(QDict *dest, const QDict *merged, Error **errp) +{ + GString *str; + + str = g_string_new(""); + keyval_do_merge(dest, merged, str, errp); + g_string_free(str, TRUE); +} + +/* * Listify @cur recursively. * Replace QDicts whose keys are all valid list indexes by QLists. * @key_of_cur is the list of key fragments leading up to @cur. @@ -431,13 +511,14 @@ 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 failure, store an error through @errp and return NULL. + * On success, return @dict, now filled with the parsed keys and values. + * + * On failure, store an error through @errp and return NULL. Any keys + * and values parsed so far will be in @dict nevertheless. */ -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; const char *s; bool help = false; @@ -446,7 +527,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; @@ -456,15 +536,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; +} diff --git a/util/meson.build b/util/meson.build index 97fad44105..0ffd7f4bde 100644 --- a/util/meson.build +++ b/util/meson.build @@ -1,9 +1,10 @@ -util_ss.add(dependency('threads')) util_ss.add(files('osdep.c', 'cutils.c', 'unicode.c', 'qemu-timer-common.c')) util_ss.add(when: 'CONFIG_ATOMIC64', if_false: files('atomic64.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('aio-posix.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('fdmon-poll.c')) -util_ss.add(when: 'CONFIG_EPOLL_CREATE1', if_true: files('fdmon-epoll.c')) +if config_host_data.get('CONFIG_EPOLL_CREATE1') + util_ss.add(files('fdmon-epoll.c')) +endif util_ss.add(when: ['CONFIG_LINUX_IO_URING', linux_io_uring], if_true: files('fdmon-io_uring.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('compatfd.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('event_notifier-posix.c')) diff --git a/util/qemu-option.c b/util/qemu-option.c index 4944015a25..ee78e42216 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; @@ -811,7 +797,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; @@ -833,7 +819,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); @@ -889,11 +875,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; @@ -903,21 +889,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; @@ -936,7 +914,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); } /** @@ -954,7 +932,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) { @@ -968,15 +946,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) |