From nobody Tue May 14 01:32:34 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1487711037959637.6403160019726; Tue, 21 Feb 2017 13:03:57 -0800 (PST) Received: from localhost ([::1]:48562 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cgHbI-0006EF-Nl for importer@patchew.org; Tue, 21 Feb 2017 16:03:56 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38700) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cgHZM-0005AR-O3 for qemu-devel@nongnu.org; Tue, 21 Feb 2017 16:01:57 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cgHZM-0006de-2a for qemu-devel@nongnu.org; Tue, 21 Feb 2017 16:01:56 -0500 Received: from mx1.redhat.com ([209.132.183.28]:34438) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1cgHZH-0006ax-OR; Tue, 21 Feb 2017 16:01:51 -0500 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 8F1824E03F; Tue, 21 Feb 2017 21:01:51 +0000 (UTC) Received: from blackfin.pond.sub.org (ovpn-116-55.ams2.redhat.com [10.36.116.55]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id v1LL1nPP017236 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Tue, 21 Feb 2017 16:01:51 -0500 Received: by blackfin.pond.sub.org (Postfix, from userid 1000) id 6132D113864A; Tue, 21 Feb 2017 22:01:48 +0100 (CET) From: Markus Armbruster To: qemu-devel@nongnu.org Date: Tue, 21 Feb 2017 22:01:44 +0100 Message-Id: <1487710908-26356-2-git-send-email-armbru@redhat.com> In-Reply-To: <1487710908-26356-1-git-send-email-armbru@redhat.com> References: <1487710908-26356-1-git-send-email-armbru@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Tue, 21 Feb 2017 21:01:51 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH RFC v3 1/5] tests: Fix gcov-files-test-qemu-opts-y, gcov-files-test-logging-y X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, pkrempa@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake --- tests/Makefile.include | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index e60bb6c..5591f60 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -94,7 +94,7 @@ gcov-files-check-qom-interface-y =3D qom/object.c check-unit-y +=3D tests/check-qom-proplist$(EXESUF) gcov-files-check-qom-proplist-y =3D qom/object.c check-unit-y +=3D tests/test-qemu-opts$(EXESUF) -gcov-files-test-qemu-opts-y =3D qom/test-qemu-opts.c +gcov-files-test-qemu-opts-y =3D util/qemu-option.c check-unit-y +=3D tests/test-write-threshold$(EXESUF) gcov-files-test-write-threshold-y =3D block/write-threshold.c check-unit-y +=3D tests/test-crypto-hash$(EXESUF) @@ -119,8 +119,8 @@ check-unit-y +=3D tests/test-crypto-ivgen$(EXESUF) check-unit-y +=3D tests/test-crypto-afsplit$(EXESUF) check-unit-y +=3D tests/test-crypto-xts$(EXESUF) check-unit-y +=3D tests/test-crypto-block$(EXESUF) -gcov-files-test-logging-y =3D tests/test-logging.c check-unit-y +=3D tests/test-logging$(EXESUF) +gcov-files-test-logging-y =3D util/log.c check-unit-$(CONFIG_REPLICATION) +=3D tests/test-replication$(EXESUF) check-unit-y +=3D tests/test-bufferiszero$(EXESUF) gcov-files-check-bufferiszero-y =3D util/bufferiszero.c --=20 2.7.4 From nobody Tue May 14 01:32:34 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1487711258835830.8651834610557; Tue, 21 Feb 2017 13:07:38 -0800 (PST) Received: from localhost ([::1]:48580 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cgHer-0000a9-Ia for importer@patchew.org; Tue, 21 Feb 2017 16:07:37 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38763) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cgHZO-0005EP-M4 for qemu-devel@nongnu.org; Tue, 21 Feb 2017 16:02:00 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cgHZM-0006eG-VX for qemu-devel@nongnu.org; Tue, 21 Feb 2017 16:01:58 -0500 Received: from mx1.redhat.com ([209.132.183.28]:36908) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1cgHZH-0006b9-Us; Tue, 21 Feb 2017 16:01:52 -0500 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0E087C054C5B; Tue, 21 Feb 2017 21:01:52 +0000 (UTC) Received: from blackfin.pond.sub.org (ovpn-116-55.ams2.redhat.com [10.36.116.55]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id v1LL1nFZ031734 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Tue, 21 Feb 2017 16:01:51 -0500 Received: by blackfin.pond.sub.org (Postfix, from userid 1000) id 642DA113864D; Tue, 21 Feb 2017 22:01:48 +0100 (CET) From: Markus Armbruster To: qemu-devel@nongnu.org Date: Tue, 21 Feb 2017 22:01:45 +0100 Message-Id: <1487710908-26356-3-git-send-email-armbru@redhat.com> In-Reply-To: <1487710908-26356-1-git-send-email-armbru@redhat.com> References: <1487710908-26356-1-git-send-email-armbru@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.27 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Tue, 21 Feb 2017 21:01:52 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH RFC v3 2/5] keyval: New keyval_parse() X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, pkrempa@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" keyval_parse() parses KEY=3DVALUE,... into a QDict. Works like qemu_opts_parse(), except: * Returns a QDict instead of a QemuOpts (d'oh). * It supports nesting, unlike QemuOpts: a KEY is split into key components at '.' (dotted key convention; the block layer does something similar on top of QemuOpts). The key components are QDict keys, and the last one's value is updated to VALUE. * Each key component may be up to 127 bytes long. qemu_opts_parse() limits the entire key to 127 bytes. * Overlong key components are rejected. qemu_opts_parse() silently truncates them. * Empty key components are rejected. qemu_opts_parse() happily accepts empty keys. * It does not store the returned value. qemu_opts_parse() stores it in the QemuOptsList. * It does not treat parameter "id" specially. qemu_opts_parse() ignores all but the first "id", and fails when its value isn't id_wellformed(), or duplicate (a QemuOpts with the same ID is already stored). It also screws up when a value contains ",id=3D". I intend to grow this into a saner replacement for QemuOpts. It'll take time, though. TODO Support lists TODO Function comment is missing. Signed-off-by: Markus Armbruster --- include/qemu/option.h | 3 + tests/.gitignore | 1 + tests/Makefile.include | 3 + tests/test-keyval.c | 154 +++++++++++++++++++++++++++++++++++++++++++++= ++++ util/Makefile.objs | 1 + util/keyval.c | 150 +++++++++++++++++++++++++++++++++++++++++++++= ++ 6 files changed, 312 insertions(+) create mode 100644 tests/test-keyval.c create mode 100644 util/keyval.c diff --git a/include/qemu/option.h b/include/qemu/option.h index e786df0..f7338db 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -141,4 +141,7 @@ void qemu_opts_print_help(QemuOptsList *list); void qemu_opts_free(QemuOptsList *list); QemuOptsList *qemu_opts_append(QemuOptsList *dst, QemuOptsList *list); =20 +QDict *keyval_parse(const char *params, const char *implied_key, + Error **errp); + #endif diff --git a/tests/.gitignore b/tests/.gitignore index dc37519..30b7740 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -47,6 +47,7 @@ test-io-channel-file.txt test-io-channel-socket test-io-channel-tls test-io-task +test-keyval test-logging test-mul64 test-opts-visitor diff --git a/tests/Makefile.include b/tests/Makefile.include index 5591f60..5b66651 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -95,6 +95,8 @@ check-unit-y +=3D tests/check-qom-proplist$(EXESUF) gcov-files-check-qom-proplist-y =3D qom/object.c check-unit-y +=3D tests/test-qemu-opts$(EXESUF) gcov-files-test-qemu-opts-y =3D util/qemu-option.c +check-unit-y +=3D tests/test-keyval$(EXESUF) +gcov-files-test-keyval-y =3D util/keyval.c check-unit-y +=3D tests/test-write-threshold$(EXESUF) gcov-files-test-write-threshold-y =3D block/write-threshold.c check-unit-y +=3D tests/test-crypto-hash$(EXESUF) @@ -720,6 +722,7 @@ tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o= $(test-util-obj-y) \ $(chardev-obj-y) tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_s= cm_helper.o tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y) +tests/test-keyval$(EXESUF): tests/test-keyval.o $(test-util-obj-y) tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-b= lock-obj-y) tests/test-netfilter$(EXESUF): tests/test-netfilter.o $(qtest-obj-y) tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-= y) diff --git a/tests/test-keyval.c b/tests/test-keyval.c new file mode 100644 index 0000000..91b4391 --- /dev/null +++ b/tests/test-keyval.c @@ -0,0 +1,154 @@ +/* + * Unit tests for parsing of KEY=3DVALUE,... strings + * + * Copyright (C) 2017 Red Hat Inc. + * + * Authors: + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/option.h" + +static void test_keyval_parse(void) +{ + Error *err =3D NULL; + QDict *qdict, *sub_qdict; + char long_key[129]; + char *params; + + /* Nothing */ + qdict =3D keyval_parse("", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), =3D=3D, 0); + QDECREF(qdict); + + /* Empty key */ + qdict =3D keyval_parse("=3Dval", NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Empty key component */ + qdict =3D keyval_parse(".", NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + qdict =3D keyval_parse("key.", NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Overlong key */ + memset(long_key, 'a', 127); + long_key[127] =3D 'z'; + long_key[128] =3D 0; + params =3D g_strdup_printf("k.%s=3Dv", long_key); + qdict =3D keyval_parse(params + 2, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Overlong key component */ + qdict =3D keyval_parse(params, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + g_free(params); + + /* Long key */ + params =3D g_strdup_printf("k.%s=3Dv", long_key + 1); + qdict =3D keyval_parse(params + 2, NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), =3D=3D, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, long_key + 1), =3D=3D, "v"); + QDECREF(qdict); + + /* Long key component */ + qdict =3D keyval_parse(params, NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), =3D=3D, 1); + sub_qdict =3D qdict_get_qdict(qdict, "k"); + g_assert(sub_qdict); + g_assert_cmpuint(qdict_size(sub_qdict), =3D=3D, 1); + g_assert_cmpstr(qdict_get_try_str(sub_qdict, long_key + 1), =3D=3D, "v= "); + QDECREF(qdict); + g_free(params); + + /* Multiple keys, last one wins */ + qdict =3D keyval_parse("a=3D1,b=3D2,,x,a=3D3", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), =3D=3D, 2); + g_assert_cmpstr(qdict_get_try_str(qdict, "a"), =3D=3D, "3"); + g_assert_cmpstr(qdict_get_try_str(qdict, "b"), =3D=3D, "2,x"); + QDECREF(qdict); + + /* Even when it doesn't in QemuOpts */ + qdict =3D keyval_parse("id=3Dfoo,id=3Dbar", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), =3D=3D, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "id"), =3D=3D, "bar"); + QDECREF(qdict); + + /* Dotted keys */ + qdict =3D keyval_parse("a.b.c=3D1,a.b.c=3D2,d=3D3", NULL, &error_abort= ); + g_assert_cmpuint(qdict_size(qdict), =3D=3D, 2); + sub_qdict =3D qdict_get_qdict(qdict, "a"); + g_assert(sub_qdict); + g_assert_cmpuint(qdict_size(sub_qdict), =3D=3D, 1); + sub_qdict =3D qdict_get_qdict(sub_qdict, "b"); + g_assert(sub_qdict); + g_assert_cmpuint(qdict_size(sub_qdict), =3D=3D, 1); + g_assert_cmpstr(qdict_get_try_str(sub_qdict, "c"), =3D=3D, "2"); + g_assert_cmpstr(qdict_get_try_str(qdict, "d"), =3D=3D, "3"); + QDECREF(qdict); + + /* Inconsistent dotted keys */ + qdict =3D keyval_parse("a.b=3D1,a=3D2", NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + qdict =3D keyval_parse("a.b=3D1,a.b.c=3D2", NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Implied value */ + qdict =3D keyval_parse("an,noaus,noaus=3D", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), =3D=3D, 3); + g_assert_cmpstr(qdict_get_try_str(qdict, "an"), =3D=3D, "on"); + g_assert_cmpstr(qdict_get_try_str(qdict, "aus"), =3D=3D, "off"); + g_assert_cmpstr(qdict_get_try_str(qdict, "noaus"), =3D=3D, ""); + QDECREF(qdict); + + /* Implied key */ + qdict =3D keyval_parse("an,noaus,noaus=3D", "implied", &error_abort); + g_assert_cmpuint(qdict_size(qdict), =3D=3D, 3); + g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), =3D=3D, "an"); + g_assert_cmpstr(qdict_get_try_str(qdict, "aus"), =3D=3D, "off"); + g_assert_cmpstr(qdict_get_try_str(qdict, "noaus"), =3D=3D, ""); + QDECREF(qdict); + + /* Trailing comma is ignored */ + qdict =3D keyval_parse("x=3Dy,", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), =3D=3D, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "x"), =3D=3D, "y"); + QDECREF(qdict); + + /* Except when it isn't */ + qdict =3D keyval_parse(",", NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Value containing ,id=3D not misinterpreted as QemuOpts does */ + qdict =3D keyval_parse("x=3D,,id=3Dbar", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), =3D=3D, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "x"), =3D=3D, ",id=3Dbar"); + QDECREF(qdict); + + /* Anti-social ID is left to caller */ + qdict =3D keyval_parse("id=3D666", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), =3D=3D, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "id"), =3D=3D, "666"); + QDECREF(qdict); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/keyval/keyval_parse", test_keyval_parse); + g_test_run(); + return 0; +} diff --git a/util/Makefile.objs b/util/Makefile.objs index bc629e2..06366b5 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -24,6 +24,7 @@ util-obj-y +=3D error.o qemu-error.o util-obj-y +=3D id.o util-obj-y +=3D iov.o qemu-config.o qemu-sockets.o uri.o notify.o util-obj-y +=3D qemu-option.o qemu-progress.o +util-obj-y +=3D keyval.o util-obj-y +=3D hexdump.o util-obj-y +=3D crc32c.o util-obj-y +=3D uuid.o diff --git a/util/keyval.c b/util/keyval.c new file mode 100644 index 0000000..c6cdd22 --- /dev/null +++ b/util/keyval.c @@ -0,0 +1,150 @@ +/* + * Parsing KEY=3DVALUE,... strings + * + * Copyright (C) 2017 Red Hat Inc. + * + * Authors: + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qmp/qstring.h" +#include "qemu/option.h" + +/* TODO Support lists */ + +static QObject *keyval_parse_put(QDict *qdict, const char *key, QString *v= alue, + Error **errp) +{ + QObject *old, *new; + + old =3D qdict_get(qdict, key); + if (old) { + if (qobject_type(old) !=3D (value ? QTYPE_QSTRING : QTYPE_QDICT)) { + error_setg(errp, "Option key '%s' used inconsistently", key); + return NULL; + } + if (!value) { + return old; + } + new =3D QOBJECT(value); + } else { + new =3D QOBJECT(value) ?: QOBJECT(qdict_new()); + } + qdict_put_obj(qdict, key, new); + return new; +} + +static const char *keyval_parse_one(QDict *qdict, const char *params, + const char *implied_key, + Error **errp) +{ + QDict *cur =3D qdict; + QObject *next; + const char *s, *key; + size_t len; + char key_buf[128]; + QString *val; + + s =3D params; + len =3D strcspn(s, ".=3D,"); + if (implied_key && (s[len] =3D=3D ',' || !s[len])) { + /* Desugar implied key */ + key =3D implied_key; + } else { + key_buf[0] =3D 0; + for (;;) { + if (!len) { + error_setg(errp, "Invalid option key"); + return NULL; + } + if (len >=3D sizeof(key_buf)) { + error_setg(errp, "Option key component '%.*s' is too long", + (int)len, s); + return NULL; + } + + if (key_buf[0]) { + next =3D keyval_parse_put(cur, key_buf, NULL, errp); + if (!next) { + return NULL; + } + cur =3D qobject_to_qdict(next); + assert(cur); + } + + memcpy(key_buf, s, len); + key_buf[len] =3D 0; + s +=3D len; + if (*s !=3D '.') { + break; + } + s++; + len =3D strcspn(s, ".=3D,"); + } + key =3D key_buf; + + if (*s =3D=3D '=3D') { + s++; + } else { + /* + * Desugar implied value: it's "on", except when @key + * starts with "no", it's "off". Thus, key "novocaine" + * gets desugard to "vocaine=3Doff", not to "novocaine=3Don". + * If sugar isn't bad enough for you, make it ambiguous... + */ + if (*s =3D=3D ',') + s++; + if (!strncmp(key, "no", 2)) { + key +=3D 2; + val =3D qstring_from_str("off"); + } else { + val =3D qstring_from_str("on"); + } + goto got_val; + } + } + + val =3D qstring_new(); + for (;;) { + if (!*s) { + break; + } else if (*s =3D=3D ',') { + s++; + if (*s !=3D ',') { + break; + } + } + qstring_append_chr(val, *s++); + } + +got_val: + if (!keyval_parse_put(cur, key, val, errp)) { + return NULL; + } + return s; +} + +/* TODO function comment */ +QDict *keyval_parse(const char *params, const char *implied_key, + Error **errp) +{ + QDict *qdict =3D qdict_new(); + const char *s; + + s =3D params; + while (*s) { + s =3D keyval_parse_one(qdict, s, implied_key, errp); + if (!s) { + QDECREF(qdict); + return NULL; + } + implied_key =3D NULL; + } + + return qdict; +} --=20 2.7.4 From nobody Tue May 14 01:32:34 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1487711256153958.6231773605414; Tue, 21 Feb 2017 13:07:36 -0800 (PST) Received: from localhost ([::1]:48578 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cgHen-0000Wp-Hc for importer@patchew.org; Tue, 21 Feb 2017 16:07:33 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38784) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cgHZR-0005Hu-IP for qemu-devel@nongnu.org; Tue, 21 Feb 2017 16:02:04 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cgHZO-0006fA-Nd for qemu-devel@nongnu.org; Tue, 21 Feb 2017 16:02:01 -0500 Received: from mx1.redhat.com ([209.132.183.28]:34610) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1cgHZI-0006bB-3M; Tue, 21 Feb 2017 16:01:52 -0500 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 301257FB6F; Tue, 21 Feb 2017 21:01:52 +0000 (UTC) Received: from blackfin.pond.sub.org (ovpn-116-55.ams2.redhat.com [10.36.116.55]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id v1LL1nQO031736 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Tue, 21 Feb 2017 16:01:51 -0500 Received: by blackfin.pond.sub.org (Postfix, from userid 1000) id 674C81138650; Tue, 21 Feb 2017 22:01:48 +0100 (CET) From: Markus Armbruster To: qemu-devel@nongnu.org Date: Tue, 21 Feb 2017 22:01:46 +0100 Message-Id: <1487710908-26356-4-git-send-email-armbru@redhat.com> In-Reply-To: <1487710908-26356-1-git-send-email-armbru@redhat.com> References: <1487710908-26356-1-git-send-email-armbru@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.27 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Tue, 21 Feb 2017 21:01:52 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH RFC v3 3/5] qapi: Permit scalar type conversions in QObjectInputVisitor X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, pkrempa@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: "Daniel P. Berrange" Currently the QObjectInputVisitor assumes that all scalar values are directly represented as the final types declared by the thing being visited. i.e. it assumes an 'int' is using QInt, and a 'bool' is using QBool, etc. This is good when QObjectInputVisitor is fed a QObject that came from a JSON document on the QMP monitor, as it will strictly validate correctness. To allow QObjectInputVisitor to be reused for visiting a QObject originating from keyval_parse(), an alternative mode is needed where all the scalars types are represented as QString and converted on the fly to the final desired type. Signed-off-by: Daniel P. Berrange Message-Id: <1475246744-29302-8-git-send-email-berrange@redhat.com> Rebased, conflicts resolved, commit message updated to refer to keyval_parse(). Control flow in qobject_input_type_number_autocast() simplified. Additional tests in test-qemu-opts.c to verify QemuOpts compatibility. To make the tests pass, use qemu_strtou64() instead of parse_uint_full(). Use qemu_strtou64() and qemu_strtosz() instead of parse_option_number() and parse_option_size() so we have to call qobject_input_get_name() only when actually needed. Signed-off-by: Markus Armbruster --- include/qapi/qobject-input-visitor.h | 32 ++++- qapi/qobject-input-visitor.c | 165 ++++++++++++++++++++++++ tests/test-keyval.c | 241 +++++++++++++++++++++++++++++++= ++++ tests/test-qobject-input-visitor.c | 194 +++++++++++++++++++++++++++- 4 files changed, 624 insertions(+), 8 deletions(-) diff --git a/include/qapi/qobject-input-visitor.h b/include/qapi/qobject-in= put-visitor.h index cde328d..5022297 100644 --- a/include/qapi/qobject-input-visitor.h +++ b/include/qapi/qobject-input-visitor.h @@ -19,12 +19,36 @@ =20 typedef struct QObjectInputVisitor QObjectInputVisitor; =20 -/* - * Return a new input visitor that converts a QObject to a QAPI object. +/** + * Create a new input visitor that converts @obj to a QAPI object. * - * Set @strict to reject a parse that doesn't consume all keys of a - * dictionary; otherwise excess input is ignored. + * Any scalar values in the @obj input data structure should be in the + * required type already. i.e. if visiting a bool, the value should + * already be a QBool instance. + * + * If @strict is set to true, then an error will be reported if any + * dict keys are not consumed during visitation. If @strict is false + * then extra dict keys are silently ignored. + * + * The returned input visitor should be released by calling + * visit_free() when no longer required. */ Visitor *qobject_input_visitor_new(QObject *obj, bool strict); =20 +/** + * Create a new input visitor that converts @obj to a QAPI object. + * + * Any scalar values in the @obj input data structure should always be + * represented as strings. i.e. if visiting a boolean, the value should + * be a QString whose contents represent a valid boolean. + * + * The visitor always operates in strict mode, requiring all dict keys + * to be consumed during visitation. An error will be reported if this + * does not happen. + * + * The returned input visitor should be released by calling + * visit_free() when no longer required. + */ +Visitor *qobject_input_visitor_new_autocast(QObject *obj); + #endif diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index 2439f1a..0bf6849 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -20,6 +20,7 @@ #include "qemu-common.h" #include "qapi/qmp/types.h" #include "qapi/qmp/qerror.h" +#include "qemu/cutils.h" =20 typedef struct StackObject { const char *name; /* Name if parent is QDict */ @@ -71,6 +72,7 @@ static const char *qobject_input_get_name(QObjectInputVis= itor *qiv, g_string_prepend(qiv->errname, name); g_string_prepend_c(qiv->errname, '.'); } else { + /* TODO needs to be .%zu for autocast */ snprintf(buf, sizeof(buf), "[%zu]", so->index); g_string_prepend(qiv->errname, buf); } @@ -321,6 +323,31 @@ static void qobject_input_type_int64(Visitor *v, const= char *name, int64_t *obj, *obj =3D qint_get_int(qint); } =20 + +static void qobject_input_type_int64_autocast(Visitor *v, const char *name, + int64_t *obj, Error **errp) +{ + QObjectInputVisitor *qiv =3D to_qiv(v); + QObject *qobj =3D qobject_input_get_object(qiv, name, true, errp); + QString *qstr; + + if (!qobj) { + return; + } + qstr =3D qobject_to_qstring(qobj); + if (!qstr) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, + qobject_input_get_name(qiv, name), "string"); + return; + } + + if (qemu_strtoi64(qstring_get_str(qstr), NULL, 0, obj) < 0) { + /* TODO report -ERANGE more nicely */ + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + qobject_input_get_name(qiv, name), "integer"); + } +} + static void qobject_input_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) { @@ -342,6 +369,30 @@ static void qobject_input_type_uint64(Visitor *v, cons= t char *name, *obj =3D qint_get_int(qint); } =20 +static void qobject_input_type_uint64_autocast(Visitor *v, const char *nam= e, + uint64_t *obj, Error **errp) +{ + QObjectInputVisitor *qiv =3D to_qiv(v); + QObject *qobj =3D qobject_input_get_object(qiv, name, true, errp); + QString *qstr; + + if (!qobj) { + return; + } + qstr =3D qobject_to_qstring(qobj); + if (!qstr) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, + qobject_input_get_name(qiv, name), "string"); + return; + } + + if (qemu_strtou64(qstring_get_str(qstr), NULL, 0, obj) < 0) { + /* TODO report -ERANGE more nicely */ + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + qobject_input_get_name(qiv, name), "integer"); + } +} + static void qobject_input_type_bool(Visitor *v, const char *name, bool *ob= j, Error **errp) { @@ -362,6 +413,35 @@ static void qobject_input_type_bool(Visitor *v, const = char *name, bool *obj, *obj =3D qbool_get_bool(qbool); } =20 +static void qobject_input_type_bool_autocast(Visitor *v, const char *name, + bool *obj, Error **errp) +{ + QObjectInputVisitor *qiv =3D to_qiv(v); + QObject *qobj =3D qobject_input_get_object(qiv, name, true, errp); + QString *qstr; + const char *str; + + if (!qobj) { + return; + } + qstr =3D qobject_to_qstring(qobj); + if (!qstr) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, + qobject_input_get_name(qiv, name), "string"); + return; + } + + str =3D qstring_get_str(qstr); + if (!strcmp(str, "on")) { + *obj =3D true; + } else if (!strcmp(str, "off")) { + *obj =3D false; + } else { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + qobject_input_get_name(qiv, name), "'on' or 'off'"); + } +} + static void qobject_input_type_str(Visitor *v, const char *name, char **ob= j, Error **errp) { @@ -410,6 +490,35 @@ static void qobject_input_type_number(Visitor *v, cons= t char *name, double *obj, qobject_input_get_name(qiv, name), "number"); } =20 +static void qobject_input_type_number_autocast(Visitor *v, const char *nam= e, + double *obj, Error **errp) +{ + QObjectInputVisitor *qiv =3D to_qiv(v); + QObject *qobj =3D qobject_input_get_object(qiv, name, true, errp); + QString *qstr; + const char *str; + char *endp; + + if (!qobj) { + return; + } + qstr =3D qobject_to_qstring(qobj); + if (!qstr) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, + qobject_input_get_name(qiv, name), "string"); + return; + } + + str =3D qstring_get_str(qstr); + errno =3D 0; + *obj =3D strtod(str, &endp); + if (errno || endp =3D=3D str || *endp) { + /* TODO report -ERANGE more nicely */ + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, + qobject_input_get_name(qiv, name), "number"); + } +} + static void qobject_input_type_any(Visitor *v, const char *name, QObject *= *obj, Error **errp) { @@ -440,6 +549,30 @@ static void qobject_input_type_null(Visitor *v, const = char *name, Error **errp) } } =20 +static void qobject_input_type_size_autocast(Visitor *v, const char *name, + uint64_t *obj, Error **errp) +{ + QObjectInputVisitor *qiv =3D to_qiv(v); + QObject *qobj =3D qobject_input_get_object(qiv, name, true, errp); + QString *qstr; + + if (!qobj) { + return; + } + qstr =3D qobject_to_qstring(qobj); + if (!qstr) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, + qobject_input_get_name(qiv, name), "string"); + return; + } + + if (qemu_strtosz(qstring_get_str(qstr), NULL, obj) < 0) { + /* TODO report -ERANGE more nicely */ + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + qobject_input_get_name(qiv, name), "size"); + } +} + static void qobject_input_optional(Visitor *v, const char *name, bool *pre= sent) { QObjectInputVisitor *qiv =3D to_qiv(v); @@ -502,3 +635,35 @@ Visitor *qobject_input_visitor_new(QObject *obj, bool = strict) =20 return &v->visitor; } + +Visitor *qobject_input_visitor_new_autocast(QObject *obj) +{ + QObjectInputVisitor *v; + + v =3D g_malloc0(sizeof(*v)); + + v->visitor.type =3D VISITOR_INPUT; + v->visitor.start_struct =3D qobject_input_start_struct; + v->visitor.check_struct =3D qobject_input_check_struct; + v->visitor.end_struct =3D qobject_input_pop; + v->visitor.start_list =3D qobject_input_start_list; + v->visitor.next_list =3D qobject_input_next_list; + v->visitor.end_list =3D qobject_input_pop; + v->visitor.start_alternate =3D qobject_input_start_alternate; + v->visitor.type_int64 =3D qobject_input_type_int64_autocast; + v->visitor.type_uint64 =3D qobject_input_type_uint64_autocast; + v->visitor.type_bool =3D qobject_input_type_bool_autocast; + v->visitor.type_str =3D qobject_input_type_str; + v->visitor.type_number =3D qobject_input_type_number_autocast; + v->visitor.type_any =3D qobject_input_type_any; + v->visitor.type_null =3D qobject_input_type_null; + v->visitor.type_size =3D qobject_input_type_size_autocast; + v->visitor.optional =3D qobject_input_optional; + v->visitor.free =3D qobject_input_free; + v->strict =3D true; + + v->root =3D obj; + qobject_incref(obj); + + return &v->visitor; +} diff --git a/tests/test-keyval.c b/tests/test-keyval.c index 91b4391..116ff2e 100644 --- a/tests/test-keyval.c +++ b/tests/test-keyval.c @@ -12,7 +12,9 @@ =20 #include "qemu/osdep.h" #include "qapi/error.h" +#include "qapi/qobject-input-visitor.h" #include "qemu/option.h" +#include "qemu/cutils.h" =20 static void test_keyval_parse(void) { @@ -145,10 +147,249 @@ static void test_keyval_parse(void) QDECREF(qdict); } =20 +static void test_parse_visit_bool(void) +{ + Error *err =3D NULL; + Visitor *v; + QDict *qdict; + bool b; + + qdict =3D keyval_parse("bool1=3Don,bool2=3Doff", NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_bool(v, "bool1", &b, &error_abort); + g_assert(b); + visit_type_bool(v, "bool2", &b, &error_abort); + g_assert(!b); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + qdict =3D keyval_parse("bool1=3Doffer", NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_bool(v, "bool1", &b, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); +} + +static void test_parse_visit_number(void) +{ + Error *err =3D NULL; + Visitor *v; + QDict *qdict; + uint64_t u; + + /* Lower limit zero */ + qdict =3D keyval_parse("number1=3D0", NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &error_abort); + g_assert_cmpuint(u, =3D=3D, 0); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Upper limit 2^64-1 */ + qdict =3D keyval_parse("number1=3D18446744073709551615,number2=3D-1", + NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &error_abort); + g_assert_cmphex(u, =3D=3D, UINT64_MAX); + visit_type_uint64(v, "number2", &u, &error_abort); + g_assert_cmphex(u, =3D=3D, UINT64_MAX); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Above upper limit */ + qdict =3D keyval_parse("number1=3D18446744073709551616", + NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); + + /* Below lower limit */ + qdict =3D keyval_parse("number1=3D-18446744073709551616", + NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); + + /* Hex and octal */ + qdict =3D keyval_parse("number1=3D0x2a,number2=3D052", + NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &error_abort); + g_assert_cmpuint(u, =3D=3D, 42); + visit_type_uint64(v, "number2", &u, &error_abort); + g_assert_cmpuint(u, =3D=3D, 42); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Trailing crap */ + qdict =3D keyval_parse("number1=3D3.14,number2=3D08", + NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &err); + error_free_or_abort(&err); + visit_type_uint64(v, "number2", &u, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); +} + +static void test_parse_visit_size(void) +{ + Error *err =3D NULL; + Visitor *v; + QDict *qdict; + uint64_t sz; + + /* Lower limit zero */ + qdict =3D keyval_parse("sz1=3D0", NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmpuint(sz, =3D=3D, 0); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Note: precision is 53 bits since we're parsing with strtod() */ + + /* Around limit of precision: 2^53-1, 2^53, 2^54 */ + qdict =3D keyval_parse("sz1=3D9007199254740991," + "sz2=3D9007199254740992," + "sz3=3D9007199254740993", + NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmphex(sz, =3D=3D, 0x1fffffffffffff); + visit_type_size(v, "sz2", &sz, &error_abort); + g_assert_cmphex(sz, =3D=3D, 0x20000000000000); + visit_type_size(v, "sz3", &sz, &error_abort); + g_assert_cmphex(sz, =3D=3D, 0x20000000000000); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */ + qdict =3D keyval_parse("sz1=3D9223372036854774784," /* 7ffffffffffffc0= 0 */ + "sz2=3D9223372036854775295", /* 7ffffffffffffdff = */ + NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmphex(sz, =3D=3D, 0x7ffffffffffffc00); + visit_type_size(v, "sz2", &sz, &error_abort); + g_assert_cmphex(sz, =3D=3D, 0x7ffffffffffffc00); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */ + qdict =3D keyval_parse("sz1=3D18446744073709549568," /* fffffffffffff8= 00 */ + "sz2=3D18446744073709550591", /* fffffffffffffbff= */ + NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmphex(sz, =3D=3D, 0xfffffffffffff800); + visit_type_size(v, "sz2", &sz, &error_abort); + g_assert_cmphex(sz, =3D=3D, 0xfffffffffffff800); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Beyond limits */ + qdict =3D keyval_parse("sz1=3D-1," + "sz2=3D18446744073709550592", /* fffffffffffffc00= */ + NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &err); + error_free_or_abort(&err); + visit_type_size(v, "sz2", &sz, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); + + /* Suffixes */ + qdict =3D keyval_parse("sz1=3D8b,sz2=3D1.5k,sz3=3D2M,sz4=3D0.1G,sz5=3D= 16777215T", + NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmpuint(sz, =3D=3D, 8); + visit_type_size(v, "sz2", &sz, &error_abort); + g_assert_cmpuint(sz, =3D=3D, 1536); + visit_type_size(v, "sz3", &sz, &error_abort); + g_assert_cmphex(sz, =3D=3D, 2 * M_BYTE); + visit_type_size(v, "sz4", &sz, &error_abort); + g_assert_cmphex(sz, =3D=3D, G_BYTE / 10); + visit_type_size(v, "sz5", &sz, &error_abort); + g_assert_cmphex(sz, =3D=3D, 16777215 * T_BYTE); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Beyond limit with suffix */ + qdict =3D keyval_parse("sz1=3D16777216T", NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); + + /* Trailing crap */ + qdict =3D keyval_parse("sz1=3D16E,sz2=3D16Gi", NULL, &error_abort); + v =3D qobject_input_visitor_new_autocast(QOBJECT(qdict)); + QDECREF(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &err); + error_free_or_abort(&err); + visit_type_size(v, "sz2", &sz, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); +} + int main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); g_test_add_func("/keyval/keyval_parse", test_keyval_parse); + g_test_add_func("/keyval/parse_visit/bool", test_parse_visit_bool); + g_test_add_func("/keyval/parse_visit/number", test_parse_visit_number); + g_test_add_func("/keyval/parse_visit/size", test_parse_visit_size); g_test_run(); return 0; } diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-= visitor.c index 945404a..95ad855 100644 --- a/tests/test-qobject-input-visitor.c +++ b/tests/test-qobject-input-visitor.c @@ -41,6 +41,7 @@ static void visitor_input_teardown(TestInputVisitorData *= data, function so that the JSON string used by the tests are kept in the test functions (and not in main()). */ static Visitor *visitor_input_test_init_internal(TestInputVisitorData *dat= a, + bool strict, bool autocas= t, const char *json_string, va_list *ap) { @@ -49,11 +50,31 @@ static Visitor *visitor_input_test_init_internal(TestIn= putVisitorData *data, data->obj =3D qobject_from_jsonv(json_string, ap); g_assert(data->obj); =20 - data->qiv =3D qobject_input_visitor_new(data->obj, false); + if (autocast) { + assert(strict); + data->qiv =3D qobject_input_visitor_new_autocast(data->obj); + } else { + data->qiv =3D qobject_input_visitor_new(data->obj, strict); + } g_assert(data->qiv); return data->qiv; } =20 +static GCC_FMT_ATTR(4, 5) +Visitor *visitor_input_test_init_full(TestInputVisitorData *data, + bool strict, bool autocast, + const char *json_string, ...) +{ + Visitor *v; + va_list ap; + + va_start(ap, json_string); + v =3D visitor_input_test_init_internal(data, strict, autocast, + json_string, &ap); + va_end(ap); + return v; +} + static GCC_FMT_ATTR(2, 3) Visitor *visitor_input_test_init(TestInputVisitorData *data, const char *json_string, ...) @@ -62,7 +83,8 @@ Visitor *visitor_input_test_init(TestInputVisitorData *da= ta, va_list ap; =20 va_start(ap, json_string); - v =3D visitor_input_test_init_internal(data, json_string, &ap); + v =3D visitor_input_test_init_internal(data, true, false, + json_string, &ap); va_end(ap); return v; } @@ -77,7 +99,8 @@ Visitor *visitor_input_test_init(TestInputVisitorData *da= ta, static Visitor *visitor_input_test_init_raw(TestInputVisitorData *data, const char *json_string) { - return visitor_input_test_init_internal(data, json_string, NULL); + return visitor_input_test_init_internal(data, true, false, + json_string, NULL); } =20 static void test_visitor_in_int(TestInputVisitorData *data, @@ -110,6 +133,45 @@ static void test_visitor_in_int_overflow(TestInputVisi= torData *data, error_free_or_abort(&err); } =20 +static void test_visitor_in_int_autocast(TestInputVisitorData *data, + const void *unused) +{ + int64_t res =3D 0, value =3D -42; + Error *err =3D NULL; + Visitor *v; + + v =3D visitor_input_test_init_full(data, true, true, + "%" PRId64, value); + visit_type_int(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_int_str_autocast(TestInputVisitorData *data, + const void *unused) +{ + int64_t res =3D 0, value =3D -42; + Visitor *v; + + v =3D visitor_input_test_init_full(data, true, true, + "\"-42\""); + + visit_type_int(v, NULL, &res, &error_abort); + g_assert_cmpint(res, =3D=3D, value); +} + +static void test_visitor_in_int_str_noautocast(TestInputVisitorData *data, + const void *unused) +{ + int64_t res =3D 0; + Visitor *v; + Error *err =3D NULL; + + v =3D visitor_input_test_init(data, "\"-42\""); + + visit_type_int(v, NULL, &res, &err); + error_free_or_abort(&err); +} + static void test_visitor_in_bool(TestInputVisitorData *data, const void *unused) { @@ -122,6 +184,44 @@ static void test_visitor_in_bool(TestInputVisitorData = *data, g_assert_cmpint(res, =3D=3D, true); } =20 +static void test_visitor_in_bool_autocast(TestInputVisitorData *data, + const void *unused) +{ + bool res =3D false; + Error *err =3D NULL; + Visitor *v; + + v =3D visitor_input_test_init_full(data, true, true, "true"); + + visit_type_bool(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_bool_str_autocast(TestInputVisitorData *data, + const void *unused) +{ + bool res =3D false; + Visitor *v; + + v =3D visitor_input_test_init_full(data, true, true, "\"on\""); + + visit_type_bool(v, NULL, &res, &error_abort); + g_assert_cmpint(res, =3D=3D, true); +} + +static void test_visitor_in_bool_str_noautocast(TestInputVisitorData *data, + const void *unused) +{ + bool res =3D false; + Visitor *v; + Error *err =3D NULL; + + v =3D visitor_input_test_init(data, "\"true\""); + + visit_type_bool(v, NULL, &res, &err); + error_free_or_abort(&err); +} + static void test_visitor_in_number(TestInputVisitorData *data, const void *unused) { @@ -134,6 +234,69 @@ static void test_visitor_in_number(TestInputVisitorDat= a *data, g_assert_cmpfloat(res, =3D=3D, value); } =20 +static void test_visitor_in_number_autocast(TestInputVisitorData *data, + const void *unused) +{ + double res =3D 0, value =3D 3.14; + Error *err =3D NULL; + Visitor *v; + + v =3D visitor_input_test_init_full(data, true, true, "%f", value); + + visit_type_number(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_number_str_autocast(TestInputVisitorData *data, + const void *unused) +{ + double res =3D 0, value =3D 3.14; + Visitor *v; + + v =3D visitor_input_test_init_full(data, true, true, "\"3.14\""); + + visit_type_number(v, NULL, &res, &error_abort); + g_assert_cmpfloat(res, =3D=3D, value); +} + +static void test_visitor_in_number_str_noautocast(TestInputVisitorData *da= ta, + const void *unused) +{ + double res =3D 0; + Visitor *v; + Error *err =3D NULL; + + v =3D visitor_input_test_init(data, "\"3.14\""); + + visit_type_number(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_size_str_autocast(TestInputVisitorData *data, + const void *unused) +{ + uint64_t res, value =3D 500 * 1024 * 1024; + Visitor *v; + + v =3D visitor_input_test_init_full(data, true, true, "\"500M\""); + + visit_type_size(v, NULL, &res, &error_abort); + g_assert_cmpfloat(res, =3D=3D, value); +} + +static void test_visitor_in_size_str_noautocast(TestInputVisitorData *data, + const void *unused) +{ + uint64_t res =3D 0; + Visitor *v; + Error *err =3D NULL; + + v =3D visitor_input_test_init(data, "\"500M\""); + + visit_type_size(v, NULL, &res, &err); + error_free_or_abort(&err); +} + static void test_visitor_in_string(TestInputVisitorData *data, const void *unused) { @@ -290,7 +453,8 @@ static void test_visitor_in_null(TestInputVisitorData *= data, * when input is not null. */ =20 - v =3D visitor_input_test_init(data, "{ 'a': null, 'b': '' }"); + v =3D visitor_input_test_init_full(data, false, false, + "{ 'a': null, 'b': '' }"); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_null(v, "a", &error_abort); visit_type_str(v, "a", &tmp, &err); @@ -841,10 +1005,32 @@ int main(int argc, char **argv) NULL, test_visitor_in_int); input_visitor_test_add("/visitor/input/int_overflow", NULL, test_visitor_in_int_overflow); + input_visitor_test_add("/visitor/input/int_autocast", + NULL, test_visitor_in_int_autocast); + input_visitor_test_add("/visitor/input/int_str_autocast", + NULL, test_visitor_in_int_str_autocast); + input_visitor_test_add("/visitor/input/int_str_noautocast", + NULL, test_visitor_in_int_str_noautocast); input_visitor_test_add("/visitor/input/bool", NULL, test_visitor_in_bool); + input_visitor_test_add("/visitor/input/bool_autocast", + NULL, test_visitor_in_bool_autocast); + input_visitor_test_add("/visitor/input/bool_str_autocast", + NULL, test_visitor_in_bool_str_autocast); + input_visitor_test_add("/visitor/input/bool_str_noautocast", + NULL, test_visitor_in_bool_str_noautocast); input_visitor_test_add("/visitor/input/number", NULL, test_visitor_in_number); + input_visitor_test_add("/visitor/input/number_autocast", + NULL, test_visitor_in_number_autocast); + input_visitor_test_add("/visitor/input/number_str_autocast", + NULL, test_visitor_in_number_str_autocast); + input_visitor_test_add("/visitor/input/number_str_noautocast", + NULL, test_visitor_in_number_str_noautocast); + input_visitor_test_add("/visitor/input/size_str_autocast", + NULL, test_visitor_in_size_str_autocast); + input_visitor_test_add("/visitor/input/size_str_noautocast", + NULL, test_visitor_in_size_str_noautocast); input_visitor_test_add("/visitor/input/string", NULL, test_visitor_in_string); input_visitor_test_add("/visitor/input/enum", --=20 2.7.4 From nobody Tue May 14 01:32:34 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1487711257734326.6092588081349; Tue, 21 Feb 2017 13:07:37 -0800 (PST) Received: from localhost ([::1]:48579 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cgHep-0000YC-GB for importer@patchew.org; Tue, 21 Feb 2017 16:07:35 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38717) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cgHZN-0005CU-7a for qemu-devel@nongnu.org; Tue, 21 Feb 2017 16:01:58 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cgHZM-0006do-A3 for qemu-devel@nongnu.org; Tue, 21 Feb 2017 16:01:57 -0500 Received: from mx1.redhat.com ([209.132.183.28]:44120) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1cgHZH-0006az-P1; Tue, 21 Feb 2017 16:01:51 -0500 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id B92EE80F8F; Tue, 21 Feb 2017 21:01:51 +0000 (UTC) Received: from blackfin.pond.sub.org (ovpn-116-55.ams2.redhat.com [10.36.116.55]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id v1LL1nAZ001092 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Tue, 21 Feb 2017 16:01:51 -0500 Received: by blackfin.pond.sub.org (Postfix, from userid 1000) id 6A0D21138657; Tue, 21 Feb 2017 22:01:48 +0100 (CET) From: Markus Armbruster To: qemu-devel@nongnu.org Date: Tue, 21 Feb 2017 22:01:47 +0100 Message-Id: <1487710908-26356-5-git-send-email-armbru@redhat.com> In-Reply-To: <1487710908-26356-1-git-send-email-armbru@redhat.com> References: <1487710908-26356-1-git-send-email-armbru@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Tue, 21 Feb 2017 21:01:51 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH RFC v3 4/5] qapi: Factor qobject_input_get_autocast() out of methods X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, pkrempa@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake --- qapi/qobject-input-visitor.c | 87 ++++++++++++++++++----------------------= ---- 1 file changed, 35 insertions(+), 52 deletions(-) diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index 0bf6849..970bb37 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -143,6 +143,28 @@ static QObject *qobject_input_get_object(QObjectInputV= isitor *qiv, return obj; } =20 +static const char *qobject_input_get_autocast(QObjectInputVisitor *qiv, + const char *name, + Error **errp) +{ + QObject *qobj; + QString *qstr; + + qobj =3D qobject_input_get_object(qiv, name, true, errp); + if (!qobj) { + return NULL; + } + + qstr =3D qobject_to_qstring(qobj); + if (!qstr) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, + qobject_input_get_name(qiv, name), "string"); + return NULL; + } + + return qstring_get_str(qstr); +} + static void qdict_add_key(const char *key, QObject *obj, void *opaque) { GHashTable *h =3D opaque; @@ -328,20 +350,13 @@ static void qobject_input_type_int64_autocast(Visitor= *v, const char *name, int64_t *obj, Error **errp) { QObjectInputVisitor *qiv =3D to_qiv(v); - QObject *qobj =3D qobject_input_get_object(qiv, name, true, errp); - QString *qstr; + const char *str =3D qobject_input_get_autocast(qiv, name, errp); =20 - if (!qobj) { - return; - } - qstr =3D qobject_to_qstring(qobj); - if (!qstr) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - qobject_input_get_name(qiv, name), "string"); + if (!str) { return; } =20 - if (qemu_strtoi64(qstring_get_str(qstr), NULL, 0, obj) < 0) { + if (qemu_strtoi64(str, NULL, 0, obj) < 0) { /* TODO report -ERANGE more nicely */ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, qobject_input_get_name(qiv, name), "integer"); @@ -373,20 +388,13 @@ static void qobject_input_type_uint64_autocast(Visito= r *v, const char *name, uint64_t *obj, Error **errp) { QObjectInputVisitor *qiv =3D to_qiv(v); - QObject *qobj =3D qobject_input_get_object(qiv, name, true, errp); - QString *qstr; + const char *str =3D qobject_input_get_autocast(qiv, name, errp); =20 - if (!qobj) { - return; - } - qstr =3D qobject_to_qstring(qobj); - if (!qstr) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - qobject_input_get_name(qiv, name), "string"); + if (!str) { return; } =20 - if (qemu_strtou64(qstring_get_str(qstr), NULL, 0, obj) < 0) { + if (qemu_strtou64(str, NULL, 0, obj) < 0) { /* TODO report -ERANGE more nicely */ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, qobject_input_get_name(qiv, name), "integer"); @@ -417,21 +425,12 @@ static void qobject_input_type_bool_autocast(Visitor = *v, const char *name, bool *obj, Error **errp) { QObjectInputVisitor *qiv =3D to_qiv(v); - QObject *qobj =3D qobject_input_get_object(qiv, name, true, errp); - QString *qstr; - const char *str; + const char *str =3D qobject_input_get_autocast(qiv, name, errp); =20 - if (!qobj) { - return; - } - qstr =3D qobject_to_qstring(qobj); - if (!qstr) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - qobject_input_get_name(qiv, name), "string"); + if (!str) { return; } =20 - str =3D qstring_get_str(qstr); if (!strcmp(str, "on")) { *obj =3D true; } else if (!strcmp(str, "off")) { @@ -494,22 +493,13 @@ static void qobject_input_type_number_autocast(Visito= r *v, const char *name, double *obj, Error **errp) { QObjectInputVisitor *qiv =3D to_qiv(v); - QObject *qobj =3D qobject_input_get_object(qiv, name, true, errp); - QString *qstr; - const char *str; + const char *str =3D qobject_input_get_autocast(qiv, name, errp); char *endp; =20 - if (!qobj) { - return; - } - qstr =3D qobject_to_qstring(qobj); - if (!qstr) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - qobject_input_get_name(qiv, name), "string"); + if (!str) { return; } =20 - str =3D qstring_get_str(qstr); errno =3D 0; *obj =3D strtod(str, &endp); if (errno || endp =3D=3D str || *endp) { @@ -553,20 +543,13 @@ static void qobject_input_type_size_autocast(Visitor = *v, const char *name, uint64_t *obj, Error **errp) { QObjectInputVisitor *qiv =3D to_qiv(v); - QObject *qobj =3D qobject_input_get_object(qiv, name, true, errp); - QString *qstr; + const char *str =3D qobject_input_get_autocast(qiv, name, errp); =20 - if (!qobj) { - return; - } - qstr =3D qobject_to_qstring(qobj); - if (!qstr) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - qobject_input_get_name(qiv, name), "string"); + if (!str) { return; } =20 - if (qemu_strtosz(qstring_get_str(qstr), NULL, obj) < 0) { + if (qemu_strtosz(str, NULL, obj) < 0) { /* TODO report -ERANGE more nicely */ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, qobject_input_get_name(qiv, name), "size"); --=20 2.7.4 From nobody Tue May 14 01:32:34 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1487711032880308.9685806454205; Tue, 21 Feb 2017 13:03:52 -0800 (PST) Received: from localhost ([::1]:48561 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cgHbB-000694-SX for importer@patchew.org; Tue, 21 Feb 2017 16:03:49 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38735) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cgHZN-0005D2-NI for qemu-devel@nongnu.org; Tue, 21 Feb 2017 16:01:58 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cgHZM-0006dz-Ii for qemu-devel@nongnu.org; Tue, 21 Feb 2017 16:01:57 -0500 Received: from mx1.redhat.com ([209.132.183.28]:52312) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1cgHZJ-0006c3-7X; Tue, 21 Feb 2017 16:01:53 -0500 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 5A21E7E9D0; Tue, 21 Feb 2017 21:01:53 +0000 (UTC) Received: from blackfin.pond.sub.org (ovpn-116-55.ams2.redhat.com [10.36.116.55]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id v1LL1pZt001104 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Tue, 21 Feb 2017 16:01:52 -0500 Received: by blackfin.pond.sub.org (Postfix, from userid 1000) id 6CFB311386CE; Tue, 21 Feb 2017 22:01:48 +0100 (CET) From: Markus Armbruster To: qemu-devel@nongnu.org Date: Tue, 21 Feb 2017 22:01:48 +0100 Message-Id: <1487710908-26356-6-git-send-email-armbru@redhat.com> In-Reply-To: <1487710908-26356-1-git-send-email-armbru@redhat.com> References: <1487710908-26356-1-git-send-email-armbru@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Tue, 21 Feb 2017 21:01:53 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH RFC v3 5/5] block: Crude initial implementation of -blockdev X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, pkrempa@redhat.com, qemu-block@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" The new command line option -blockdev works like QMP command blockdev-add. The option argument may be given in JSON syntax, exactly as in QMP. Example usage: -blockdev '{"node-name": "foo", "driver": "raw", "file": {"driver": "fi= le", "filename": "foo.img"} }' The JSON argument doesn't exactly blend into the existing option syntax, so the traditional KEY=3DVALUE,... syntax is also supported, using dotted keys to do the nesting: -blockdev node-name=3Dfoo,driver=3Draw,file.driver=3Dfile,file.filename= =3Dfoo.img Note that calling qmp_blockdev_add() (say via qmp_marshal_block_add()) right away would crash. We need to stash the configuration for later instead. This is crudely done, and bypasses QemuOpts, even though storing configuration is what QemuOpts is for. Need to revamp option infrastructure to support QAPI types like BlockdevOptions. Signed-off-by: Markus Armbruster --- qemu-options.hx | 3 +++ vl.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/qemu-options.hx b/qemu-options.hx index 9936cf3..36a38d7 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -512,6 +512,9 @@ Use @var{file} as CD-ROM image (you cannot use @option{= -hdc} and using @file{/dev/cdrom} as filename (@pxref{host_drives}). ETEXI =20 +DEF("blockdev", HAS_ARG, QEMU_OPTION_blockdev, + "-blockdev FIXME document\n", QEMU_OPTION_blockdev) + DEF("drive", HAS_ARG, QEMU_OPTION_drive, "-drive [file=3Dfile][,if=3Dtype][,bus=3Dn][,unit=3Dm][,media=3Dd][,in= dex=3Di]\n" " [,cyls=3Dc,heads=3Dh,secs=3Ds[,trans=3Dt]][,snapshot=3Don|off]= \n" diff --git a/vl.c b/vl.c index b5d0a19..37be73c 100644 --- a/vl.c +++ b/vl.c @@ -95,6 +95,9 @@ int main(int argc, char **argv) #include "migration/colo.h" #include "sysemu/kvm.h" #include "sysemu/hax.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi-visit.h" #include "qapi/qmp/qjson.h" #include "qemu/option.h" #include "qemu/config-file.h" @@ -2954,6 +2957,12 @@ int main(int argc, char **argv, char **envp) Error *main_loop_err =3D NULL; Error *err =3D NULL; bool list_data_dirs =3D false; + typedef struct BlockdevOptions_queue { + BlockdevOptions *bdo; + Location loc; + QSIMPLEQ_ENTRY(BlockdevOptions_queue) entry; + } BlockdevOptions_queue; + QSIMPLEQ_HEAD(, BlockdevOptions_queue) bdo_queue =3D QSIMPLEQ_HEAD_INI= TIALIZER(bdo_queue); =20 module_call_init(MODULE_INIT_TRACE); =20 @@ -3095,6 +3104,37 @@ int main(int argc, char **argv, char **envp) drive_add(IF_DEFAULT, popt->index - QEMU_OPTION_hda, optar= g, HD_OPTS); break; + case QEMU_OPTION_blockdev: + { + BlockdevOptions_queue *bdo =3D g_new(BlockdevOptions_q= ueue, 1); + bool is_json =3D optarg[0] =3D=3D '{'; + QObject *obj; + QDict *args; + Visitor *v; + + if (is_json) { + obj =3D qobject_from_json(optarg); + // TODO get error out of parser + if (!obj) { + error_report("invalid JSON"); + exit(1); + } + args =3D qobject_to_qdict(obj); + assert(args); + v =3D qobject_input_visitor_new(QOBJECT(args), tru= e); + } else { + args =3D keyval_parse(optarg, "driver", &error_fat= al); + v =3D qobject_input_visitor_new_autocast(QOBJECT(a= rgs)); + } + + visit_type_BlockdevOptions(v, NULL, &bdo->bdo, + &error_fatal); + visit_free(v); + QDECREF(args); + loc_save(&bdo->loc); + QSIMPLEQ_INSERT_TAIL(&bdo_queue, bdo, entry); + break; + } case QEMU_OPTION_drive: if (drive_def(optarg) =3D=3D NULL) { exit(1); @@ -4407,6 +4447,16 @@ int main(int argc, char **argv, char **envp) qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, NULL); } + while (!QSIMPLEQ_EMPTY(&bdo_queue)) { + BlockdevOptions_queue *bdo =3D QSIMPLEQ_FIRST(&bdo_queue); + + QSIMPLEQ_REMOVE_HEAD(&bdo_queue, entry); + loc_push_restore(&bdo->loc); + qmp_blockdev_add(bdo->bdo, &error_fatal); + loc_pop(&bdo->loc); + qapi_free_BlockdevOptions(bdo->bdo); + g_free(bdo); + } if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func, &machine_class->block_default_type, NULL)) { exit(1); --=20 2.7.4