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> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
4db4385a7a
commit
3770141139
@ -1,6 +1,8 @@
|
|||||||
#ifndef QEMU_CONFIG_FILE_H
|
#ifndef QEMU_CONFIG_FILE_H
|
||||||
#define 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);
|
void qemu_load_module_for_opts(const char *group);
|
||||||
QemuOptsList *qemu_find_opts(const char *group);
|
QemuOptsList *qemu_find_opts(const char *group);
|
||||||
QemuOptsList *qemu_find_opts_err(const char *group, Error **errp);
|
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,
|
int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname,
|
||||||
Error **errp);
|
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
|
/* Parse QDict options as a replacement for a config file (allowing multiple
|
||||||
enumerated (0..(n-1)) configuration "sections") */
|
enumerated (0..(n-1)) configuration "sections") */
|
||||||
|
@ -2133,7 +2133,7 @@ static void qemu_read_default_config_file(Error **errp)
|
|||||||
int ret;
|
int ret;
|
||||||
g_autofree char *file = get_relocated_path(CONFIG_QEMU_CONFDIR "/qemu.conf");
|
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 < 0) {
|
||||||
if (ret == -ENOENT) {
|
if (ret == -ENOENT) {
|
||||||
error_free(*errp);
|
error_free(*errp);
|
||||||
@ -3399,7 +3399,7 @@ void qemu_init(int argc, char **argv, char **envp)
|
|||||||
qemu_plugin_opt_parse(optarg, &plugin_list);
|
qemu_plugin_opt_parse(optarg, &plugin_list);
|
||||||
break;
|
break;
|
||||||
case QEMU_OPTION_readconfig:
|
case QEMU_OPTION_readconfig:
|
||||||
qemu_read_config_file(optarg, &error_fatal);
|
qemu_read_config_file(optarg, qemu_config_do_parse, &error_fatal);
|
||||||
break;
|
break;
|
||||||
case QEMU_OPTION_spice:
|
case QEMU_OPTION_spice:
|
||||||
olist = qemu_find_opts_err("spice", NULL);
|
olist = qemu_find_opts_err("spice", NULL);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "block/qdict.h" /* for qdict_extract_subqdict() */
|
#include "block/qdict.h" /* for qdict_extract_subqdict() */
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qapi-commands-misc.h"
|
#include "qapi/qapi-commands-misc.h"
|
||||||
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qlist.h"
|
#include "qapi/qmp/qlist.h"
|
||||||
#include "qemu/error-report.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 */
|
/* 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;
|
Location loc;
|
||||||
QemuOptsList *list = NULL;
|
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
QemuOpts *opts = NULL;
|
QDict *qdict = NULL;
|
||||||
int res = -EINVAL, lno = 0;
|
int res = -EINVAL, lno = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
loc_push_none(&loc);
|
loc_push_none(&loc);
|
||||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||||
loc_set_file(fname, ++lno);
|
++lno;
|
||||||
if (line[0] == '\n') {
|
if (line[0] == '\n') {
|
||||||
/* skip empty lines */
|
/* skip empty lines */
|
||||||
continue;
|
continue;
|
||||||
@ -372,39 +373,39 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error *
|
|||||||
/* comment */
|
/* comment */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (sscanf(line, "[%63s \"%63[^\"]\"]", group, id) == 2) {
|
if (line[0] == '[') {
|
||||||
/* group with id */
|
QDict *prev = qdict;
|
||||||
list = find_list(lists, group, &local_err);
|
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++;
|
||||||
|
}
|
||||||
|
if (qdict != prev) {
|
||||||
|
if (prev) {
|
||||||
|
cb(prev_group, prev, opaque, &local_err);
|
||||||
|
qobject_unref(prev);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
opts = qemu_opts_create(list, id, 1, NULL);
|
}
|
||||||
count++;
|
strcpy(prev_group, group);
|
||||||
continue;
|
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;
|
|
||||||
}
|
|
||||||
opts = qemu_opts_create(list, NULL, 0, &error_abort);
|
|
||||||
count++;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
loc_set_file(fname, lno);
|
||||||
value[0] = '\0';
|
value[0] = '\0';
|
||||||
if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2 ||
|
if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2 ||
|
||||||
sscanf(line, " %63s = \"\"", arg) == 1) {
|
sscanf(line, " %63s = \"\"", arg) == 1) {
|
||||||
/* arg = value */
|
/* arg = value */
|
||||||
if (opts == NULL) {
|
if (qdict == NULL) {
|
||||||
error_setg(errp, "no group defined");
|
error_setg(errp, "no group defined");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (!qemu_opt_set(opts, arg, value, errp)) {
|
qdict_put_str(qdict, arg, value);
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
error_setg(errp, "parse error");
|
error_setg(errp, "parse error");
|
||||||
@ -417,11 +418,48 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error *
|
|||||||
}
|
}
|
||||||
res = count;
|
res = count;
|
||||||
out:
|
out:
|
||||||
|
if (qdict) {
|
||||||
|
cb(group, qdict, opaque, errp);
|
||||||
|
qobject_unref(qdict);
|
||||||
|
}
|
||||||
loc_pop(&loc);
|
loc_pop(&loc);
|
||||||
return res;
|
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)) {
|
||||||
|
qemu_opts_del(opts);
|
||||||
|
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");
|
FILE *f = fopen(filename, "r");
|
||||||
int ret;
|
int ret;
|
||||||
@ -431,7 +469,7 @@ int qemu_read_config_file(const char *filename, Error **errp)
|
|||||||
return -errno;
|
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);
|
fclose(f);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user