|  | /* | 
|  | * Input Visitor | 
|  | * | 
|  | * Copyright (C) 2012-2017 Red Hat, Inc. | 
|  | * Copyright IBM, Corp. 2011 | 
|  | * | 
|  | * Authors: | 
|  | *  Anthony Liguori   <aliguori@us.ibm.com> | 
|  | * | 
|  | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | 
|  | * See the COPYING.LIB file in the top-level directory. | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include "qemu/osdep.h" | 
|  | #include <math.h> | 
|  | #include "qapi/compat-policy.h" | 
|  | #include "qapi/error.h" | 
|  | #include "qapi/qobject-input-visitor.h" | 
|  | #include "qapi/visitor-impl.h" | 
|  | #include "qemu/queue.h" | 
|  | #include "qapi/qmp/qjson.h" | 
|  | #include "qapi/qmp/qbool.h" | 
|  | #include "qapi/qmp/qdict.h" | 
|  | #include "qapi/qmp/qerror.h" | 
|  | #include "qapi/qmp/qlist.h" | 
|  | #include "qapi/qmp/qnull.h" | 
|  | #include "qapi/qmp/qnum.h" | 
|  | #include "qapi/qmp/qstring.h" | 
|  | #include "qemu/cutils.h" | 
|  | #include "qemu/keyval.h" | 
|  |  | 
|  | typedef struct StackObject { | 
|  | const char *name;            /* Name of @obj in its parent, if any */ | 
|  | QObject *obj;                /* QDict or QList being visited */ | 
|  | void *qapi; /* sanity check that caller uses same pointer */ | 
|  |  | 
|  | GHashTable *h;              /* If @obj is QDict: unvisited keys */ | 
|  | const QListEntry *entry;    /* If @obj is QList: unvisited tail */ | 
|  | unsigned index;             /* If @obj is QList: list index of @entry */ | 
|  |  | 
|  | QSLIST_ENTRY(StackObject) node; /* parent */ | 
|  | } StackObject; | 
|  |  | 
|  | struct QObjectInputVisitor { | 
|  | Visitor visitor; | 
|  |  | 
|  | /* Root of visit at visitor creation. */ | 
|  | QObject *root; | 
|  | bool keyval;                /* Assume @root made with keyval_parse() */ | 
|  |  | 
|  | /* Stack of objects being visited (all entries will be either | 
|  | * QDict or QList). */ | 
|  | QSLIST_HEAD(, StackObject) stack; | 
|  |  | 
|  | GString *errname;           /* Accumulator for full_name() */ | 
|  | }; | 
|  |  | 
|  | static QObjectInputVisitor *to_qiv(Visitor *v) | 
|  | { | 
|  | return container_of(v, QObjectInputVisitor, visitor); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Find the full name of something @qiv is currently visiting. | 
|  | * @qiv is visiting something named @name in the stack of containers | 
|  | * @qiv->stack. | 
|  | * If @n is zero, return its full name. | 
|  | * If @n is positive, return the full name of the @n-th container | 
|  | * counting from the top.  The stack of containers must have at least | 
|  | * @n elements. | 
|  | * The returned string is valid until the next full_name_nth(@v) or | 
|  | * destruction of @v. | 
|  | */ | 
|  | static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name, | 
|  | int n) | 
|  | { | 
|  | StackObject *so; | 
|  | char buf[32]; | 
|  |  | 
|  | if (qiv->errname) { | 
|  | g_string_truncate(qiv->errname, 0); | 
|  | } else { | 
|  | qiv->errname = g_string_new(""); | 
|  | } | 
|  |  | 
|  | QSLIST_FOREACH(so , &qiv->stack, node) { | 
|  | if (n) { | 
|  | n--; | 
|  | } else if (qobject_type(so->obj) == QTYPE_QDICT) { | 
|  | g_string_prepend(qiv->errname, name ?: "<anonymous>"); | 
|  | g_string_prepend_c(qiv->errname, '.'); | 
|  | } else { | 
|  | snprintf(buf, sizeof(buf), | 
|  | qiv->keyval ? ".%u" : "[%u]", | 
|  | so->index); | 
|  | g_string_prepend(qiv->errname, buf); | 
|  | } | 
|  | name = so->name; | 
|  | } | 
|  | assert(!n); | 
|  |  | 
|  | if (name) { | 
|  | g_string_prepend(qiv->errname, name); | 
|  | } else if (qiv->errname->str[0] == '.') { | 
|  | g_string_erase(qiv->errname, 0, 1); | 
|  | } else if (!qiv->errname->str[0]) { | 
|  | return "<anonymous>"; | 
|  | } | 
|  |  | 
|  | return qiv->errname->str; | 
|  | } | 
|  |  | 
|  | static const char *full_name(QObjectInputVisitor *qiv, const char *name) | 
|  | { | 
|  | return full_name_nth(qiv, name, 0); | 
|  | } | 
|  |  | 
|  | static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv, | 
|  | const char *name, | 
|  | bool consume) | 
|  | { | 
|  | StackObject *tos; | 
|  | QObject *qobj; | 
|  | QObject *ret; | 
|  |  | 
|  | if (QSLIST_EMPTY(&qiv->stack)) { | 
|  | /* Starting at root, name is ignored. */ | 
|  | assert(qiv->root); | 
|  | return qiv->root; | 
|  | } | 
|  |  | 
|  | /* We are in a container; find the next element. */ | 
|  | tos = QSLIST_FIRST(&qiv->stack); | 
|  | qobj = tos->obj; | 
|  | assert(qobj); | 
|  |  | 
|  | if (qobject_type(qobj) == QTYPE_QDICT) { | 
|  | assert(name); | 
|  | ret = qdict_get(qobject_to(QDict, qobj), name); | 
|  | if (tos->h && consume && ret) { | 
|  | bool removed = g_hash_table_remove(tos->h, name); | 
|  | assert(removed); | 
|  | } | 
|  | } else { | 
|  | assert(qobject_type(qobj) == QTYPE_QLIST); | 
|  | assert(!name); | 
|  | if (tos->entry) { | 
|  | ret = qlist_entry_obj(tos->entry); | 
|  | if (consume) { | 
|  | tos->entry = qlist_next(tos->entry); | 
|  | } | 
|  | } else { | 
|  | ret = NULL; | 
|  | } | 
|  | if (consume) { | 
|  | tos->index++; | 
|  | } | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static QObject *qobject_input_get_object(QObjectInputVisitor *qiv, | 
|  | const char *name, | 
|  | bool consume, Error **errp) | 
|  | { | 
|  | QObject *obj = qobject_input_try_get_object(qiv, name, consume); | 
|  |  | 
|  | if (!obj) { | 
|  | error_setg(errp, QERR_MISSING_PARAMETER, full_name(qiv, name)); | 
|  | } | 
|  | return obj; | 
|  | } | 
|  |  | 
|  | static const char *qobject_input_get_keyval(QObjectInputVisitor *qiv, | 
|  | const char *name, | 
|  | Error **errp) | 
|  | { | 
|  | QObject *qobj; | 
|  | QString *qstr; | 
|  |  | 
|  | qobj = qobject_input_get_object(qiv, name, true, errp); | 
|  | if (!qobj) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | qstr = qobject_to(QString, qobj); | 
|  | if (!qstr) { | 
|  | switch (qobject_type(qobj)) { | 
|  | case QTYPE_QDICT: | 
|  | case QTYPE_QLIST: | 
|  | error_setg(errp, "Parameters '%s.*' are unexpected", | 
|  | full_name(qiv, name)); | 
|  | return NULL; | 
|  | default: | 
|  | /* Non-string scalar (should this be an assertion?) */ | 
|  | error_setg(errp, "Internal error: parameter %s invalid", | 
|  | full_name(qiv, name)); | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | return qstring_get_str(qstr); | 
|  | } | 
|  |  | 
|  | static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv, | 
|  | const char *name, | 
|  | QObject *obj, void *qapi) | 
|  | { | 
|  | GHashTable *h; | 
|  | StackObject *tos = g_new0(StackObject, 1); | 
|  | QDict *qdict = qobject_to(QDict, obj); | 
|  | QList *qlist = qobject_to(QList, obj); | 
|  | const QDictEntry *entry; | 
|  |  | 
|  | assert(obj); | 
|  | tos->name = name; | 
|  | tos->obj = obj; | 
|  | tos->qapi = qapi; | 
|  |  | 
|  | if (qdict) { | 
|  | h = g_hash_table_new(g_str_hash, g_str_equal); | 
|  | for (entry = qdict_first(qdict); | 
|  | entry; | 
|  | entry = qdict_next(qdict, entry)) { | 
|  | g_hash_table_insert(h, (void *)qdict_entry_key(entry), NULL); | 
|  | } | 
|  | tos->h = h; | 
|  | } else { | 
|  | assert(qlist); | 
|  | tos->entry = qlist_first(qlist); | 
|  | tos->index = -1; | 
|  | } | 
|  |  | 
|  | QSLIST_INSERT_HEAD(&qiv->stack, tos, node); | 
|  | return tos->entry; | 
|  | } | 
|  |  | 
|  |  | 
|  | static bool qobject_input_check_struct(Visitor *v, Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | StackObject *tos = QSLIST_FIRST(&qiv->stack); | 
|  | GHashTableIter iter; | 
|  | const char *key; | 
|  |  | 
|  | assert(tos && !tos->entry); | 
|  |  | 
|  | g_hash_table_iter_init(&iter, tos->h); | 
|  | if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) { | 
|  | error_setg(errp, "Parameter '%s' is unexpected", | 
|  | full_name(qiv, key)); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static void qobject_input_stack_object_free(StackObject *tos) | 
|  | { | 
|  | if (tos->h) { | 
|  | g_hash_table_unref(tos->h); | 
|  | } | 
|  |  | 
|  | g_free(tos); | 
|  | } | 
|  |  | 
|  | static void qobject_input_pop(Visitor *v, void **obj) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | StackObject *tos = QSLIST_FIRST(&qiv->stack); | 
|  |  | 
|  | assert(tos && tos->qapi == obj); | 
|  | QSLIST_REMOVE_HEAD(&qiv->stack, node); | 
|  | qobject_input_stack_object_free(tos); | 
|  | } | 
|  |  | 
|  | static bool qobject_input_start_struct(Visitor *v, const char *name, void **obj, | 
|  | size_t size, Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | 
|  |  | 
|  | if (obj) { | 
|  | *obj = NULL; | 
|  | } | 
|  | if (!qobj) { | 
|  | return false; | 
|  | } | 
|  | if (qobject_type(qobj) != QTYPE_QDICT) { | 
|  | error_setg(errp, "Invalid parameter type for '%s', expected: object", | 
|  | full_name(qiv, name)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | qobject_input_push(qiv, name, qobj, obj); | 
|  |  | 
|  | if (obj) { | 
|  | *obj = g_malloc0(size); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static void qobject_input_end_struct(Visitor *v, void **obj) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | StackObject *tos = QSLIST_FIRST(&qiv->stack); | 
|  |  | 
|  | assert(qobject_type(tos->obj) == QTYPE_QDICT && tos->h); | 
|  | qobject_input_pop(v, obj); | 
|  | } | 
|  |  | 
|  |  | 
|  | static bool qobject_input_start_list(Visitor *v, const char *name, | 
|  | GenericList **list, size_t size, | 
|  | Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | 
|  | const QListEntry *entry; | 
|  |  | 
|  | if (list) { | 
|  | *list = NULL; | 
|  | } | 
|  | if (!qobj) { | 
|  | return false; | 
|  | } | 
|  | if (qobject_type(qobj) != QTYPE_QLIST) { | 
|  | error_setg(errp, "Invalid parameter type for '%s', expected: array", | 
|  | full_name(qiv, name)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | entry = qobject_input_push(qiv, name, qobj, list); | 
|  | if (entry && list) { | 
|  | *list = g_malloc0(size); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail, | 
|  | size_t size) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | StackObject *tos = QSLIST_FIRST(&qiv->stack); | 
|  |  | 
|  | assert(tos && qobject_to(QList, tos->obj)); | 
|  |  | 
|  | if (!tos->entry) { | 
|  | return NULL; | 
|  | } | 
|  | tail->next = g_malloc0(size); | 
|  | return tail->next; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_check_list(Visitor *v, Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | StackObject *tos = QSLIST_FIRST(&qiv->stack); | 
|  |  | 
|  | assert(tos && qobject_to(QList, tos->obj)); | 
|  |  | 
|  | if (tos->entry) { | 
|  | error_setg(errp, "Only %u list elements expected in %s", | 
|  | tos->index + 1, full_name_nth(qiv, NULL, 1)); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static void qobject_input_end_list(Visitor *v, void **obj) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | StackObject *tos = QSLIST_FIRST(&qiv->stack); | 
|  |  | 
|  | assert(qobject_type(tos->obj) == QTYPE_QLIST && !tos->h); | 
|  | qobject_input_pop(v, obj); | 
|  | } | 
|  |  | 
|  | static bool qobject_input_start_alternate(Visitor *v, const char *name, | 
|  | GenericAlternate **obj, size_t size, | 
|  | Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | QObject *qobj = qobject_input_get_object(qiv, name, false, errp); | 
|  |  | 
|  | if (!qobj) { | 
|  | *obj = NULL; | 
|  | return false; | 
|  | } | 
|  | *obj = g_malloc0(size); | 
|  | (*obj)->type = qobject_type(qobj); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj, | 
|  | Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | 
|  | QNum *qnum; | 
|  |  | 
|  | if (!qobj) { | 
|  | return false; | 
|  | } | 
|  | qnum = qobject_to(QNum, qobj); | 
|  | if (!qnum || !qnum_get_try_int(qnum, obj)) { | 
|  | error_setg(errp, "Invalid parameter type for '%s', expected: integer", | 
|  | full_name(qiv, name)); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_type_int64_keyval(Visitor *v, const char *name, | 
|  | int64_t *obj, Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | const char *str = qobject_input_get_keyval(qiv, name, errp); | 
|  |  | 
|  | if (!str) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (qemu_strtoi64(str, NULL, 0, obj) < 0) { | 
|  | /* TODO report -ERANGE more nicely */ | 
|  | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | 
|  | full_name(qiv, name), "integer"); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_type_uint64(Visitor *v, const char *name, | 
|  | uint64_t *obj, Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | 
|  | QNum *qnum; | 
|  | int64_t val; | 
|  |  | 
|  | if (!qobj) { | 
|  | return false; | 
|  | } | 
|  | qnum = qobject_to(QNum, qobj); | 
|  | if (!qnum) { | 
|  | goto err; | 
|  | } | 
|  |  | 
|  | if (qnum_get_try_uint(qnum, obj)) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* Need to accept negative values for backward compatibility */ | 
|  | if (qnum_get_try_int(qnum, &val)) { | 
|  | *obj = val; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | err: | 
|  | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | 
|  | full_name(qiv, name), "uint64"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_type_uint64_keyval(Visitor *v, const char *name, | 
|  | uint64_t *obj, Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | const char *str = qobject_input_get_keyval(qiv, name, errp); | 
|  |  | 
|  | if (!str) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (qemu_strtou64(str, NULL, 0, obj) < 0) { | 
|  | /* TODO report -ERANGE more nicely */ | 
|  | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | 
|  | full_name(qiv, name), "integer"); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_type_bool(Visitor *v, const char *name, bool *obj, | 
|  | Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | 
|  | QBool *qbool; | 
|  |  | 
|  | if (!qobj) { | 
|  | return false; | 
|  | } | 
|  | qbool = qobject_to(QBool, qobj); | 
|  | if (!qbool) { | 
|  | error_setg(errp, "Invalid parameter type for '%s', expected: boolean", | 
|  | full_name(qiv, name)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *obj = qbool_get_bool(qbool); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_type_bool_keyval(Visitor *v, const char *name, | 
|  | bool *obj, Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | const char *str = qobject_input_get_keyval(qiv, name, errp); | 
|  |  | 
|  | if (!str) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!qapi_bool_parse(name, str, obj, NULL)) { | 
|  | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | 
|  | full_name(qiv, name), "'on' or 'off'"); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_type_str(Visitor *v, const char *name, char **obj, | 
|  | Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | 
|  | QString *qstr; | 
|  |  | 
|  | *obj = NULL; | 
|  | if (!qobj) { | 
|  | return false; | 
|  | } | 
|  | qstr = qobject_to(QString, qobj); | 
|  | if (!qstr) { | 
|  | error_setg(errp, "Invalid parameter type for '%s', expected: string", | 
|  | full_name(qiv, name)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *obj = g_strdup(qstring_get_str(qstr)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_type_str_keyval(Visitor *v, const char *name, | 
|  | char **obj, Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | const char *str = qobject_input_get_keyval(qiv, name, errp); | 
|  |  | 
|  | *obj = g_strdup(str); | 
|  | return !!str; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_type_number(Visitor *v, const char *name, double *obj, | 
|  | Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | 
|  | QNum *qnum; | 
|  |  | 
|  | if (!qobj) { | 
|  | return false; | 
|  | } | 
|  | qnum = qobject_to(QNum, qobj); | 
|  | if (!qnum) { | 
|  | error_setg(errp, "Invalid parameter type for '%s', expected: number", | 
|  | full_name(qiv, name)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *obj = qnum_get_double(qnum); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_type_number_keyval(Visitor *v, const char *name, | 
|  | double *obj, Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | const char *str = qobject_input_get_keyval(qiv, name, errp); | 
|  | double val; | 
|  |  | 
|  | if (!str) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (qemu_strtod_finite(str, NULL, &val)) { | 
|  | /* TODO report -ERANGE more nicely */ | 
|  | error_setg(errp, "Invalid parameter type for '%s', expected: number", | 
|  | full_name(qiv, name)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *obj = val; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_type_any(Visitor *v, const char *name, QObject **obj, | 
|  | Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | 
|  |  | 
|  | *obj = NULL; | 
|  | if (!qobj) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *obj = qobject_ref(qobj); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_type_null(Visitor *v, const char *name, | 
|  | QNull **obj, Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | 
|  |  | 
|  | *obj = NULL; | 
|  | if (!qobj) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (qobject_type(qobj) != QTYPE_QNULL) { | 
|  | error_setg(errp, "Invalid parameter type for '%s', expected: null", | 
|  | full_name(qiv, name)); | 
|  | return false; | 
|  | } | 
|  | *obj = qnull(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_type_size_keyval(Visitor *v, const char *name, | 
|  | uint64_t *obj, Error **errp) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | const char *str = qobject_input_get_keyval(qiv, name, errp); | 
|  |  | 
|  | if (!str) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (qemu_strtosz(str, NULL, obj) < 0) { | 
|  | /* TODO report -ERANGE more nicely */ | 
|  | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | 
|  | full_name(qiv, name), "size"); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static void qobject_input_optional(Visitor *v, const char *name, bool *present) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  | QObject *qobj = qobject_input_try_get_object(qiv, name, false); | 
|  |  | 
|  | if (!qobj) { | 
|  | *present = false; | 
|  | return; | 
|  | } | 
|  |  | 
|  | *present = true; | 
|  | } | 
|  |  | 
|  | static bool qobject_input_policy_reject(Visitor *v, const char *name, | 
|  | unsigned special_features, | 
|  | Error **errp) | 
|  | { | 
|  | return !compat_policy_input_ok(special_features, &v->compat_policy, | 
|  | ERROR_CLASS_GENERIC_ERROR, | 
|  | "parameter", name, errp); | 
|  | } | 
|  |  | 
|  | static void qobject_input_free(Visitor *v) | 
|  | { | 
|  | QObjectInputVisitor *qiv = to_qiv(v); | 
|  |  | 
|  | while (!QSLIST_EMPTY(&qiv->stack)) { | 
|  | StackObject *tos = QSLIST_FIRST(&qiv->stack); | 
|  |  | 
|  | QSLIST_REMOVE_HEAD(&qiv->stack, node); | 
|  | qobject_input_stack_object_free(tos); | 
|  | } | 
|  |  | 
|  | qobject_unref(qiv->root); | 
|  | if (qiv->errname) { | 
|  | g_string_free(qiv->errname, TRUE); | 
|  | } | 
|  | g_free(qiv); | 
|  | } | 
|  |  | 
|  | static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj) | 
|  | { | 
|  | QObjectInputVisitor *v = g_malloc0(sizeof(*v)); | 
|  |  | 
|  | assert(obj); | 
|  |  | 
|  | v->visitor.type = VISITOR_INPUT; | 
|  | v->visitor.start_struct = qobject_input_start_struct; | 
|  | v->visitor.check_struct = qobject_input_check_struct; | 
|  | v->visitor.end_struct = qobject_input_end_struct; | 
|  | v->visitor.start_list = qobject_input_start_list; | 
|  | v->visitor.next_list = qobject_input_next_list; | 
|  | v->visitor.check_list = qobject_input_check_list; | 
|  | v->visitor.end_list = qobject_input_end_list; | 
|  | v->visitor.start_alternate = qobject_input_start_alternate; | 
|  | v->visitor.optional = qobject_input_optional; | 
|  | v->visitor.policy_reject = qobject_input_policy_reject; | 
|  | v->visitor.free = qobject_input_free; | 
|  |  | 
|  | v->root = qobject_ref(obj); | 
|  |  | 
|  | return v; | 
|  | } | 
|  |  | 
|  | Visitor *qobject_input_visitor_new(QObject *obj) | 
|  | { | 
|  | QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); | 
|  |  | 
|  | v->visitor.type_int64 = qobject_input_type_int64; | 
|  | v->visitor.type_uint64 = qobject_input_type_uint64; | 
|  | v->visitor.type_bool = qobject_input_type_bool; | 
|  | v->visitor.type_str = qobject_input_type_str; | 
|  | v->visitor.type_number = qobject_input_type_number; | 
|  | v->visitor.type_any = qobject_input_type_any; | 
|  | v->visitor.type_null = qobject_input_type_null; | 
|  |  | 
|  | return &v->visitor; | 
|  | } | 
|  |  | 
|  | Visitor *qobject_input_visitor_new_keyval(QObject *obj) | 
|  | { | 
|  | QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); | 
|  |  | 
|  | v->visitor.type_int64 = qobject_input_type_int64_keyval; | 
|  | v->visitor.type_uint64 = qobject_input_type_uint64_keyval; | 
|  | v->visitor.type_bool = qobject_input_type_bool_keyval; | 
|  | v->visitor.type_str = qobject_input_type_str_keyval; | 
|  | v->visitor.type_number = qobject_input_type_number_keyval; | 
|  | v->visitor.type_any = qobject_input_type_any; | 
|  | v->visitor.type_null = qobject_input_type_null; | 
|  | v->visitor.type_size = qobject_input_type_size_keyval; | 
|  | v->keyval = true; | 
|  |  | 
|  | return &v->visitor; | 
|  | } | 
|  |  | 
|  | Visitor *qobject_input_visitor_new_str(const char *str, | 
|  | const char *implied_key, | 
|  | Error **errp) | 
|  | { | 
|  | bool is_json = str[0] == '{'; | 
|  | QObject *obj; | 
|  | QDict *args; | 
|  | Visitor *v; | 
|  |  | 
|  | if (is_json) { | 
|  | obj = qobject_from_json(str, errp); | 
|  | if (!obj) { | 
|  | return NULL; | 
|  | } | 
|  | args = qobject_to(QDict, obj); | 
|  | assert(args); | 
|  | v = qobject_input_visitor_new(QOBJECT(args)); | 
|  | } else { | 
|  | args = keyval_parse(str, implied_key, NULL, errp); | 
|  | if (!args) { | 
|  | return NULL; | 
|  | } | 
|  | v = qobject_input_visitor_new_keyval(QOBJECT(args)); | 
|  | } | 
|  | qobject_unref(args); | 
|  |  | 
|  | return v; | 
|  | } |