qemu/util/qemu-config.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

296 lines
7.7 KiB
C
Raw Normal View History

#include "qemu/osdep.h"
#include "block/qdict.h" /* for qdict_extract_subqdict() */
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qlist.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
QemuOptsList *vm_config_groups[48];
QemuOptsList *drive_config_groups[5];
static QemuOptsList *find_list(QemuOptsList **lists, const char *group,
Error **errp)
{
int i;
qemu_load_module_for_opts(group);
for (i = 0; lists[i] != NULL; i++) {
if (strcmp(lists[i]->name, group) == 0)
break;
}
if (lists[i] == NULL) {
error_setg(errp, "There is no option group '%s'", group);
}
return lists[i];
}
QemuOptsList *qemu_find_opts(const char *group)
{
QemuOptsList *ret;
Error *local_err = NULL;
ret = find_list(vm_config_groups, group, &local_err);
if (local_err) {
error_report_err(local_err);
}
return ret;
}
QemuOpts *qemu_find_opts_singleton(const char *group)
{
QemuOptsList *list;
QemuOpts *opts;
list = qemu_find_opts(group);
assert(list);
opts = qemu_opts_find(list, NULL);
if (!opts) {
opts = qemu_opts_create(list, NULL, 0, &error_abort);
}
return opts;
}
QemuOptsList *qemu_find_opts_err(const char *group, Error **errp)
{
return find_list(vm_config_groups, group, errp);
}
void qemu_add_drive_opts(QemuOptsList *list)
{
int entries, i;
entries = ARRAY_SIZE(drive_config_groups);
entries--; /* keep list NULL terminated */
for (i = 0; i < entries; i++) {
if (drive_config_groups[i] == NULL) {
drive_config_groups[i] = list;
return;
}
}
fprintf(stderr, "ran out of space in drive_config_groups");
abort();
}
void qemu_add_opts(QemuOptsList *list)
{
int entries, i;
entries = ARRAY_SIZE(vm_config_groups);
entries--; /* keep list NULL terminated */
for (i = 0; i < entries; i++) {
if (vm_config_groups[i] == NULL) {
vm_config_groups[i] = list;
return;
}
}
fprintf(stderr, "ran out of space in vm_config_groups");
abort();
}
/* Returns number of config groups on success, -errno on error */
static int qemu_config_foreach(FILE *fp, QEMUConfigCB *cb, void *opaque,
const char *fname, Error **errp)
{
ERRP_GUARD();
char line[1024], prev_group[64], group[64], arg[64], value[1024];
Location loc;
QDict *qdict = NULL;
int res = -EINVAL, lno = 0;
int count = 0;
loc_push_none(&loc);
while (fgets(line, sizeof(line), fp) != NULL) {
++lno;
if (line[0] == '\n') {
/* skip empty lines */
continue;
}
if (line[0] == '#') {
/* comment */
continue;
}
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++;
}
if (qdict != prev) {
if (prev) {
cb(prev_group, prev, opaque, errp);
qobject_unref(prev);
if (*errp) {
goto out;
}
}
strcpy(prev_group, group);
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 (qdict == NULL) {
error_setg(errp, "no group defined");
goto out;
}
qdict_put_str(qdict, arg, value);
continue;
}
error_setg(errp, "parse error");
goto out;
}
if (ferror(fp)) {
loc_pop(&loc);
error_setg_errno(errp, errno, "Cannot read config file");
goto out_no_loc;
}
res = count;
if (qdict) {
cb(group, qdict, opaque, errp);
}
out:
loc_pop(&loc);
out_no_loc:
qobject_unref(qdict);
return res;
}
void qemu_config_do_parse(const char *group, QDict *qdict, void *opaque, Error **errp)
{
QemuOptsList **lists = opaque;
QemuOptsList *list;
list = find_list(lists, group, errp);
if (!list) {
return;
}
qemu_opts_from_qdict(list, 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;
if (f == NULL) {
error_setg_file_open(errp, errno, filename);
return -errno;
}
ret = qemu_config_foreach(f, cb, vm_config_groups, filename, errp);
fclose(f);
return ret;
}
static bool config_parse_qdict_section(QDict *options, QemuOptsList *opts,
Error **errp)
{
QemuOpts *subopts;
g_autoptr(QDict) subqdict = NULL;
g_autoptr(QList) list = NULL;
size_t orig_size, enum_size;
char *prefix;
prefix = g_strdup_printf("%s.", opts->name);
qdict_extract_subqdict(options, &subqdict, prefix);
g_free(prefix);
orig_size = qdict_size(subqdict);
if (!orig_size) {
return true;
}
subopts = qemu_opts_create(opts, NULL, 0, errp);
if (!subopts) {
return false;
}
error: Eliminate error_propagate() with Coccinelle, part 1 When all we do with an Error we receive into a local variable is propagating to somewhere else, we can just as well receive it there right away. Convert if (!foo(..., &err)) { ... error_propagate(errp, err); ... return ... } to if (!foo(..., errp)) { ... ... return ... } where nothing else needs @err. Coccinelle script: @rule1 forall@ identifier fun, err, errp, lbl; expression list args, args2; binary operator op; constant c1, c2; symbol false; @@ if ( ( - fun(args, &err, args2) + fun(args, errp, args2) | - !fun(args, &err, args2) + !fun(args, errp, args2) | - fun(args, &err, args2) op c1 + fun(args, errp, args2) op c1 ) ) { ... when != err when != lbl: when strict - error_propagate(errp, err); ... when != err ( return; | return c2; | return false; ) } @rule2 forall@ identifier fun, err, errp, lbl; expression list args, args2; expression var; binary operator op; constant c1, c2; symbol false; @@ - var = fun(args, &err, args2); + var = fun(args, errp, args2); ... when != err if ( ( var | !var | var op c1 ) ) { ... when != err when != lbl: when strict - error_propagate(errp, err); ... when != err ( return; | return c2; | return false; | return var; ) } @depends on rule1 || rule2@ identifier err; @@ - Error *err = NULL; ... when != err Not exactly elegant, I'm afraid. The "when != lbl:" is necessary to avoid transforming if (fun(args, &err)) { goto out } ... out: error_propagate(errp, err); even though other paths to label out still need the error_propagate(). For an actual example, see sclp_realize(). Without the "when strict", Coccinelle transforms vfio_msix_setup(), incorrectly. I don't know what exactly "when strict" does, only that it helps here. The match of return is narrower than what I want, but I can't figure out how to express "return where the operand doesn't use @err". For an example where it's too narrow, see vfio_intx_enable(). Silently fails to convert hw/arm/armsse.c, because Coccinelle gets confused by ARMSSE being used both as typedef and function-like macro there. Converted manually. Line breaks tidied up manually. One nested declaration of @local_err deleted manually. Preexisting unwanted blank line dropped in hw/riscv/sifive_e.c. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20200707160613.848843-35-armbru@redhat.com>
2020-07-07 19:06:02 +03:00
if (!qemu_opts_absorb_qdict(subopts, subqdict, errp)) {
return false;
}
enum_size = qdict_size(subqdict);
if (enum_size < orig_size && enum_size) {
error_setg(errp, "Unknown option '%s' for [%s]",
qdict_first(subqdict)->key, opts->name);
return false;
}
if (enum_size) {
/* Multiple, enumerated sections */
QListEntry *list_entry;
unsigned i = 0;
/* Not required anymore */
qemu_opts_del(subopts);
qdict_array_split(subqdict, &list);
if (qdict_size(subqdict)) {
error_setg(errp, "Unused option '%s' for [%s]",
qdict_first(subqdict)->key, opts->name);
return false;
}
QLIST_FOREACH_ENTRY(list, list_entry) {
QDict *section = qobject_to(QDict, qlist_entry_obj(list_entry));
char *opt_name;
if (!section) {
error_setg(errp, "[%s] section (index %u) does not consist of "
"keys", opts->name, i);
return false;
}
opt_name = g_strdup_printf("%s.%u", opts->name, i++);
subopts = qemu_opts_create(opts, opt_name, 1, errp);
g_free(opt_name);
if (!subopts) {
return false;
}
error: Eliminate error_propagate() with Coccinelle, part 1 When all we do with an Error we receive into a local variable is propagating to somewhere else, we can just as well receive it there right away. Convert if (!foo(..., &err)) { ... error_propagate(errp, err); ... return ... } to if (!foo(..., errp)) { ... ... return ... } where nothing else needs @err. Coccinelle script: @rule1 forall@ identifier fun, err, errp, lbl; expression list args, args2; binary operator op; constant c1, c2; symbol false; @@ if ( ( - fun(args, &err, args2) + fun(args, errp, args2) | - !fun(args, &err, args2) + !fun(args, errp, args2) | - fun(args, &err, args2) op c1 + fun(args, errp, args2) op c1 ) ) { ... when != err when != lbl: when strict - error_propagate(errp, err); ... when != err ( return; | return c2; | return false; ) } @rule2 forall@ identifier fun, err, errp, lbl; expression list args, args2; expression var; binary operator op; constant c1, c2; symbol false; @@ - var = fun(args, &err, args2); + var = fun(args, errp, args2); ... when != err if ( ( var | !var | var op c1 ) ) { ... when != err when != lbl: when strict - error_propagate(errp, err); ... when != err ( return; | return c2; | return false; | return var; ) } @depends on rule1 || rule2@ identifier err; @@ - Error *err = NULL; ... when != err Not exactly elegant, I'm afraid. The "when != lbl:" is necessary to avoid transforming if (fun(args, &err)) { goto out } ... out: error_propagate(errp, err); even though other paths to label out still need the error_propagate(). For an actual example, see sclp_realize(). Without the "when strict", Coccinelle transforms vfio_msix_setup(), incorrectly. I don't know what exactly "when strict" does, only that it helps here. The match of return is narrower than what I want, but I can't figure out how to express "return where the operand doesn't use @err". For an example where it's too narrow, see vfio_intx_enable(). Silently fails to convert hw/arm/armsse.c, because Coccinelle gets confused by ARMSSE being used both as typedef and function-like macro there. Converted manually. Line breaks tidied up manually. One nested declaration of @local_err deleted manually. Preexisting unwanted blank line dropped in hw/riscv/sifive_e.c. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20200707160613.848843-35-armbru@redhat.com>
2020-07-07 19:06:02 +03:00
if (!qemu_opts_absorb_qdict(subopts, section, errp)) {
qemu_opts_del(subopts);
return false;
}
if (qdict_size(section)) {
error_setg(errp, "[%s] section doesn't support the option '%s'",
opts->name, qdict_first(section)->key);
qemu_opts_del(subopts);
return false;
}
}
}
return true;
}
bool qemu_config_parse_qdict(QDict *options, QemuOptsList **lists,
Error **errp)
{
int i;
for (i = 0; lists[i]; i++) {
if (!config_parse_qdict_section(options, lists[i], errp)) {
return false;
}
}
return true;
}