aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS3
-rw-r--r--docs/qapi-code-gen.txt2
-rw-r--r--error.c3
-rw-r--r--error.h2
-rw-r--r--hw/cadence_gem.c2
-rw-r--r--hw/dp8393x.c2
-rw-r--r--hw/e1000.c2
-rw-r--r--hw/eepro100.c2
-rw-r--r--hw/etraxfs_eth.c2
-rw-r--r--hw/lan9118.c2
-rw-r--r--hw/lance.c2
-rw-r--r--hw/mcf_fec.c2
-rw-r--r--hw/milkymist-minimac2.c2
-rw-r--r--hw/mipsnet.c2
-rw-r--r--hw/musicpal.c2
-rw-r--r--hw/ne2000-isa.c2
-rw-r--r--hw/ne2000.c2
-rw-r--r--hw/opencores_eth.c2
-rw-r--r--hw/pcnet-pci.c2
-rw-r--r--hw/rtl8139.c2
-rw-r--r--hw/smc91c111.c2
-rw-r--r--hw/spapr_llan.c2
-rw-r--r--hw/stellaris_enet.c2
-rw-r--r--hw/usb/dev-network.c2
-rw-r--r--hw/vhost_net.c2
-rw-r--r--hw/virtio-net.c10
-rw-r--r--hw/xen_nic.c2
-rw-r--r--hw/xgmac.c2
-rw-r--r--hw/xilinx_axienet.c2
-rw-r--r--hw/xilinx_ethlite.c2
-rw-r--r--net.c494
-rw-r--r--net.h16
-rw-r--r--net/dump.c24
-rw-r--r--net/dump.h5
-rw-r--r--net/slirp.c96
-rw-r--r--net/slirp.h4
-rw-r--r--net/socket.c124
-rw-r--r--net/socket.h5
-rw-r--r--net/tap-aix.c2
-rw-r--r--net/tap-bsd.c2
-rw-r--r--net/tap-haiku.c2
-rw-r--r--net/tap-linux.c9
-rw-r--r--net/tap-solaris.c2
-rw-r--r--net/tap-win32.c14
-rw-r--r--net/tap.c152
-rw-r--r--net/tap.h10
-rw-r--r--net/vde.c20
-rw-r--r--net/vde.h5
-rw-r--r--qapi-schema.json288
-rw-r--r--qapi/Makefile.objs2
-rw-r--r--qapi/opts-visitor.c427
-rw-r--r--qapi/opts-visitor.h31
-rw-r--r--qapi/qapi-visit-core.c17
-rw-r--r--qapi/qapi-visit-core.h3
-rw-r--r--qemu-option-internal.h53
-rw-r--r--qemu-option.c24
-rw-r--r--scripts/qapi-visit.py150
-rw-r--r--scripts/qapi.py6
-rw-r--r--tests/test-qmp-commands.c42
-rw-r--r--tests/test-qmp-input-visitor.c24
60 files changed, 1352 insertions, 771 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 30ed56dd77..2d219d2ea0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -560,9 +560,10 @@ F: monitor.c
Network device layer
M: Anthony Liguori <aliguori@us.ibm.com>
-M: Mark McLoughlin <markmc@redhat.com>
+M: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
S: Maintained
F: net/
+T: git git://github.com/stefanha/qemu.git net
Network Block Device (NBD)
M: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index ad11767a2f..cccb11e562 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -220,6 +220,8 @@ Example:
#endif
mdroth@illuin:~/w/qemu2.git$
+(The actual structure of the visit_type_* functions is a bit more complex
+in order to propagate errors correctly and avoid leaking memory).
=== scripts/qapi-commands.py ===
diff --git a/error.c b/error.c
index a52b7710d2..58f55a012e 100644
--- a/error.c
+++ b/error.c
@@ -32,6 +32,7 @@ void error_set(Error **errp, const char *fmt, ...)
if (errp == NULL) {
return;
}
+ assert(*errp == NULL);
err = g_malloc0(sizeof(*err));
@@ -132,7 +133,7 @@ bool error_is_type(Error *err, const char *fmt)
void error_propagate(Error **dst_err, Error *local_err)
{
- if (dst_err) {
+ if (dst_err && !*dst_err) {
*dst_err = local_err;
} else if (local_err) {
error_free(local_err);
diff --git a/error.h b/error.h
index 45ff6c1ffe..3d9d96def0 100644
--- a/error.h
+++ b/error.h
@@ -57,7 +57,7 @@ void error_set_field(Error *err, const char *field, const char *value);
/**
* Propagate an error to an indirect pointer to an error. This function will
* always transfer ownership of the error reference and handles the case where
- * dst_err is NULL correctly.
+ * dst_err is NULL correctly. Errors after the first are discarded.
*/
void error_propagate(Error **dst_err, Error *local_err);
diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c
index 87143caf2d..a0f51dea80 100644
--- a/hw/cadence_gem.c
+++ b/hw/cadence_gem.c
@@ -1161,7 +1161,7 @@ static void gem_set_link(VLANClientState *nc)
}
static NetClientInfo net_gem_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = gem_can_receive,
.receive = gem_receive,
diff --git a/hw/dp8393x.c b/hw/dp8393x.c
index 017d0742ae..756d6301b0 100644
--- a/hw/dp8393x.c
+++ b/hw/dp8393x.c
@@ -872,7 +872,7 @@ static void nic_cleanup(VLANClientState *nc)
}
static NetClientInfo net_dp83932_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = nic_can_receive,
.receive = nic_receive,
diff --git a/hw/e1000.c b/hw/e1000.c
index 4573f1301e..ad242981cc 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -1206,7 +1206,7 @@ pci_e1000_uninit(PCIDevice *dev)
}
static NetClientInfo net_e1000_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = e1000_can_receive,
.receive = e1000_receive,
diff --git a/hw/eepro100.c b/hw/eepro100.c
index 6279ae36ec..f343685dbd 100644
--- a/hw/eepro100.c
+++ b/hw/eepro100.c
@@ -1845,7 +1845,7 @@ static int pci_nic_uninit(PCIDevice *pci_dev)
}
static NetClientInfo net_eepro100_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = nic_can_receive,
.receive = nic_receive,
diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c
index 16a0637a4a..45fb40ce76 100644
--- a/hw/etraxfs_eth.c
+++ b/hw/etraxfs_eth.c
@@ -579,7 +579,7 @@ static void eth_cleanup(VLANClientState *nc)
}
static NetClientInfo net_etraxfs_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = eth_can_receive,
.receive = eth_receive,
diff --git a/hw/lan9118.c b/hw/lan9118.c
index 7b4fe87fca..40fb7654f4 100644
--- a/hw/lan9118.c
+++ b/hw/lan9118.c
@@ -1310,7 +1310,7 @@ static void lan9118_cleanup(VLANClientState *nc)
}
static NetClientInfo net_lan9118_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = lan9118_can_receive,
.receive = lan9118_receive,
diff --git a/hw/lance.c b/hw/lance.c
index ce3d46c17b..91c0e16237 100644
--- a/hw/lance.c
+++ b/hw/lance.c
@@ -93,7 +93,7 @@ static void lance_cleanup(VLANClientState *nc)
}
static NetClientInfo net_lance_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = pcnet_can_receive,
.receive = pcnet_receive,
diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c
index ae37bef0f0..4ab4ff583d 100644
--- a/hw/mcf_fec.c
+++ b/hw/mcf_fec.c
@@ -450,7 +450,7 @@ static void mcf_fec_cleanup(VLANClientState *nc)
}
static NetClientInfo net_mcf_fec_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = mcf_fec_can_receive,
.receive = mcf_fec_receive,
diff --git a/hw/milkymist-minimac2.c b/hw/milkymist-minimac2.c
index 70bf336add..3924b8343d 100644
--- a/hw/milkymist-minimac2.c
+++ b/hw/milkymist-minimac2.c
@@ -448,7 +448,7 @@ static void milkymist_minimac2_reset(DeviceState *d)
}
static NetClientInfo net_milkymist_minimac2_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = minimac2_can_rx,
.receive = minimac2_rx,
diff --git a/hw/mipsnet.c b/hw/mipsnet.c
index 31072463f4..3385be7683 100644
--- a/hw/mipsnet.c
+++ b/hw/mipsnet.c
@@ -217,7 +217,7 @@ static void mipsnet_cleanup(VLANClientState *nc)
}
static NetClientInfo net_mipsnet_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = mipsnet_can_receive,
.receive = mipsnet_receive,
diff --git a/hw/musicpal.c b/hw/musicpal.c
index f14f20d689..448897f82c 100644
--- a/hw/musicpal.c
+++ b/hw/musicpal.c
@@ -374,7 +374,7 @@ static void eth_cleanup(VLANClientState *nc)
}
static NetClientInfo net_mv88w8618_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = eth_can_receive,
.receive = eth_receive,
diff --git a/hw/ne2000-isa.c b/hw/ne2000-isa.c
index a4a783ab89..99ed965eac 100644
--- a/hw/ne2000-isa.c
+++ b/hw/ne2000-isa.c
@@ -44,7 +44,7 @@ static void isa_ne2000_cleanup(VLANClientState *nc)
}
static NetClientInfo net_ne2000_isa_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = ne2000_can_receive,
.receive = ne2000_receive,
diff --git a/hw/ne2000.c b/hw/ne2000.c
index d02e60c4a6..760ed2972a 100644
--- a/hw/ne2000.c
+++ b/hw/ne2000.c
@@ -711,7 +711,7 @@ static void ne2000_cleanup(VLANClientState *nc)
}
static NetClientInfo net_ne2000_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = ne2000_can_receive,
.receive = ne2000_receive,
diff --git a/hw/opencores_eth.c b/hw/opencores_eth.c
index 350f73173a..f4498d413d 100644
--- a/hw/opencores_eth.c
+++ b/hw/opencores_eth.c
@@ -467,7 +467,7 @@ static void open_eth_cleanup(VLANClientState *nc)
}
static NetClientInfo net_open_eth_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = open_eth_can_receive,
.receive = open_eth_receive,
diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c
index 34d73aaea1..931fedd913 100644
--- a/hw/pcnet-pci.c
+++ b/hw/pcnet-pci.c
@@ -284,7 +284,7 @@ static int pci_pcnet_uninit(PCIDevice *dev)
}
static NetClientInfo net_pci_pcnet_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = pcnet_can_receive,
.receive = pcnet_receive,
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index 436b015c64..82fe235417 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -3455,7 +3455,7 @@ static int pci_rtl8139_uninit(PCIDevice *dev)
}
static NetClientInfo net_rtl8139_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = rtl8139_can_receive,
.receive = rtl8139_receive,
diff --git a/hw/smc91c111.c b/hw/smc91c111.c
index 1a5213fa56..451ede0588 100644
--- a/hw/smc91c111.c
+++ b/hw/smc91c111.c
@@ -736,7 +736,7 @@ static void smc91c111_cleanup(VLANClientState *nc)
}
static NetClientInfo net_smc91c111_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = smc91c111_can_receive,
.receive = smc91c111_receive,
diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c
index d26fe9fea3..d54f933d3a 100644
--- a/hw/spapr_llan.c
+++ b/hw/spapr_llan.c
@@ -176,7 +176,7 @@ static ssize_t spapr_vlan_receive(VLANClientState *nc, const uint8_t *buf,
}
static NetClientInfo net_spapr_vlan_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = spapr_vlan_can_receive,
.receive = spapr_vlan_receive,
diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c
index fbe99cb4a9..b593cd0ed9 100644
--- a/hw/stellaris_enet.c
+++ b/hw/stellaris_enet.c
@@ -393,7 +393,7 @@ static void stellaris_enet_cleanup(VLANClientState *nc)
}
static NetClientInfo net_stellaris_enet_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = stellaris_enet_can_receive,
.receive = stellaris_enet_receive,
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index 5d2f0982c9..f40c349fc3 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1313,7 +1313,7 @@ static void usb_net_handle_destroy(USBDevice *dev)
}
static NetClientInfo net_usbnet_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = usbnet_can_receive,
.receive = usbnet_receive,
diff --git a/hw/vhost_net.c b/hw/vhost_net.c
index f672e9dafd..75f8211046 100644
--- a/hw/vhost_net.c
+++ b/hw/vhost_net.c
@@ -83,7 +83,7 @@ void vhost_net_ack_features(struct vhost_net *net, unsigned features)
static int vhost_net_get_fd(VLANClientState *backend)
{
switch (backend->info->type) {
- case NET_CLIENT_TYPE_TAP:
+ case NET_CLIENT_OPTIONS_KIND_TAP:
return tap_get_fd(backend);
default:
fprintf(stderr, "vhost-net requires tap backend\n");
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 533aa3d0f3..df204999bc 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -108,7 +108,7 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
if (!n->nic->nc.peer) {
return;
}
- if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) {
+ if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
return;
}
@@ -205,7 +205,7 @@ static int peer_has_vnet_hdr(VirtIONet *n)
if (!n->nic->nc.peer)
return 0;
- if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP)
+ if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP)
return 0;
n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer);
@@ -249,7 +249,7 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features)
}
if (!n->nic->nc.peer ||
- n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) {
+ n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
return features;
}
if (!tap_get_vhost_net(n->nic->nc.peer)) {
@@ -288,7 +288,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
(features >> VIRTIO_NET_F_GUEST_UFO) & 1);
}
if (!n->nic->nc.peer ||
- n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) {
+ n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
return;
}
if (!tap_get_vhost_net(n->nic->nc.peer)) {
@@ -988,7 +988,7 @@ static void virtio_net_cleanup(VLANClientState *nc)
}
static NetClientInfo net_virtio_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = virtio_net_can_receive,
.receive = virtio_net_receive,
diff --git a/hw/xen_nic.c b/hw/xen_nic.c
index 98db9bb8f6..593a572119 100644
--- a/hw/xen_nic.c
+++ b/hw/xen_nic.c
@@ -301,7 +301,7 @@ static ssize_t net_rx_packet(VLANClientState *nc, const uint8_t *buf, size_t siz
/* ------------------------------------------------------------- */
static NetClientInfo net_xen_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = net_rx_ok,
.receive = net_rx_packet,
diff --git a/hw/xgmac.c b/hw/xgmac.c
index dd4bdc46f5..e539681d83 100644
--- a/hw/xgmac.c
+++ b/hw/xgmac.c
@@ -371,7 +371,7 @@ static void eth_cleanup(VLANClientState *nc)
}
static NetClientInfo net_xgmac_enet_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = eth_can_rx,
.receive = eth_rx,
diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c
index 2e8d8a59ba..e948505849 100644
--- a/hw/xilinx_axienet.c
+++ b/hw/xilinx_axienet.c
@@ -832,7 +832,7 @@ axienet_stream_push(void *opaque, uint8_t *buf, size_t size, uint32_t *hdr)
}
static NetClientInfo net_xilinx_enet_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = eth_can_rx,
.receive = eth_rx,
diff --git a/hw/xilinx_ethlite.c b/hw/xilinx_ethlite.c
index affbb8bfff..9006322855 100644
--- a/hw/xilinx_ethlite.c
+++ b/hw/xilinx_ethlite.c
@@ -202,7 +202,7 @@ static void eth_cleanup(VLANClientState *nc)
}
static NetClientInfo net_xilinx_ethlite_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = eth_can_rx,
.receive = eth_rx,
diff --git a/net.c b/net.c
index abf0fd0a0d..dbca77bad1 100644
--- a/net.c
+++ b/net.c
@@ -37,6 +37,9 @@
#include "qmp-commands.h"
#include "hw/qdev.h"
#include "iov.h"
+#include "qapi-visit.h"
+#include "qapi/opts-visitor.h"
+#include "qapi/qapi-dealloc-visitor.h"
/* Net bridge is currently not supported for W32. */
#if !defined(_WIN32)
@@ -239,7 +242,7 @@ NICState *qemu_new_nic(NetClientInfo *info,
VLANClientState *nc;
NICState *nic;
- assert(info->type == NET_CLIENT_TYPE_NIC);
+ assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC);
assert(info->size >= sizeof(NICState));
nc = qemu_new_net_client(info, conf->vlan, conf->peer, model, name);
@@ -282,7 +285,7 @@ static void qemu_free_vlan_client(VLANClientState *vc)
void qemu_del_vlan_client(VLANClientState *vc)
{
/* If there is a peer NIC, delete and cleanup client, but do not free. */
- if (!vc->vlan && vc->peer && vc->peer->info->type == NET_CLIENT_TYPE_NIC) {
+ if (!vc->vlan && vc->peer && vc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
NICState *nic = DO_UPCAST(NICState, nc, vc->peer);
if (nic->peer_deleted) {
return;
@@ -298,7 +301,7 @@ void qemu_del_vlan_client(VLANClientState *vc)
}
/* If this is a peer NIC and peer has already been deleted, free it now. */
- if (!vc->vlan && vc->peer && vc->info->type == NET_CLIENT_TYPE_NIC) {
+ if (!vc->vlan && vc->peer && vc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
NICState *nic = DO_UPCAST(NICState, nc, vc);
if (nic->peer_deleted) {
qemu_free_vlan_client(vc->peer);
@@ -341,14 +344,14 @@ void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
VLANState *vlan;
QTAILQ_FOREACH(nc, &non_vlan_clients, next) {
- if (nc->info->type == NET_CLIENT_TYPE_NIC) {
+ if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
func(DO_UPCAST(NICState, nc, nc), opaque);
}
}
QTAILQ_FOREACH(vlan, &vlans, next) {
QTAILQ_FOREACH(nc, &vlan->clients, next) {
- if (nc->info->type == NET_CLIENT_TYPE_NIC) {
+ if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
func(DO_UPCAST(NICState, nc, nc), opaque);
}
}
@@ -664,7 +667,7 @@ VLANClientState *qemu_find_netdev(const char *id)
VLANClientState *vc;
QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
- if (vc->info->type == NET_CLIENT_TYPE_NIC)
+ if (vc->info->type == NET_CLIENT_OPTIONS_KIND_NIC)
continue;
if (!strcmp(vc->name, id)) {
return vc;
@@ -745,11 +748,15 @@ int net_handle_fd_param(Monitor *mon, const char *param)
return fd;
}
-static int net_init_nic(QemuOpts *opts, const char *name, VLANState *vlan)
+static int net_init_nic(const NetClientOptions *opts, const char *name,
+ VLANState *vlan)
{
int idx;
NICInfo *nd;
- const char *netdev;
+ const NetLegacyNicOptions *nic;
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_NIC);
+ nic = opts->nic;
idx = nic_get_free_idx();
if (idx == -1 || nb_nics >= MAX_NICS) {
@@ -761,10 +768,10 @@ static int net_init_nic(QemuOpts *opts, const char *name, VLANState *vlan)
memset(nd, 0, sizeof(*nd));
- if ((netdev = qemu_opt_get(opts, "netdev"))) {
- nd->netdev = qemu_find_netdev(netdev);
+ if (nic->has_netdev) {
+ nd->netdev = qemu_find_netdev(nic->netdev);
if (!nd->netdev) {
- error_report("netdev '%s' not found", netdev);
+ error_report("netdev '%s' not found", nic->netdev);
return -1;
}
} else {
@@ -774,26 +781,28 @@ static int net_init_nic(QemuOpts *opts, const char *name, VLANState *vlan)
if (name) {
nd->name = g_strdup(name);
}
- if (qemu_opt_get(opts, "model")) {
- nd->model = g_strdup(qemu_opt_get(opts, "model"));
+ if (nic->has_model) {
+ nd->model = g_strdup(nic->model);
}
- if (qemu_opt_get(opts, "addr")) {
- nd->devaddr = g_strdup(qemu_opt_get(opts, "addr"));
+ if (nic->has_addr) {
+ nd->devaddr = g_strdup(nic->addr);
}
- if (qemu_opt_get(opts, "macaddr") &&
- net_parse_macaddr(nd->macaddr.a, qemu_opt_get(opts, "macaddr")) < 0) {
+ if (nic->has_macaddr &&
+ net_parse_macaddr(nd->macaddr.a, nic->macaddr) < 0) {
error_report("invalid syntax for ethernet address");
return -1;
}
qemu_macaddr_default_if_unset(&nd->macaddr);
- nd->nvectors = qemu_opt_get_number(opts, "vectors",
- DEV_NVECTORS_UNSPECIFIED);
- if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
- (nd->nvectors < 0 || nd->nvectors > 0x7ffffff)) {
- error_report("invalid # of vectors: %d", nd->nvectors);
- return -1;
+ if (nic->has_vectors) {
+ if (nic->vectors > 0x7ffffff) {
+ error_report("invalid # of vectors: %"PRIu32, nic->vectors);
+ return -1;
+ }
+ nd->nvectors = nic->vectors;
+ } else {
+ nd->nvectors = DEV_NVECTORS_UNSPECIFIED;
}
nd->used = 1;
@@ -802,371 +811,128 @@ static int net_init_nic(QemuOpts *opts, const char *name, VLANState *vlan)
return idx;
}
-#define NET_COMMON_PARAMS_DESC \
- { \
- .name = "type", \
- .type = QEMU_OPT_STRING, \
- .help = "net client type (nic, tap etc.)", \
- }, { \
- .name = "vlan", \
- .type = QEMU_OPT_NUMBER, \
- .help = "vlan number", \
- }, { \
- .name = "name", \
- .type = QEMU_OPT_STRING, \
- .help = "identifier for monitor commands", \
- }
-
-typedef int (*net_client_init_func)(QemuOpts *opts,
- const char *name,
- VLANState *vlan);
-
-/* magic number, but compiler will warn if too small */
-#define NET_MAX_DESC 20
-
-static const struct {
- const char *type;
- net_client_init_func init;
- QemuOptDesc desc[NET_MAX_DESC];
-} net_client_types[NET_CLIENT_TYPE_MAX] = {
- [NET_CLIENT_TYPE_NONE] = {
- .type = "none",
- .desc = {
- NET_COMMON_PARAMS_DESC,
- { /* end of list */ }
- },
- },
- [NET_CLIENT_TYPE_NIC] = {
- .type = "nic",
- .init = net_init_nic,
- .desc = {
- NET_COMMON_PARAMS_DESC,
- {
- .name = "netdev",
- .type = QEMU_OPT_STRING,
- .help = "id of -netdev to connect to",
- },
- {
- .name = "macaddr",
- .type = QEMU_OPT_STRING,
- .help = "MAC address",
- }, {
- .name = "model",
- .type = QEMU_OPT_STRING,
- .help = "device model (e1000, rtl8139, virtio etc.)",
- }, {
- .name = "addr",
- .type = QEMU_OPT_STRING,
- .help = "PCI device address",
- }, {
- .name = "vectors",
- .type = QEMU_OPT_NUMBER,
- .help = "number of MSI-x vectors, 0 to disable MSI-X",
- },
- { /* end of list */ }
- },
- },
+
+static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])(
+ const NetClientOptions *opts,
+ const char *name,
+ VLANState *vlan) = {
+ [NET_CLIENT_OPTIONS_KIND_NIC] = net_init_nic,
#ifdef CONFIG_SLIRP
- [NET_CLIENT_TYPE_USER] = {
- .type = "user",
- .init = net_init_slirp,
- .desc = {
- NET_COMMON_PARAMS_DESC,
- {
- .name = "hostname",
- .type = QEMU_OPT_STRING,
- .help = "client hostname reported by the builtin DHCP server",
- }, {
- .name = "restrict",
- .type = QEMU_OPT_STRING,
- .help = "isolate the guest from the host (y|yes|n|no)",
- }, {
- .name = "ip",
- .type = QEMU_OPT_STRING,
- .help = "legacy parameter, use net= instead",
- }, {
- .name = "net",
- .type = QEMU_OPT_STRING,
- .help = "IP address and optional netmask",
- }, {
- .name = "host",
- .type = QEMU_OPT_STRING,
- .help = "guest-visible address of the host",
- }, {
- .name = "tftp",
- .type = QEMU_OPT_STRING,
- .help = "root directory of the built-in TFTP server",
- }, {
- .name = "bootfile",
- .type = QEMU_OPT_STRING,
- .help = "BOOTP filename, for use with tftp=",
- }, {
- .name = "dhcpstart",
- .type = QEMU_OPT_STRING,
- .help = "the first of the 16 IPs the built-in DHCP server can assign",
- }, {
- .name = "dns",
- .type = QEMU_OPT_STRING,
- .help = "guest-visible address of the virtual nameserver",
- }, {
- .name = "smb",
- .type = QEMU_OPT_STRING,
- .help = "root directory of the built-in SMB server",
- }, {
- .name = "smbserver",
- .type = QEMU_OPT_STRING,
- .help = "IP address of the built-in SMB server",
- }, {
- .name = "hostfwd",
- .type = QEMU_OPT_STRING,
- .help = "guest port number to forward incoming TCP or UDP connections",
- }, {
- .name = "guestfwd",
- .type = QEMU_OPT_STRING,
- .help = "IP address and port to forward guest TCP connections",
- },
- { /* end of list */ }
- },
- },
+ [NET_CLIENT_OPTIONS_KIND_USER] = net_init_slirp,
#endif
- [NET_CLIENT_TYPE_TAP] = {
- .type = "tap",
- .init = net_init_tap,
- .desc = {
- NET_COMMON_PARAMS_DESC,
- {
- .name = "ifname",
- .type = QEMU_OPT_STRING,
- .help = "interface name",
- },
-#ifndef _WIN32
- {
- .name = "fd",
- .type = QEMU_OPT_STRING,
- .help = "file descriptor of an already opened tap",
- }, {
- .name = "script",
- .type = QEMU_OPT_STRING,
- .help = "script to initialize the interface",
- }, {
- .name = "downscript",
- .type = QEMU_OPT_STRING,
- .help = "script to shut down the interface",
- }, {
-#ifdef CONFIG_NET_BRIDGE
- .name = "helper",
- .type = QEMU_OPT_STRING,
- .help = "command to execute to configure bridge",
- }, {
-#endif
- .name = "sndbuf",
- .type = QEMU_OPT_SIZE,
- .help = "send buffer limit"
- }, {
- .name = "vnet_hdr",
- .type = QEMU_OPT_BOOL,
- .help = "enable the IFF_VNET_HDR flag on the tap interface"
- }, {
- .name = "vhost",
- .type = QEMU_OPT_BOOL,
- .help = "enable vhost-net network accelerator",
- }, {
- .name = "vhostfd",
- .type = QEMU_OPT_STRING,
- .help = "file descriptor of an already opened vhost net device",
- }, {
- .name = "vhostforce",
- .type = QEMU_OPT_BOOL,
- .help = "force vhost on for non-MSIX virtio guests",
- },
-#endif /* _WIN32 */
- { /* end of list */ }
- },
- },
- [NET_CLIENT_TYPE_SOCKET] = {
- .type = "socket",
- .init = net_init_socket,
- .desc = {
- NET_COMMON_PARAMS_DESC,
- {
- .name = "fd",
- .type = QEMU_OPT_STRING,
- .help = "file descriptor of an already opened socket",
- }, {
- .name = "listen",
- .type = QEMU_OPT_STRING,
- .help = "port number, and optional hostname, to listen on",
- }, {
- .name = "connect",
- .type = QEMU_OPT_STRING,
- .help = "port number, and optional hostname, to connect to",
- }, {
- .name = "mcast",
- .type = QEMU_OPT_STRING,
- .help = "UDP multicast address and port number",
- }, {
- .name = "localaddr",
- .type = QEMU_OPT_STRING,
- .help = "source address and port for multicast and udp packets",
- }, {
- .name = "udp",
- .type = QEMU_OPT_STRING,
- .help = "UDP unicast address and port number",
- },
- { /* end of list */ }
- },
- },
+ [NET_CLIENT_OPTIONS_KIND_TAP] = net_init_tap,
+ [NET_CLIENT_OPTIONS_KIND_SOCKET] = net_init_socket,
#ifdef CONFIG_VDE
- [NET_CLIENT_TYPE_VDE] = {
- .type = "vde",
- .init = net_init_vde,
- .desc = {
- NET_COMMON_PARAMS_DESC,
- {
- .name = "sock",
- .type = QEMU_OPT_STRING,
- .help = "socket path",
- }, {
- .name = "port",
- .type = QEMU_OPT_NUMBER,
- .help = "port number",
- }, {
- .name = "group",
- .type = QEMU_OPT_STRING,
- .help = "group owner of socket",
- }, {
- .name = "mode",
- .type = QEMU_OPT_NUMBER,
- .help = "permissions for socket",
- },
- { /* end of list */ }
- },
- },
+ [NET_CLIENT_OPTIONS_KIND_VDE] = net_init_vde,
#endif
- [NET_CLIENT_TYPE_DUMP] = {
- .type = "dump",
- .init = net_init_dump,
- .desc = {
- NET_COMMON_PARAMS_DESC,
- {
- .name = "len",
- .type = QEMU_OPT_SIZE,
- .help = "per-packet size limit (64k default)",
- }, {
- .name = "file",
- .type = QEMU_OPT_STRING,
- .help = "dump file path (default is qemu-vlan0.pcap)",
- },
- { /* end of list */ }
- },
- },
+ [NET_CLIENT_OPTIONS_KIND_DUMP] = net_init_dump,
#ifdef CONFIG_NET_BRIDGE
- [NET_CLIENT_TYPE_BRIDGE] = {
- .type = "bridge",
- .init = net_init_bridge,
- .desc = {
- NET_COMMON_PARAMS_DESC,
- {
- .name = "br",
- .type = QEMU_OPT_STRING,
- .help = "bridge name",
- }, {
- .name = "helper",
- .type = QEMU_OPT_STRING,
- .help = "command to execute to configure bridge",
- },
- { /* end of list */ }
- },
- },
-#endif /* CONFIG_NET_BRIDGE */
+ [NET_CLIENT_OPTIONS_KIND_BRIDGE] = net_init_bridge,
+#endif
};
-int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
+
+static int net_client_init1(const void *object, int is_netdev, Error **errp)
{
+ union {
+ const Netdev *netdev;
+ const NetLegacy *net;
+ } u;
+ const NetClientOptions *opts;
const char *name;
- const char *type;
- int i;
-
- type = qemu_opt_get(opts, "type");
- if (!type) {
- error_set(errp, QERR_MISSING_PARAMETER, "type");
- return -1;
- }
if (is_netdev) {
- if (strcmp(type, "tap") != 0 &&
-#ifdef CONFIG_NET_BRIDGE
- strcmp(type, "bridge") != 0 &&
-#endif
+ u.netdev = object;
+ opts = u.netdev->opts;
+ name = u.netdev->id;
+
+ switch (opts->kind) {
#ifdef CONFIG_SLIRP
- strcmp(type, "user") != 0 &&
+ case NET_CLIENT_OPTIONS_KIND_USER:
#endif
+ case NET_CLIENT_OPTIONS_KIND_TAP:
+ case NET_CLIENT_OPTIONS_KIND_SOCKET:
#ifdef CONFIG_VDE
- strcmp(type, "vde") != 0 &&
+ case NET_CLIENT_OPTIONS_KIND_VDE:
+#endif
+#ifdef CONFIG_NET_BRIDGE
+ case NET_CLIENT_OPTIONS_KIND_BRIDGE:
#endif
- strcmp(type, "socket") != 0) {
+ break;
+
+ default:
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
"a netdev backend type");
return -1;
}
+ } else {
+ u.net = object;
+ opts = u.net->opts;
+ /* missing optional values have been initialized to "all bits zero" */
+ name = u.net->has_id ? u.net->id : u.net->name;
+ }
- if (qemu_opt_get(opts, "vlan")) {
- error_set(errp, QERR_INVALID_PARAMETER, "vlan");
- return -1;
- }
- if (qemu_opt_get(opts, "name")) {
- error_set(errp, QERR_INVALID_PARAMETER, "name");
- return -1;
+ if (net_client_init_fun[opts->kind]) {
+ VLANState *vlan = NULL;
+
+ /* Do not add to a vlan if it's a -netdev or a nic with a netdev=
+ * parameter. */
+ if (!is_netdev &&
+ (opts->kind != NET_CLIENT_OPTIONS_KIND_NIC ||
+ !opts->nic->has_netdev)) {
+ vlan = qemu_find_vlan(u.net->has_vlan ? u.net->vlan : 0, true);
}
- if (!qemu_opts_id(opts)) {
- error_set(errp, QERR_MISSING_PARAMETER, "id");
+
+ if (net_client_init_fun[opts->kind](opts, name, vlan) < 0) {
+ /* TODO push error reporting into init() methods */
+ error_set(errp, QERR_DEVICE_INIT_FAILED,
+ NetClientOptionsKind_lookup[opts->kind]);
return -1;
}
}
+ return 0;
+}
+
- name = qemu_opts_id(opts);
- if (!name) {
- name = qemu_opt_get(opts, "name");
+static void net_visit(Visitor *v, int is_netdev, void **object, Error **errp)
+{
+ if (is_netdev) {
+ visit_type_Netdev(v, (Netdev **)object, NULL, errp);
+ } else {
+ visit_type_NetLegacy(v, (NetLegacy **)object, NULL, errp);
}
+}
- for (i = 0; i < NET_CLIENT_TYPE_MAX; i++) {
- if (net_client_types[i].type != NULL &&
- !strcmp(net_client_types[i].type, type)) {
- Error *local_err = NULL;
- VLANState *vlan = NULL;
- int ret;
- qemu_opts_validate(opts, &net_client_types[i].desc[0], &local_err);
- if (error_is_set(&local_err)) {
- error_propagate(errp, local_err);
- return -1;
- }
+int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
+{
+ void *object = NULL;
+ Error *err = NULL;
+ int ret = -1;
- /* Do not add to a vlan if it's a -netdev or a nic with a
- * netdev= parameter. */
- if (!(is_netdev ||
- (strcmp(type, "nic") == 0 && qemu_opt_get(opts, "netdev")))) {
- vlan = qemu_find_vlan(qemu_opt_get_number(opts, "vlan", 0), 1);
- }
+ {
+ OptsVisitor *ov = opts_visitor_new(opts);
- ret = 0;
- if (net_client_types[i].init) {
- ret = net_client_types[i].init(opts, name, vlan);
- if (ret < 0) {
- /* TODO push error reporting into init() methods */
- error_set(errp, QERR_DEVICE_INIT_FAILED, type);
- return -1;
- }
- }
- return ret;
- }
+ net_visit(opts_get_visitor(ov), is_netdev, &object, &err);
+ opts_visitor_cleanup(ov);
}
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
- "a network client type");
- return -1;
+ if (!err) {
+ ret = net_client_init1(object, is_netdev, &err);
+ }
+
+ if (object) {
+ QapiDeallocVisitor *dv = qapi_dealloc_visitor_new();
+
+ net_visit(qapi_dealloc_get_visitor(dv), is_netdev, &object, NULL);
+ qapi_dealloc_visitor_cleanup(dv);
+ }
+
+ error_propagate(errp, err);
+ return ret;
}
+
static int net_host_check_device(const char *device)
{
int i;
@@ -1286,14 +1052,14 @@ void qmp_netdev_del(const char *id, Error **errp)
static void print_net_client(Monitor *mon, VLANClientState *vc)
{
monitor_printf(mon, "%s: type=%s,%s\n", vc->name,
- net_client_types[vc->info->type].type, vc->info_str);
+ NetClientOptionsKind_lookup[vc->info->type], vc->info_str);
}
void do_info_network(Monitor *mon)
{
VLANState *vlan;
VLANClientState *vc, *peer;
- net_client_type type;
+ NetClientOptionsKind type;
QTAILQ_FOREACH(vlan, &vlans, next) {
monitor_printf(mon, "VLAN %d devices:\n", vlan->id);
@@ -1307,11 +1073,11 @@ void do_info_network(Monitor *mon)
QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
peer = vc->peer;
type = vc->info->type;
- if (!peer || type == NET_CLIENT_TYPE_NIC) {
+ if (!peer || type == NET_CLIENT_OPTIONS_KIND_NIC) {
monitor_printf(mon, " ");
print_net_client(mon, vc);
} /* else it's a netdev connected to a NIC, printed with the NIC */
- if (peer && type == NET_CLIENT_TYPE_NIC) {
+ if (peer && type == NET_CLIENT_OPTIONS_KIND_NIC) {
monitor_printf(mon, " \\ ");
print_net_client(mon, peer);
}
@@ -1399,13 +1165,13 @@ void net_check_clients(void)
QTAILQ_FOREACH(vc, &vlan->clients, next) {
switch (vc->info->type) {
- case NET_CLIENT_TYPE_NIC:
+ case NET_CLIENT_OPTIONS_KIND_NIC:
has_nic = 1;
break;
- case NET_CLIENT_TYPE_USER:
- case NET_CLIENT_TYPE_TAP:
- case NET_CLIENT_TYPE_SOCKET:
- case NET_CLIENT_TYPE_VDE:
+ case NET_CLIENT_OPTIONS_KIND_USER:
+ case NET_CLIENT_OPTIONS_KIND_TAP:
+ case NET_CLIENT_OPTIONS_KIND_SOCKET:
+ case NET_CLIENT_OPTIONS_KIND_VDE:
has_host_dev = 1;
break;
default: ;
@@ -1421,7 +1187,7 @@ void net_check_clients(void)
QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
if (!vc->peer) {
fprintf(stderr, "Warning: %s %s has no peer\n",
- vc->info->type == NET_CLIENT_TYPE_NIC ? "nic" : "netdev",
+ vc->info->type == NET_CLIENT_OPTIONS_KIND_NIC ? "nic" : "netdev",
vc->name);
}
}
diff --git a/net.h b/net.h
index bdc2a0602d..b0b8c7ab6b 100644
--- a/net.h
+++ b/net.h
@@ -7,6 +7,7 @@
#include "qemu-option.h"
#include "net/queue.h"
#include "vmstate.h"
+#include "qapi-types.h"
struct MACAddr {
uint8_t a[6];
@@ -29,19 +30,6 @@ typedef struct NICConf {
/* VLANs support */
-typedef enum {
- NET_CLIENT_TYPE_NONE,
- NET_CLIENT_TYPE_NIC,
- NET_CLIENT_TYPE_USER,
- NET_CLIENT_TYPE_TAP,
- NET_CLIENT_TYPE_SOCKET,
- NET_CLIENT_TYPE_VDE,
- NET_CLIENT_TYPE_DUMP,
- NET_CLIENT_TYPE_BRIDGE,
-
- NET_CLIENT_TYPE_MAX
-} net_client_type;
-
typedef void (NetPoll)(VLANClientState *, bool enable);
typedef int (NetCanReceive)(VLANClientState *);
typedef ssize_t (NetReceive)(VLANClientState *, const uint8_t *, size_t);
@@ -50,7 +38,7 @@ typedef void (NetCleanup) (VLANClientState *);
typedef void (LinkStatusChanged)(VLANClientState *);
typedef struct NetClientInfo {
- net_client_type type;
+ NetClientOptionsKind type;
size_t size;
NetReceive *receive;
NetReceive *receive_raw;
diff --git a/net/dump.c b/net/dump.c
index f835c51187..b575430787 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -93,7 +93,7 @@ static void dump_cleanup(VLANClientState *nc)
}
static NetClientInfo net_dump_info = {
- .type = NET_CLIENT_TYPE_DUMP,
+ .type = NET_CLIENT_OPTIONS_KIND_DUMP,
.size = sizeof(DumpState),
.receive = dump_receive,
.cleanup = dump_cleanup,
@@ -144,21 +144,35 @@ static int net_dump_init(VLANState *vlan, const char *device,
return 0;
}
-int net_init_dump(QemuOpts *opts, const char *name, VLANState *vlan)
+int net_init_dump(const NetClientOptions *opts, const char *name,
+ VLANState *vlan)
{
int len;
const char *file;
char def_file[128];
+ const NetdevDumpOptions *dump;
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP);
+ dump = opts->dump;
assert(vlan);
- file = qemu_opt_get(opts, "file");
- if (!file) {
+ if (dump->has_file) {
+ file = dump->file;
+ } else {
snprintf(def_file, sizeof(def_file), "qemu-vlan%d.pcap", vlan->id);
file = def_file;
}
- len = qemu_opt_get_size(opts, "len", 65536);
+ if (dump->has_len) {
+ if (dump->len > INT_MAX) {
+ error_report("invalid length: %"PRIu64, dump->len);
+ return -1;
+ }
+ len = dump->len;
+ } else {
+ len = 65536;
+ }
return net_dump_init(vlan, "dump", name, file, len);
}
diff --git a/net/dump.h b/net/dump.h
index 2b5d9ba644..0fa2dd72ca 100644
--- a/net/dump.h
+++ b/net/dump.h
@@ -25,8 +25,9 @@
#define QEMU_NET_DUMP_H
#include "net.h"
-#include "qemu-common.h"
+#include "qapi-types.h"
-int net_init_dump(QemuOpts *opts, const char *name, VLANState *vlan);
+int net_init_dump(const NetClientOptions *opts, const char *name,
+ VLANState *vlan);
#endif /* QEMU_NET_DUMP_H */
diff --git a/net/slirp.c b/net/slirp.c
index b82eab0a07..5c2e6b2cec 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -129,7 +129,7 @@ static void net_slirp_cleanup(VLANClientState *nc)
}
static NetClientInfo net_slirp_info = {
- .type = NET_CLIENT_TYPE_USER,
+ .type = NET_CLIENT_OPTIONS_KIND_USER,
.size = sizeof(SlirpState),
.receive = net_slirp_receive,
.cleanup = net_slirp_cleanup,
@@ -686,88 +686,46 @@ void do_info_usernet(Monitor *mon)
}
}
-static int net_init_slirp_configs(const char *name, const char *value, void *opaque)
+static void
+net_init_slirp_configs(const StringList *fwd, int flags)
{
- struct slirp_config_str *config;
-
- if (strcmp(name, "hostfwd") != 0 && strcmp(name, "guestfwd") != 0) {
- return 0;
- }
-
- config = g_malloc0(sizeof(*config));
+ while (fwd) {
+ struct slirp_config_str *config;
- pstrcpy(config->str, sizeof(config->str), value);
+ config = g_malloc0(sizeof(*config));
+ pstrcpy(config->str, sizeof(config->str), fwd->value->str);
+ config->flags = flags;
+ config->next = slirp_configs;
+ slirp_configs = config;
- if (!strcmp(name, "hostfwd")) {
- config->flags = SLIRP_CFG_HOSTFWD;
+ fwd = fwd->next;
}
-
- config->next = slirp_configs;
- slirp_configs = config;
-
- return 0;
}
-int net_init_slirp(QemuOpts *opts, const char *name, VLANState *vlan)
+int net_init_slirp(const NetClientOptions *opts, const char *name,
+ VLANState *vlan)
{
struct slirp_config_str *config;
- const char *vhost;
- const char *vhostname;
- const char *vdhcp_start;
- const char *vnamesrv;
- const char *tftp_export;
- const char *bootfile;
- const char *smb_export;
- const char *vsmbsrv;
- const char *restrict_opt;
- char *vnet = NULL;
- int restricted = 0;
+ char *vnet;
int ret;
+ const NetdevUserOptions *user;
- vhost = qemu_opt_get(opts, "host");
- vhostname = qemu_opt_get(opts, "hostname");
- vdhcp_start = qemu_opt_get(opts, "dhcpstart");
- vnamesrv = qemu_opt_get(opts, "dns");
- tftp_export = qemu_opt_get(opts, "tftp");
- bootfile = qemu_opt_get(opts, "bootfile");
- smb_export = qemu_opt_get(opts, "smb");
- vsmbsrv = qemu_opt_get(opts, "smbserver");
-
- restrict_opt = qemu_opt_get(opts, "restrict");
- if (restrict_opt) {
- if (!strcmp(restrict_opt, "on") ||
- !strcmp(restrict_opt, "yes") || !strcmp(restrict_opt, "y")) {
- restricted = 1;
- } else if (strcmp(restrict_opt, "off") &&
- strcmp(restrict_opt, "no") && strcmp(restrict_opt, "n")) {
- error_report("invalid option: 'restrict=%s'", restrict_opt);
- return -1;
- }
- }
-
- if (qemu_opt_get(opts, "ip")) {
- const char *ip = qemu_opt_get(opts, "ip");
- int l = strlen(ip) + strlen("/24") + 1;
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_USER);
+ user = opts->user;
- vnet = g_malloc(l);
+ vnet = user->has_net ? g_strdup(user->net) :
+ user->has_ip ? g_strdup_printf("%s/24", user->ip) :
+ NULL;
- /* emulate legacy ip= parameter */
- pstrcpy(vnet, l, ip);
- pstrcat(vnet, l, "/24");
- }
-
- if (qemu_opt_get(opts, "net")) {
- if (vnet) {
- g_free(vnet);
- }
- vnet = g_strdup(qemu_opt_get(opts, "net"));
- }
+ /* all optional fields are initialized to "all bits zero" */
- qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 0);
+ net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD);
+ net_init_slirp_configs(user->guestfwd, 0);
- ret = net_slirp_init(vlan, "user", name, restricted, vnet, vhost,
- vhostname, tftp_export, bootfile, vdhcp_start,
- vnamesrv, smb_export, vsmbsrv);
+ ret = net_slirp_init(vlan, "user", name, user->restrict, vnet, user->host,
+ user->hostname, user->tftp, user->bootfile,
+ user->dhcpstart, user->dns, user->smb,
+ user->smbserver);
while (slirp_configs) {
config = slirp_configs;
diff --git a/net/slirp.h b/net/slirp.h
index 53fe95dc12..e2c71eeca0 100644
--- a/net/slirp.h
+++ b/net/slirp.h
@@ -27,10 +27,12 @@
#include "qemu-common.h"
#include "qdict.h"
#include "qemu-option.h"
+#include "qapi-types.h"
#ifdef CONFIG_SLIRP
-int net_init_slirp(QemuOpts *opts, const char *name, VLANState *vlan);
+int net_init_slirp(const NetClientOptions *opts, const char *name,
+ VLANState *vlan);
void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict);
void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict);
diff --git a/net/socket.c b/net/socket.c
index fcd0a3c162..600c287d79 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -239,7 +239,7 @@ static void net_socket_cleanup(VLANClientState *nc)
}
static NetClientInfo net_dgram_socket_info = {
- .type = NET_CLIENT_TYPE_SOCKET,
+ .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
.size = sizeof(NetSocketState),
.receive = net_socket_receive_dgram,
.cleanup = net_socket_cleanup,
@@ -317,7 +317,7 @@ static void net_socket_connect(void *opaque)
}
static NetClientInfo net_socket_info = {
- .type = NET_CLIENT_TYPE_SOCKET,
+ .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
.size = sizeof(NetSocketState),
.receive = net_socket_receive,
.cleanup = net_socket_cleanup,
@@ -586,100 +586,68 @@ static int net_socket_udp_init(VLANState *vlan,
return 0;
}
-int net_init_socket(QemuOpts *opts, const char *name, VLANState *vlan)
+int net_init_socket(const NetClientOptions *opts, const char *name,
+ VLANState *vlan)
{
- if (qemu_opt_get(opts, "fd")) {
- int fd;
+ const NetdevSocketOptions *sock;
- if (qemu_opt_get(opts, "listen") ||
- qemu_opt_get(opts, "connect") ||
- qemu_opt_get(opts, "mcast") ||
- qemu_opt_get(opts, "localaddr")) {
- error_report("listen=, connect=, mcast= and localaddr= is invalid with fd=");
- return -1;
- }
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_SOCKET);
+ sock = opts->socket;
- fd = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "fd"));
- if (fd == -1) {
- return -1;
- }
+ if (sock->has_fd + sock->has_listen + sock->has_connect + sock->has_mcast +
+ sock->has_udp != 1) {
+ error_report("exactly one of fd=, listen=, connect=, mcast= or udp="
+ " is required");
+ return -1;
+ }
- if (!net_socket_fd_init(vlan, "socket", name, fd, 1)) {
- return -1;
- }
- } else if (qemu_opt_get(opts, "listen")) {
- const char *listen;
-
- if (qemu_opt_get(opts, "fd") ||
- qemu_opt_get(opts, "connect") ||
- qemu_opt_get(opts, "mcast") ||
- qemu_opt_get(opts, "localaddr")) {
- error_report("fd=, connect=, mcast= and localaddr= is invalid with listen=");
- return -1;
- }
+ if (sock->has_localaddr && !sock->has_mcast && !sock->has_udp) {
+ error_report("localaddr= is only valid with mcast= or udp=");
+ return -1;
+ }
- listen = qemu_opt_get(opts, "listen");
+ if (sock->has_fd) {
+ int fd;
- if (net_socket_listen_init(vlan, "socket", name, listen) == -1) {
- return -1;
- }
- } else if (qemu_opt_get(opts, "connect")) {
- const char *connect;
-
- if (qemu_opt_get(opts, "fd") ||
- qemu_opt_get(opts, "listen") ||
- qemu_opt_get(opts, "mcast") ||
- qemu_opt_get(opts, "localaddr")) {
- error_report("fd=, listen=, mcast= and localaddr= is invalid with connect=");
+ fd = net_handle_fd_param(cur_mon, sock->fd);
+ if (fd == -1 || !net_socket_fd_init(vlan, "socket", name, fd, 1)) {
return -1;
}
+ return 0;
+ }
- connect = qemu_opt_get(opts, "connect");
-
- if (net_socket_connect_init(vlan, "socket", name, connect) == -1) {
+ if (sock->has_listen) {
+ if (net_socket_listen_init(vlan, "socket", name, sock->listen) == -1) {
return -1;
}
- } else if (qemu_opt_get(opts, "mcast")) {
- const char *mcast, *localaddr;
+ return 0;
+ }
- if (qemu_opt_get(opts, "fd") ||
- qemu_opt_get(opts, "connect") ||
- qemu_opt_get(opts, "listen")) {
- error_report("fd=, connect= and listen= is invalid with mcast=");
+ if (sock->has_connect) {
+ if (net_socket_connect_init(vlan, "socket", name, sock->connect) ==
+ -1) {
return -1;
}
+ return 0;
+ }
- mcast = qemu_opt_get(opts, "mcast");
- localaddr = qemu_opt_get(opts, "localaddr");
-
- if (net_socket_mcast_init(vlan, "socket", name, mcast, localaddr) == -1) {
- return -1;
- }
- } else if (qemu_opt_get(opts, "udp")) {
- const char *udp, *localaddr;
-
- if (qemu_opt_get(opts, "fd") ||
- qemu_opt_get(opts, "connect") ||
- qemu_opt_get(opts, "listen") ||
- qemu_opt_get(opts, "mcast")) {
- error_report("fd=, connect=, listen="
- " and mcast= is invalid with udp=");
+ if (sock->has_mcast) {
+ /* if sock->localaddr is missing, it has been initialized to "all bits
+ * zero" */
+ if (net_socket_mcast_init(vlan, "socket", name, sock->mcast,
+ sock->localaddr) == -1) {
return -1;
}
+ return 0;
+ }
- udp = qemu_opt_get(opts, "udp");
- localaddr = qemu_opt_get(opts, "localaddr");
- if (localaddr == NULL) {
- error_report("localaddr= is mandatory with udp=");
- return -1;
- }
-
- if (net_socket_udp_init(vlan, "udp", name, udp, localaddr) == -1) {
- return -1;
- }
- } else {
- error_report("-socket requires fd=, listen=,"
- " connect=, mcast= or udp=");
+ assert(sock->has_udp);
+ if (!sock->has_localaddr) {
+ error_report("localaddr= is mandatory with udp=");
+ return -1;
+ }
+ if (net_socket_udp_init(vlan, "udp", name, sock->udp, sock->localaddr) ==
+ -1) {
return -1;
}
return 0;
diff --git a/net/socket.h b/net/socket.h
index e1fe959412..c4809ad0d9 100644
--- a/net/socket.h
+++ b/net/socket.h
@@ -25,8 +25,9 @@
#define QEMU_NET_SOCKET_H
#include "net.h"
-#include "qemu-common.h"
+#include "qapi-types.h"
-int net_init_socket(QemuOpts *opts, const char *name, VLANState *vlan);
+int net_init_socket(const NetClientOptions *opts, const char *name,
+ VLANState *vlan);
#endif /* QEMU_NET_SOCKET_H */
diff --git a/net/tap-aix.c b/net/tap-aix.c
index e19aaba110..f27c17729e 100644
--- a/net/tap-aix.c
+++ b/net/tap-aix.c
@@ -31,7 +31,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
return -1;
}
-int tap_set_sndbuf(int fd, QemuOpts *opts)
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
{
return 0;
}
diff --git a/net/tap-bsd.c b/net/tap-bsd.c
index 937a94b11f..a3b717dd1c 100644
--- a/net/tap-bsd.c
+++ b/net/tap-bsd.c
@@ -117,7 +117,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
return fd;
}
-int tap_set_sndbuf(int fd, QemuOpts *opts)
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
{
return 0;
}
diff --git a/net/tap-haiku.c b/net/tap-haiku.c
index 91dda8ebc0..34739d1562 100644
--- a/net/tap-haiku.c
+++ b/net/tap-haiku.c
@@ -31,7 +31,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
return -1;
}
-int tap_set_sndbuf(int fd, QemuOpts *opts)
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
{
return 0;
}
diff --git a/net/tap-linux.c b/net/tap-linux.c
index 41d581b734..c6521bec34 100644
--- a/net/tap-linux.c
+++ b/net/tap-linux.c
@@ -98,16 +98,19 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
*/
#define TAP_DEFAULT_SNDBUF 0
-int tap_set_sndbuf(int fd, QemuOpts *opts)
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
{
int sndbuf;
- sndbuf = qemu_opt_get_size(opts, "sndbuf", TAP_DEFAULT_SNDBUF);
+ sndbuf = !tap->has_sndbuf ? TAP_DEFAULT_SNDBUF :
+ tap->sndbuf > INT_MAX ? INT_MAX :
+ tap->sndbuf;
+
if (!sndbuf) {
sndbuf = INT_MAX;
}
- if (ioctl(fd, TUNSETSNDBUF, &sndbuf) == -1 && qemu_opt_get(opts, "sndbuf")) {
+ if (ioctl(fd, TUNSETSNDBUF, &sndbuf) == -1 && tap->has_sndbuf) {
error_report("TUNSETSNDBUF ioctl failed: %s", strerror(errno));
return -1;
}
diff --git a/net/tap-solaris.c b/net/tap-solaris.c
index cf764634ef..5d6ac42f24 100644
--- a/net/tap-solaris.c
+++ b/net/tap-solaris.c
@@ -197,7 +197,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
return fd;
}
-int tap_set_sndbuf(int fd, QemuOpts *opts)
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
{
return 0;
}
diff --git a/net/tap-win32.c b/net/tap-win32.c
index a801a553c4..232807236a 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -667,7 +667,7 @@ static void tap_win32_send(void *opaque)
}
static NetClientInfo net_tap_win32_info = {
- .type = NET_CLIENT_TYPE_TAP,
+ .type = NET_CLIENT_OPTIONS_KIND_TAP,
.size = sizeof(TAPState),
.receive = tap_receive,
.cleanup = tap_cleanup,
@@ -699,18 +699,20 @@ static int tap_win32_init(VLANState *vlan, const char *model,
return 0;
}
-int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
+int net_init_tap(const NetClientOptions *opts, const char *name,
+ VLANState *vlan)
{
- const char *ifname;
+ const NetdevTapOptions *tap;
- ifname = qemu_opt_get(opts, "ifname");
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
+ tap = opts->tap;
- if (!ifname) {
+ if (!tap->has_ifname) {
error_report("tap: no interface name");
return -1;
}
- if (tap_win32_init(vlan, "tap", name, ifname) == -1) {
+ if (tap_win32_init(vlan, "tap", name, tap->ifname) == -1) {
return -1;
}
diff --git a/net/tap.c b/net/tap.c
index 17e91355ce..72062275fe 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -218,7 +218,7 @@ int tap_has_ufo(VLANClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
return s->has_ufo;
}
@@ -227,7 +227,7 @@ int tap_has_vnet_hdr(VLANClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
return !!s->host_vnet_hdr_len;
}
@@ -236,7 +236,7 @@ int tap_has_vnet_hdr_len(VLANClientState *nc, int len)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
return tap_probe_vnet_hdr_len(s->fd, len);
}
@@ -245,7 +245,7 @@ void tap_set_vnet_hdr_len(VLANClientState *nc, int len)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
len == sizeof(struct virtio_net_hdr));
@@ -259,7 +259,7 @@ void tap_using_vnet_hdr(VLANClientState *nc, int using_vnet_hdr)
using_vnet_hdr = using_vnet_hdr != 0;
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
assert(!!s->host_vnet_hdr_len == using_vnet_hdr);
s->using_vnet_hdr = using_vnet_hdr;
@@ -306,14 +306,14 @@ static void tap_poll(VLANClientState *nc, bool enable)
int tap_get_fd(VLANClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
return s->fd;
}
/* fd support */
static NetClientInfo net_tap_info = {
- .type = NET_CLIENT_TYPE_TAP,
+ .type = NET_CLIENT_OPTIONS_KIND_TAP,
.size = sizeof(TAPState),
.receive = tap_receive,
.receive_raw = tap_receive_raw,
@@ -513,20 +513,22 @@ static int net_bridge_run_helper(const char *helper, const char *bridge)
return -1;
}
-int net_init_bridge(QemuOpts *opts, const char *name, VLANState *vlan)
+int net_init_bridge(const NetClientOptions *opts, const char *name,
+ VLANState *vlan)
{
+ const NetdevBridgeOptions *bridge;
+ const char *helper, *br;
+
TAPState *s;
int fd, vnet_hdr;
- if (!qemu_opt_get(opts, "br")) {
- qemu_opt_set(opts, "br", DEFAULT_BRIDGE_INTERFACE);
- }
- if (!qemu_opt_get(opts, "helper")) {
- qemu_opt_set(opts, "helper", DEFAULT_BRIDGE_HELPER);
- }
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_BRIDGE);
+ bridge = opts->bridge;
- fd = net_bridge_run_helper(qemu_opt_get(opts, "helper"),
- qemu_opt_get(opts, "br"));
+ helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER;
+ br = bridge->has_br ? bridge->br : DEFAULT_BRIDGE_INTERFACE;
+
+ fd = net_bridge_run_helper(helper, br);
if (fd == -1) {
return -1;
}
@@ -541,35 +543,38 @@ int net_init_bridge(QemuOpts *opts, const char *name, VLANState *vlan)
return -1;
}
- snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s",
- qemu_opt_get(opts, "helper"), qemu_opt_get(opts, "br"));
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s", helper,
+ br);
return 0;
}
-static int net_tap_init(QemuOpts *opts, int *vnet_hdr)
+static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr,
+ const char *setup_script, char *ifname,
+ size_t ifname_sz)
{
int fd, vnet_hdr_required;
- char ifname[128] = {0,};
- const char *setup_script;
- if (qemu_opt_get(opts, "ifname")) {
- pstrcpy(ifname, sizeof(ifname), qemu_opt_get(opts, "ifname"));
+ if (tap->has_ifname) {
+ pstrcpy(ifname, ifname_sz, tap->ifname);
+ } else {
+ assert(ifname_sz > 0);
+ ifname[0] = '\0';
}
- *vnet_hdr = qemu_opt_get_bool(opts, "vnet_hdr", 1);
- if (qemu_opt_get(opts, "vnet_hdr")) {
+ if (tap->has_vnet_hdr) {
+ *vnet_hdr = tap->vnet_hdr;
vnet_hdr_required = *vnet_hdr;
} else {
+ *vnet_hdr = 1;
vnet_hdr_required = 0;
}
- TFR(fd = tap_open(ifname, sizeof(ifname), vnet_hdr, vnet_hdr_required));
+ TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required));
if (fd < 0) {
return -1;
}
- setup_script = qemu_opt_get(opts, "script");
if (setup_script &&
setup_script[0] != '\0' &&
strcmp(setup_script, "no") != 0 &&
@@ -578,29 +583,34 @@ static int net_tap_init(QemuOpts *opts, int *vnet_hdr)
return -1;
}
- qemu_opt_set(opts, "ifname", ifname);
-
return fd;
}
-int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
+int net_init_tap(const NetClientOptions *opts, const char *name,
+ VLANState *vlan)
{
- TAPState *s;
+ const NetdevTapOptions *tap;
+
int fd, vnet_hdr = 0;
const char *model;
+ TAPState *s;
- if (qemu_opt_get(opts, "fd")) {
- if (qemu_opt_get(opts, "ifname") ||
- qemu_opt_get(opts, "script") ||
- qemu_opt_get(opts, "downscript") ||
- qemu_opt_get(opts, "vnet_hdr") ||
- qemu_opt_get(opts, "helper")) {
+ /* for the no-fd, no-helper case */
+ const char *script = NULL; /* suppress wrong "uninit'd use" gcc warning */
+ char ifname[128];
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
+ tap = opts->tap;
+
+ if (tap->has_fd) {
+ if (tap->has_ifname || tap->has_script || tap->has_downscript ||
+ tap->has_vnet_hdr || tap->has_helper) {
error_report("ifname=, script=, downscript=, vnet_hdr=, "
"and helper= are invalid with fd=");
return -1;
}
- fd = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "fd"));
+ fd = net_handle_fd_param(cur_mon, tap->fd);
if (fd == -1) {
return -1;
}
@@ -611,18 +621,15 @@ int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
model = "tap";
- } else if (qemu_opt_get(opts, "helper")) {
- if (qemu_opt_get(opts, "ifname") ||
- qemu_opt_get(opts, "script") ||
- qemu_opt_get(opts, "downscript") ||
- qemu_opt_get(opts, "vnet_hdr")) {
+ } else if (tap->has_helper) {
+ if (tap->has_ifname || tap->has_script || tap->has_downscript ||
+ tap->has_vnet_hdr) {
error_report("ifname=, script=, downscript=, and vnet_hdr= "
"are invalid with helper=");
return -1;
}
- fd = net_bridge_run_helper(qemu_opt_get(opts, "helper"),
- DEFAULT_BRIDGE_INTERFACE);
+ fd = net_bridge_run_helper(tap->helper, DEFAULT_BRIDGE_INTERFACE);
if (fd == -1) {
return -1;
}
@@ -634,15 +641,8 @@ int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
model = "bridge";
} else {
- if (!qemu_opt_get(opts, "script")) {
- qemu_opt_set(opts, "script", DEFAULT_NETWORK_SCRIPT);
- }
-
- if (!qemu_opt_get(opts, "downscript")) {
- qemu_opt_set(opts, "downscript", DEFAULT_NETWORK_DOWN_SCRIPT);
- }
-
- fd = net_tap_init(opts, &vnet_hdr);
+ script = tap->has_script ? tap->script : DEFAULT_NETWORK_SCRIPT;
+ fd = net_tap_init(tap, &vnet_hdr, script, ifname, sizeof ifname);
if (fd == -1) {
return -1;
}
@@ -656,25 +656,24 @@ int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
return -1;
}
- if (tap_set_sndbuf(s->fd, opts) < 0) {
+ if (tap_set_sndbuf(s->fd, tap) < 0) {
return -1;
}
- if (qemu_opt_get(opts, "fd")) {
+ if (tap->has_fd) {
snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
- } else if (qemu_opt_get(opts, "helper")) {
- snprintf(s->nc.info_str, sizeof(s->nc.info_str),
- "helper=%s", qemu_opt_get(opts, "helper"));
+ } else if (tap->has_helper) {
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s",
+ tap->helper);
} else {
- const char *ifname, *script, *downscript;
+ const char *downscript;
- ifname = qemu_opt_get(opts, "ifname");
- script = qemu_opt_get(opts, "script");
- downscript = qemu_opt_get(opts, "downscript");
+ downscript = tap->has_downscript ? tap->downscript :
+ DEFAULT_NETWORK_DOWN_SCRIPT;
snprintf(s->nc.info_str, sizeof(s->nc.info_str),
- "ifname=%s,script=%s,downscript=%s",
- ifname, script, downscript);
+ "ifname=%s,script=%s,downscript=%s", ifname, script,
+ downscript);
if (strcmp(downscript, "no") != 0) {
snprintf(s->down_script, sizeof(s->down_script), "%s", downscript);
@@ -682,25 +681,26 @@ int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
}
}
- if (qemu_opt_get_bool(opts, "vhost", !!qemu_opt_get(opts, "vhostfd") ||
- qemu_opt_get_bool(opts, "vhostforce", false))) {
- int vhostfd, r;
- bool force = qemu_opt_get_bool(opts, "vhostforce", false);
- if (qemu_opt_get(opts, "vhostfd")) {
- r = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "vhostfd"));
- if (r == -1) {
+ if (tap->has_vhost ? tap->vhost :
+ tap->has_vhostfd || (tap->has_vhostforce && tap->vhostforce)) {
+ int vhostfd;
+
+ if (tap->has_vhostfd) {
+ vhostfd = net_handle_fd_param(cur_mon, tap->vhostfd);
+ if (vhostfd == -1) {
return -1;
}
- vhostfd = r;
} else {
vhostfd = -1;
}
- s->vhost_net = vhost_net_init(&s->nc, vhostfd, force);
+
+ s->vhost_net = vhost_net_init(&s->nc, vhostfd,
+ tap->has_vhostforce && tap->vhostforce);
if (!s->vhost_net) {
error_report("vhost-net requested but could not be initialized");
return -1;
}
- } else if (qemu_opt_get(opts, "vhostfd")) {
+ } else if (tap->has_vhostfd) {
error_report("vhostfd= is not valid without vhost");
return -1;
}
@@ -711,6 +711,6 @@ int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
VHostNetState *tap_get_vhost_net(VLANClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
return s->vhost_net;
}
diff --git a/net/tap.h b/net/tap.h
index b2a9450aab..19dea58dc5 100644
--- a/net/tap.h
+++ b/net/tap.h
@@ -27,12 +27,13 @@
#define QEMU_NET_TAP_H
#include "qemu-common.h"
-#include "qemu-option.h"
+#include "qapi-types.h"
#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
-int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan);
+int net_init_tap(const NetClientOptions *opts, const char *name,
+ VLANState *vlan);
int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required);
@@ -45,7 +46,7 @@ void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr);
void tap_set_offload(VLANClientState *vc, int csum, int tso4, int tso6, int ecn, int ufo);
void tap_set_vnet_hdr_len(VLANClientState *vc, int len);
-int tap_set_sndbuf(int fd, QemuOpts *opts);
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap);
int tap_probe_vnet_hdr(int fd);
int tap_probe_vnet_hdr_len(int fd, int len);
int tap_probe_has_ufo(int fd);
@@ -57,6 +58,7 @@ int tap_get_fd(VLANClientState *vc);
struct vhost_net;
struct vhost_net *tap_get_vhost_net(VLANClientState *vc);
-int net_init_bridge(QemuOpts *opts, const char *name, VLANState *vlan);
+int net_init_bridge(const NetClientOptions *opts, const char *name,
+ VLANState *vlan);
#endif /* QEMU_NET_TAP_H */
diff --git a/net/vde.c b/net/vde.c
index 6b9d45294a..ee19f5c42c 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -69,7 +69,7 @@ static void vde_cleanup(VLANClientState *nc)
}
static NetClientInfo net_vde_info = {
- .type = NET_CLIENT_TYPE_VDE,
+ .type = NET_CLIENT_OPTIONS_KIND_VDE,
.size = sizeof(VDEState),
.receive = vde_receive,
.cleanup = vde_cleanup,
@@ -110,19 +110,17 @@ static int net_vde_init(VLANState *vlan, const char *model,
return 0;
}
-int net_init_vde(QemuOpts *opts, const char *name, VLANState *vlan)
+int net_init_vde(const NetClientOptions *opts, const char *name,
+ VLANState *vlan)
{
- const char *sock;
- const char *group;
- int port, mode;
+ const NetdevVdeOptions *vde;
- sock = qemu_opt_get(opts, "sock");
- group = qemu_opt_get(opts, "group");
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VDE);
+ vde = opts->vde;
- port = qemu_opt_get_number(opts, "port", 0);
- mode = qemu_opt_get_number(opts, "mode", 0700);
-
- if (net_vde_init(vlan, "vde", name, sock, port, group, mode) == -1) {
+ /* missing optional values have been initialized to "all bits zero" */
+ if (net_vde_init(vlan, "vde", name, vde->sock, vde->port, vde->group,
+ vde->has_mode ? vde->mode : 0700) == -1) {
return -1;
}
diff --git a/net/vde.h b/net/vde.h
index 732e5756f6..ad502ef4de 100644
--- a/net/vde.h
+++ b/net/vde.h
@@ -25,11 +25,12 @@
#define QEMU_NET_VDE_H
#include "qemu-common.h"
-#include "qemu-option.h"
+#include "qapi-types.h"
#ifdef CONFIG_VDE
-int net_init_vde(QemuOpts *opts, const char *name, VLANState *vlan);
+int net_init_vde(const NetClientOptions *opts, const char *name,
+ VLANState *vlan);
#endif /* CONFIG_VDE */
diff --git a/qapi-schema.json b/qapi-schema.json
index a92adb1d5b..bc55ed2b6d 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -343,7 +343,7 @@
# @CPU: the index of the virtual CPU
#
# @current: this only exists for backwards compatible and should be ignored
-#
+#
# @halted: true if the virtual CPU is in the halt state. Halt usually refers
# to a processor specific low power mode.
#
@@ -686,7 +686,7 @@
# @SpiceInfo
#
# Information about the SPICE session.
-#
+#
# @enabled: true if the SPICE server is enabled, false otherwise
#
# @host: #optional The hostname the SPICE server is bound to. This depends on
@@ -1297,7 +1297,7 @@
##
{ 'command': 'human-monitor-command',
'data': {'command-line': 'str', '*cpu-index': 'int'},
- 'returns': 'str' }
+ 'returns': 'str' }
##
# @migrate_cancel
@@ -1458,7 +1458,7 @@
# @password: the new password
#
# @connected: #optional how to handle existing clients when changing the
-# password. If nothing is specified, defaults to `keep'
+# password. If nothing is specified, defaults to `keep'
# `fail' to fail the command if clients are connected
# `disconnect' to disconnect existing clients
# `keep' to maintain existing clients
@@ -1598,7 +1598,7 @@
# If the argument combination is invalid, InvalidParameterCombination
#
# Since: 1.1
-##
+##
{ 'command': 'block_set_io_throttle',
'data': { 'device': 'str', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int' } }
@@ -1872,6 +1872,284 @@
{ 'command': 'netdev_del', 'data': {'id': 'str'} }
##
+# @NetdevNoneOptions
+#
+# Use it alone to have zero network devices.
+#
+# Since 1.2
+##
+{ 'type': 'NetdevNoneOptions',
+ 'data': { } }
+
+##
+# @NetLegacyNicOptions
+#
+# Create a new Network Interface Card.
+#
+# @netdev: #optional id of -netdev to connect to
+#
+# @macaddr: #optional MAC address
+#
+# @model: #optional device model (e1000, rtl8139, virtio etc.)
+#
+# @addr: #optional PCI device address
+#
+# @vectors: #optional number of MSI-x vectors, 0 to disable MSI-X
+#
+# Since 1.2
+##
+{ 'type': 'NetLegacyNicOptions',
+ 'data': {
+ '*netdev': 'str',
+ '*macaddr': 'str',
+ '*model': 'str',
+ '*addr': 'str',
+ '*vectors': 'uint32' } }
+
+##
+# @String
+#
+# A fat type wrapping 'str', to be embedded in lists.
+#
+# Since 1.2
+##
+{ 'type': 'String',
+ 'data': {
+ 'str': 'str' } }
+
+##
+# @NetdevUserOptions
+#
+# Use the user mode network stack which requires no administrator privilege to
+# run.
+#
+# @hostname: #optional client hostname reported by the builtin DHCP server
+#
+# @restrict: #optional isolate the guest from the host
+#
+# @ip: #optional legacy parameter, use net= instead
+#
+# @net: #optional IP address and optional netmask
+#
+# @host: #optional guest-visible address of the host
+#
+# @tftp: #optional root directory of the built-in TFTP server
+#
+# @bootfile: #optional BOOTP filename, for use with tftp=
+#
+# @dhcpstart: #optional the first of the 16 IPs the built-in DHCP server can
+# assign
+#
+# @dns: #optional guest-visible address of the virtual nameserver
+#
+# @smb: #optional root directory of the built-in SMB server
+#
+# @smbserver: #optional IP address of the built-in SMB server
+#
+# @hostfwd: #optional redirect incoming TCP or UDP host connections to guest
+# endpoints
+#
+# @guestfwd: #optional forward guest TCP connections
+#
+# Since 1.2
+##
+{ 'type': 'NetdevUserOptions',
+ 'data': {
+ '*hostname': 'str',
+ '*restrict': 'bool',
+ '*ip': 'str',
+ '*net': 'str',
+ '*host': 'str',
+ '*tftp': 'str',
+ '*bootfile': 'str',
+ '*dhcpstart': 'str',
+ '*dns': 'str',
+ '*smb': 'str',
+ '*smbserver': 'str',
+ '*hostfwd': ['String'],
+ '*guestfwd': ['String'] } }
+
+##
+# @NetdevTapOptions
+#
+# Connect the host TAP network interface name to the VLAN.
+#
+# @ifname: #optional interface name
+#
+# @fd: #optional file descriptor of an already opened tap
+#
+# @script: #optional script to initialize the interface
+#
+# @downscript: #optional script to shut down the interface
+#
+# @helper: #optional command to execute to configure bridge
+#
+# @sndbuf: #optional send buffer limit. Understands [TGMKkb] suffixes.
+#
+# @vnet_hdr: #optional enable the IFF_VNET_HDR flag on the tap interface
+#
+# @vhost: #optional enable vhost-net network accelerator
+#
+# @vhostfd: #optional file descriptor of an already opened vhost net device
+#
+# @vhostforce: #optional vhost on for non-MSIX virtio guests
+#
+# Since 1.2
+##
+{ 'type': 'NetdevTapOptions',
+ 'data': {
+ '*ifname': 'str',
+ '*fd': 'str',
+ '*script': 'str',
+ '*downscript': 'str',
+ '*helper': 'str',
+ '*sndbuf': 'size',
+ '*vnet_hdr': 'bool',
+ '*vhost': 'bool',
+ '*vhostfd': 'str',
+ '*vhostforce': 'bool' } }
+
+##
+# @NetdevSocketOptions
+#
+# Connect the VLAN to a remote VLAN in another QEMU virtual machine using a TCP
+# socket connection.
+#
+# @fd: #optional file descriptor of an already opened socket
+#
+# @listen: #optional port number, and optional hostname, to listen on
+#
+# @connect: #optional port number, and optional hostname, to connect to
+#
+# @mcast: #optional UDP multicast address and port number
+#
+# @localaddr: #optional source address and port for multicast and udp packets
+#
+# @udp: #optional UDP unicast address and port number
+#
+# Since 1.2
+##
+{ 'type': 'NetdevSocketOptions',
+ 'data': {
+ '*fd': 'str',
+ '*listen': 'str',
+ '*connect': 'str',
+ '*mcast': 'str',
+ '*localaddr': 'str',
+ '*udp': 'str' } }
+
+##
+# @NetdevVdeOptions
+#
+# Connect the VLAN to a vde switch running on the host.
+#
+# @sock: #optional socket path
+#
+# @port: #optional port number
+#
+# @group: #optional group owner of socket
+#
+# @mode: #optional permissions for socket
+#
+# Since 1.2
+##
+{ 'type': 'NetdevVdeOptions',
+ 'data': {
+ '*sock': 'str',
+ '*port': 'uint16',
+ '*group': 'str',
+ '*mode': 'uint16' } }
+
+##
+# @NetdevDumpOptions
+#
+# Dump VLAN network traffic to a file.
+#
+# @len: #optional per-packet size limit (64k default). Understands [TGMKkb]
+# suffixes.
+#
+# @file: #optional dump file path (default is qemu-vlan0.pcap)
+#
+# Since 1.2
+##
+{ 'type': 'NetdevDumpOptions',
+ 'data': {
+ '*len': 'size',
+ '*file': 'str' } }
+
+##
+# @NetdevBridgeOptions
+#
+# Connect a host TAP network interface to a host bridge device.
+#
+# @br: #optional bridge name
+#
+# @helper: #optional command to execute to configure bridge
+#
+# Since 1.2
+##
+{ 'type': 'NetdevBridgeOptions',
+ 'data': {
+ '*br': 'str',
+ '*helper': 'str' } }
+
+##
+# @NetClientOptions
+#
+# A discriminated record of network device traits.
+#
+# Since 1.2
+##
+{ 'union': 'NetClientOptions',
+ 'data': {
+ 'none': 'NetdevNoneOptions',
+ 'nic': 'NetLegacyNicOptions',
+ 'user': 'NetdevUserOptions',
+ 'tap': 'NetdevTapOptions',
+ 'socket': 'NetdevSocketOptions',
+ 'vde': 'NetdevVdeOptions',
+ 'dump': 'NetdevDumpOptions',
+ 'bridge': 'NetdevBridgeOptions' } }
+
+##
+# @NetLegacy
+#
+# Captures the configuration of a network device; legacy.
+#
+# @vlan: #optional vlan number
+#
+# @id: #optional identifier for monitor commands
+#
+# @name: #optional identifier for monitor commands, ignored if @id is present
+#
+# @opts: device type specific properties (legacy)
+#
+# Since 1.2
+##
+{ 'type': 'NetLegacy',
+ 'data': {
+ '*vlan': 'int32',
+ '*id': 'str',
+ '*name': 'str',
+ 'opts': 'NetClientOptions' } }
+
+##
+# @Netdev
+#
+# Captures the configuration of a network device.
+#
+# @id: identifier for monitor commands.
+#
+# @opts: device type specific properties
+#
+# Since 1.2
+##
+{ 'type': 'Netdev',
+ 'data': {
+ 'id': 'str',
+ 'opts': 'NetClientOptions' } }
+
+##
# @getfd:
#
# Receive a file descriptor via SCM rights and assign it a name
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index d0b0c16b90..5f5846e767 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -1,3 +1,3 @@
qapi-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
qapi-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
-qapi-obj-y += string-input-visitor.o string-output-visitor.o
+qapi-obj-y += string-input-visitor.o string-output-visitor.o opts-visitor.o
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
new file mode 100644
index 0000000000..a59d306e46
--- /dev/null
+++ b/qapi/opts-visitor.c
@@ -0,0 +1,427 @@
+/*
+ * Options Visitor
+ *
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Author: Laszlo Ersek <lersek@redhat.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 "opts-visitor.h"
+#include "qemu-queue.h"
+#include "qemu-option-internal.h"
+#include "qapi-visit-impl.h"
+
+
+struct OptsVisitor
+{
+ Visitor visitor;
+
+ /* Ownership remains with opts_visitor_new()'s caller. */
+ const QemuOpts *opts_root;
+
+ unsigned depth;
+
+ /* Non-null iff depth is positive. Each key is a QemuOpt name. Each value
+ * is a non-empty GQueue, enumerating all QemuOpt occurrences with that
+ * name. */
+ GHashTable *unprocessed_opts;
+
+ /* The list currently being traversed with opts_start_list() /
+ * opts_next_list(). The list must have a struct element type in the
+ * schema, with a single mandatory scalar member. */
+ GQueue *repeated_opts;
+ bool repeated_opts_first;
+
+ /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for
+ * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does
+ * not survive or escape the OptsVisitor object.
+ */
+ QemuOpt *fake_id_opt;
+};
+
+
+static void
+destroy_list(gpointer list)
+{
+ g_queue_free(list);
+}
+
+
+static void
+opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
+{
+ GQueue *list;
+
+ list = g_hash_table_lookup(unprocessed_opts, opt->name);
+ if (list == NULL) {
+ list = g_queue_new();
+
+ /* GHashTable will never try to free the keys -- we supply NULL as
+ * "key_destroy_func" in opts_start_struct(). Thus cast away key
+ * const-ness in order to suppress gcc's warning.
+ */
+ g_hash_table_insert(unprocessed_opts, (gpointer)opt->name, list);
+ }
+
+ /* Similarly, destroy_list() doesn't call g_queue_free_full(). */
+ g_queue_push_tail(list, (gpointer)opt);
+}
+
+
+static void
+opts_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t size, Error **errp)
+{
+ OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ const QemuOpt *opt;
+
+ *obj = g_malloc0(size > 0 ? size : 1);
+ if (ov->depth++ > 0) {
+ return;
+ }
+
+ ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal,
+ NULL, &destroy_list);
+ QTAILQ_FOREACH(opt, &ov->opts_root->head, next) {
+ /* ensured by qemu-option.c::opts_do_parse() */
+ assert(strcmp(opt->name, "id") != 0);
+
+ opts_visitor_insert(ov->unprocessed_opts, opt);
+ }
+
+ if (ov->opts_root->id != NULL) {
+ ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt);
+
+ ov->fake_id_opt->name = "id";
+ ov->fake_id_opt->str = ov->opts_root->id;
+ opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt);
+ }
+}
+
+
+static gboolean
+ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data)
+{
+ return TRUE;
+}
+
+
+static void
+opts_end_struct(Visitor *v, Error **errp)
+{
+ OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ GQueue *any;
+
+ if (--ov->depth > 0) {
+ return;
+ }
+
+ /* we should have processed all (distinct) QemuOpt instances */
+ any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL);
+ if (any) {
+ const QemuOpt *first;
+
+ first = g_queue_peek_head(any);
+ error_set(errp, QERR_INVALID_PARAMETER, first->name);
+ }
+ g_hash_table_destroy(ov->unprocessed_opts);
+ ov->unprocessed_opts = NULL;
+ g_free(ov->fake_id_opt);
+ ov->fake_id_opt = NULL;
+}
+
+
+static GQueue *
+lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
+{
+ GQueue *list;
+
+ list = g_hash_table_lookup(ov->unprocessed_opts, name);
+ if (!list) {
+ error_set(errp, QERR_MISSING_PARAMETER, name);
+ }
+ return list;
+}
+
+
+static void
+opts_start_list(Visitor *v, const char *name, Error **errp)
+{
+ OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+
+ /* we can't traverse a list in a list */
+ assert(ov->repeated_opts == NULL);
+ ov->repeated_opts = lookup_distinct(ov, name, errp);
+ ov->repeated_opts_first = (ov->repeated_opts != NULL);
+}
+
+
+static GenericList *
+opts_next_list(Visitor *v, GenericList **list, Error **errp)
+{
+ OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ GenericList **link;
+
+ if (ov->repeated_opts_first) {
+ ov->repeated_opts_first = false;
+ link = list;
+ } else {
+ const QemuOpt *opt;
+
+ opt = g_queue_pop_head(ov->repeated_opts);
+ if (g_queue_is_empty(ov->repeated_opts)) {
+ g_hash_table_remove(ov->unprocessed_opts, opt->name);
+ return NULL;
+ }
+ link = &(*list)->next;
+ }
+
+ *link = g_malloc0(sizeof **link);
+ return *link;
+}
+
+
+static void
+opts_end_list(Visitor *v, Error **errp)
+{
+ OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+
+ ov->repeated_opts = NULL;
+}
+
+
+static const QemuOpt *
+lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp)
+{
+ if (ov->repeated_opts == NULL) {
+ GQueue *list;
+
+ /* the last occurrence of any QemuOpt takes effect when queried by name
+ */
+ list = lookup_distinct(ov, name, errp);
+ return list ? g_queue_peek_tail(list) : NULL;
+ }
+ return g_queue_peek_head(ov->repeated_opts);
+}
+
+
+static void
+processed(OptsVisitor *ov, const char *name)
+{
+ if (ov->repeated_opts == NULL) {
+ g_hash_table_remove(ov->unprocessed_opts, name);
+ }
+}
+
+
+static void
+opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)
+{
+ OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ const QemuOpt *opt;
+
+ opt = lookup_scalar(ov, name, errp);
+ if (!opt) {
+ return;
+ }
+ *obj = g_strdup(opt->str ? opt->str : "");
+ processed(ov, name);
+}
+
+
+/* mimics qemu-option.c::parse_option_bool() */
+static void
+opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
+{
+ OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ const QemuOpt *opt;
+
+ opt = lookup_scalar(ov, name, errp);
+ if (!opt) {
+ return;
+ }
+
+ if (opt->str) {
+ if (strcmp(opt->str, "on") == 0 ||
+ strcmp(opt->str, "yes") == 0 ||
+ strcmp(opt->str, "y") == 0) {
+ *obj = true;
+ } else if (strcmp(opt->str, "off") == 0 ||
+ strcmp(opt->str, "no") == 0 ||
+ strcmp(opt->str, "n") == 0) {
+ *obj = false;
+ } else {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
+ "on|yes|y|off|no|n");
+ return;
+ }
+ } else {
+ *obj = true;
+ }
+
+ processed(ov, name);
+}
+
+
+static void
+opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
+{
+ OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ const QemuOpt *opt;
+ const char *str;
+ long long val;
+ char *endptr;
+
+ opt = lookup_scalar(ov, name, errp);
+ if (!opt) {
+ return;
+ }
+ str = opt->str ? opt->str : "";
+
+ errno = 0;
+ val = strtoll(str, &endptr, 0);
+ if (*str != '\0' && *endptr == '\0' && errno == 0 && INT64_MIN <= val &&
+ val <= INT64_MAX) {
+ *obj = val;
+ processed(ov, name);
+ return;
+ }
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, "an int64 value");
+}
+
+
+static void
+opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
+{
+ OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ const QemuOpt *opt;
+ const char *str;
+
+ opt = lookup_scalar(ov, name, errp);
+ if (!opt) {
+ return;
+ }
+
+ str = opt->str;
+ if (str != NULL) {
+ while (isspace((unsigned char)*str)) {
+ ++str;
+ }
+
+ if (*str != '-' && *str != '\0') {
+ unsigned long long val;
+ char *endptr;
+
+ /* non-empty, non-negative subject sequence */
+ errno = 0;
+ val = strtoull(str, &endptr, 0);
+ if (*endptr == '\0' && errno == 0 && val <= UINT64_MAX) {
+ *obj = val;
+ processed(ov, name);
+ return;
+ }
+ }
+ }
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
+ "an uint64 value");
+}
+
+
+static void
+opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
+{
+ OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ const QemuOpt *opt;
+ int64_t val;
+ char *endptr;
+
+ opt = lookup_scalar(ov, name, errp);
+ if (!opt) {
+ return;
+ }
+
+ val = strtosz_suffix(opt->str ? opt->str : "", &endptr,
+ STRTOSZ_DEFSUFFIX_B);
+ if (val != -1 && *endptr == '\0') {
+ *obj = val;
+ processed(ov, name);
+ return;
+ }
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
+ "a size value representible as a non-negative int64");
+}
+
+
+static void
+opts_start_optional(Visitor *v, bool *present, const char *name,
+ Error **errp)
+{
+ OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+
+ /* we only support a single mandatory scalar field in a list node */
+ assert(ov->repeated_opts == NULL);
+ *present = (lookup_distinct(ov, name, NULL) != NULL);
+}
+
+
+OptsVisitor *
+opts_visitor_new(const QemuOpts *opts)
+{
+ OptsVisitor *ov;
+
+ ov = g_malloc0(sizeof *ov);
+
+ ov->visitor.start_struct = &opts_start_struct;
+ ov->visitor.end_struct = &opts_end_struct;
+
+ ov->visitor.start_list = &opts_start_list;
+ ov->visitor.next_list = &opts_next_list;
+ ov->visitor.end_list = &opts_end_list;
+
+ /* input_type_enum() covers both "normal" enums and union discriminators.
+ * The union discriminator field is always generated as "type"; it should
+ * match the "type" QemuOpt child of any QemuOpts.
+ *
+ * input_type_enum() will remove the looked-up key from the
+ * "unprocessed_opts" hash even if the lookup fails, because the removal is
+ * done earlier in opts_type_str(). This should be harmless.
+ */
+ ov->visitor.type_enum = &input_type_enum;
+
+ ov->visitor.type_int = &opts_type_int;
+ ov->visitor.type_uint64 = &opts_type_uint64;
+ ov->visitor.type_size = &opts_type_size;
+ ov->visitor.type_bool = &opts_type_bool;
+ ov->visitor.type_str = &opts_type_str;
+
+ /* type_number() is not filled in, but this is not the first visitor to
+ * skip some mandatory methods... */
+
+ ov->visitor.start_optional = &opts_start_optional;
+
+ ov->opts_root = opts;
+
+ return ov;
+}
+
+
+void
+opts_visitor_cleanup(OptsVisitor *ov)
+{
+ if (ov->unprocessed_opts != NULL) {
+ g_hash_table_destroy(ov->unprocessed_opts);
+ }
+ g_free(ov->fake_id_opt);
+ memset(ov, '\0', sizeof *ov);
+}
+
+
+Visitor *
+opts_get_visitor(OptsVisitor *ov)
+{
+ return &ov->visitor;
+}
diff --git a/qapi/opts-visitor.h b/qapi/opts-visitor.h
new file mode 100644
index 0000000000..ea1a395573
--- /dev/null
+++ b/qapi/opts-visitor.h
@@ -0,0 +1,31 @@
+/*
+ * Options Visitor
+ *
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Author: Laszlo Ersek <lersek@redhat.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.
+ *
+ */
+
+#ifndef OPTS_VISITOR_H
+#define OPTS_VISITOR_H
+
+#include "qapi-visit-core.h"
+#include "qemu-option.h"
+
+typedef struct OptsVisitor OptsVisitor;
+
+/* Contrarily to qemu-option.c::parse_option_number(), OptsVisitor's "int"
+ * parser relies on strtoll() instead of strtoull(). Consequences:
+ * - string representations of negative numbers yield negative values,
+ * - values below INT64_MIN or LLONG_MIN are rejected,
+ * - values above INT64_MAX or LLONG_MAX are rejected.
+ */
+OptsVisitor *opts_visitor_new(const QemuOpts *opts);
+void opts_visitor_cleanup(OptsVisitor *nv);
+Visitor *opts_get_visitor(OptsVisitor *nv);
+
+#endif
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 705eca90aa..7a82b63766 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -39,9 +39,8 @@ void visit_start_struct(Visitor *v, void **obj, const char *kind,
void visit_end_struct(Visitor *v, Error **errp)
{
- if (!error_is_set(errp)) {
- v->end_struct(v, errp);
- }
+ assert(!error_is_set(errp));
+ v->end_struct(v, errp);
}
void visit_start_list(Visitor *v, const char *name, Error **errp)
@@ -62,9 +61,8 @@ GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
void visit_end_list(Visitor *v, Error **errp)
{
- if (!error_is_set(errp)) {
- v->end_list(v, errp);
- }
+ assert(!error_is_set(errp));
+ v->end_list(v, errp);
}
void visit_start_optional(Visitor *v, bool *present, const char *name,
@@ -236,6 +234,13 @@ void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp)
}
}
+void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ (v->type_size ? v->type_size : v->type_uint64)(v, obj, name, errp);
+ }
+}
+
void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
{
if (!error_is_set(errp)) {
diff --git a/qapi/qapi-visit-core.h b/qapi/qapi-visit-core.h
index a19d70c104..60acedac77 100644
--- a/qapi/qapi-visit-core.h
+++ b/qapi/qapi-visit-core.h
@@ -60,6 +60,8 @@ struct Visitor
void (*type_int16)(Visitor *v, int16_t *obj, const char *name, Error **errp);
void (*type_int32)(Visitor *v, int32_t *obj, const char *name, Error **errp);
void (*type_int64)(Visitor *v, int64_t *obj, const char *name, Error **errp);
+ /* visit_type_size() falls back to (*type_uint64)() if type_size is unset */
+ void (*type_size)(Visitor *v, uint64_t *obj, const char *name, Error **errp);
};
void visit_start_handle(Visitor *v, void **obj, const char *kind,
@@ -85,6 +87,7 @@ void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp);
void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp);
void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp);
void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp);
+void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp);
void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
diff --git a/qemu-option-internal.h b/qemu-option-internal.h
new file mode 100644
index 0000000000..19fdc1ca85
--- /dev/null
+++ b/qemu-option-internal.h
@@ -0,0 +1,53 @@
+/*
+ * Commandline option parsing functions
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2009 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QEMU_OPTIONS_INTERNAL_H
+#define QEMU_OPTIONS_INTERNAL_H
+
+#include "qemu-option.h"
+
+struct QemuOpt {
+ const char *name;
+ const char *str;
+
+ const QemuOptDesc *desc;
+ union {
+ bool boolean;
+ uint64_t uint;
+ } value;
+
+ QemuOpts *opts;
+ QTAILQ_ENTRY(QemuOpt) next;
+};
+
+struct QemuOpts {
+ char *id;
+ QemuOptsList *list;
+ Location loc;
+ QTAILQ_HEAD(QemuOptHead, QemuOpt) head;
+ QTAILQ_ENTRY(QemuOpts) next;
+};
+
+#endif
diff --git a/qemu-option.c b/qemu-option.c
index bb3886c6b9..8334190c53 100644
--- a/qemu-option.c
+++ b/qemu-option.c
@@ -29,9 +29,9 @@
#include "qemu-common.h"
#include "qemu-error.h"
#include "qemu-objects.h"
-#include "qemu-option.h"
#include "error.h"
#include "qerror.h"
+#include "qemu-option-internal.h"
/*
* Extracts the name of an option from the parameter string (p points at the
@@ -511,28 +511,6 @@ void print_option_help(QEMUOptionParameter *list)
/* ------------------------------------------------------------------ */
-struct QemuOpt {
- const char *name;
- const char *str;
-
- const QemuOptDesc *desc;
- union {
- bool boolean;
- uint64_t uint;
- } value;
-
- QemuOpts *opts;
- QTAILQ_ENTRY(QemuOpt) next;
-};
-
-struct QemuOpts {
- char *id;
- QemuOptsList *list;
- Location loc;
- QTAILQ_HEAD(QemuOptHead, QemuOpt) head;
- QTAILQ_ENTRY(QemuOpts) next;
-};
-
static QemuOpt *qemu_opt_find(QemuOpts *opts, const char *name)
{
QemuOpt *opt;
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 8d4e94a45f..04ef7c41ab 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -17,32 +17,49 @@ import os
import getopt
import errno
-def generate_visit_struct_body(field_prefix, members):
- ret = ""
+def generate_visit_struct_body(field_prefix, name, members):
+ ret = mcgen('''
+if (!error_is_set(errp)) {
+''')
+ push_indent()
+
if len(field_prefix):
field_prefix = field_prefix + "."
+ ret += mcgen('''
+Error **errp = &err; /* from outer scope */
+Error *err = NULL;
+visit_start_struct(m, NULL, "", "%(name)s", 0, &err);
+''',
+ name=name)
+ else:
+ ret += mcgen('''
+Error *err = NULL;
+visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
+''',
+ name=name)
+
+ ret += mcgen('''
+if (!err) {
+ if (!obj || *obj) {
+''')
+
+ push_indent()
+ push_indent()
for argname, argentry, optional, structured in parse_args(members):
if optional:
ret += mcgen('''
-visit_start_optional(m, (obj && *obj) ? &(*obj)->%(c_prefix)shas_%(c_name)s : NULL, "%(name)s", errp);
-if ((*obj)->%(prefix)shas_%(c_name)s) {
+visit_start_optional(m, obj ? &(*obj)->%(c_prefix)shas_%(c_name)s : NULL, "%(name)s", &err);
+if (obj && (*obj)->%(prefix)shas_%(c_name)s) {
''',
c_prefix=c_var(field_prefix), prefix=field_prefix,
c_name=c_var(argname), name=argname)
push_indent()
if structured:
- ret += mcgen('''
-visit_start_struct(m, NULL, "", "%(name)s", 0, errp);
-''',
- name=argname)
- ret += generate_visit_struct_body(field_prefix + argname, argentry)
- ret += mcgen('''
-visit_end_struct(m, errp);
-''')
+ ret += generate_visit_struct_body(field_prefix + argname, argname, argentry)
else:
ret += mcgen('''
-visit_type_%(type)s(m, (obj && *obj) ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, "%(name)s", errp);
+visit_type_%(type)s(m, obj ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, "%(name)s", &err);
''',
c_prefix=c_var(field_prefix), prefix=field_prefix,
type=type_name(argentry), c_name=c_var(argname),
@@ -52,7 +69,25 @@ visit_type_%(type)s(m, (obj && *obj) ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, "
pop_indent()
ret += mcgen('''
}
-visit_end_optional(m, errp);
+visit_end_optional(m, &err);
+''')
+
+ pop_indent()
+ ret += mcgen('''
+
+ error_propagate(errp, err);
+ err = NULL;
+}
+''')
+
+ pop_indent()
+ pop_indent()
+ ret += mcgen('''
+ /* Always call end_struct if start_struct succeeded. */
+ visit_end_struct(m, &err);
+ }
+ error_propagate(errp, err);
+}
''')
return ret
@@ -61,22 +96,14 @@ def generate_visit_struct(name, members):
void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
{
- if (error_is_set(errp)) {
- return;
- }
- visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), errp);
- if (obj && !*obj) {
- goto end;
- }
''',
name=name)
+
push_indent()
- ret += generate_visit_struct_body("", members)
+ ret += generate_visit_struct_body("", name, members)
pop_indent()
ret += mcgen('''
-end:
- visit_end_struct(m, errp);
}
''')
return ret
@@ -87,18 +114,23 @@ def generate_visit_list(name, members):
void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp)
{
GenericList *i, **prev = (GenericList **)obj;
+ Error *err = NULL;
- if (error_is_set(errp)) {
- return;
- }
- visit_start_list(m, name, errp);
-
- for (; (i = visit_next_list(m, prev, errp)) != NULL; prev = &i) {
- %(name)sList *native_i = (%(name)sList *)i;
- visit_type_%(name)s(m, &native_i->value, NULL, errp);
+ if (!error_is_set(errp)) {
+ visit_start_list(m, name, &err);
+ if (!err) {
+ for (; (i = visit_next_list(m, prev, &err)) != NULL; prev = &i) {
+ %(name)sList *native_i = (%(name)sList *)i;
+ visit_type_%(name)s(m, &native_i->value, NULL, &err);
+ }
+ error_propagate(errp, err);
+ err = NULL;
+
+ /* Always call end_list if start_list succeeded. */
+ visit_end_list(m, &err);
+ }
+ error_propagate(errp, err);
}
-
- visit_end_list(m, errp);
}
''',
name=name)
@@ -122,27 +154,23 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **
{
Error *err = NULL;
- if (error_is_set(errp)) {
- return;
- }
- visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
- if (obj && !*obj) {
- goto end;
- }
- visit_type_%(name)sKind(m, &(*obj)->kind, "type", &err);
- if (err) {
- error_propagate(errp, err);
- goto end;
- }
- switch ((*obj)->kind) {
+ if (!error_is_set(errp)) {
+ visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
+ if (!err) {
+ if (!obj || *obj) {
+ visit_type_%(name)sKind(m, &(*obj)->kind, "type", &err);
+ if (!err) {
+ switch ((*obj)->kind) {
''',
name=name)
+ push_indent()
+ push_indent()
for key in members:
ret += mcgen('''
- case %(abbrev)s_KIND_%(enum)s:
- visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", errp);
- break;
+ case %(abbrev)s_KIND_%(enum)s:
+ visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);
+ break;
''',
abbrev = de_camel_case(name).upper(),
enum = c_fun(de_camel_case(key)).upper(),
@@ -150,11 +178,25 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **
c_name=c_fun(key))
ret += mcgen('''
- default:
- abort();
+ default:
+ abort();
+ }
+ }
+ error_propagate(errp, err);
+ err = NULL;
+ }
+''')
+ pop_indent()
+ ret += mcgen('''
+ /* Always call end_struct if start_struct succeeded. */
+ visit_end_struct(m, &err);
}
-end:
- visit_end_struct(m, errp);
+ error_propagate(errp, err);
+}
+''')
+
+ pop_indent();
+ ret += mcgen('''
}
''')
diff --git a/scripts/qapi.py b/scripts/qapi.py
index e06233666b..8082af3fcd 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -159,6 +159,12 @@ def c_type(name):
return 'char *'
elif name == 'int':
return 'int64_t'
+ elif (name == 'int8' or name == 'int16' or name == 'int32' or
+ name == 'int64' or name == 'uint8' or name == 'uint16' or
+ name == 'uint32' or name == 'uint64'):
+ return name + '_t'
+ elif name == 'size':
+ return 'uint64_t'
elif name == 'bool':
return 'bool'
elif name == 'number':
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 60cbf019bb..dc3c507f2b 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -3,6 +3,9 @@
#include "test-qmp-commands.h"
#include "qapi/qmp-core.h"
#include "module.h"
+#include "qapi/qmp-input-visitor.h"
+#include "tests/test-qapi-types.h"
+#include "tests/test-qapi-visit.h"
void qmp_user_def_cmd(Error **errp)
{
@@ -123,6 +126,44 @@ static void test_dealloc_types(void)
qapi_free_UserDefOneList(ud1list);
}
+/* test generated deallocation on an object whose construction was prematurely
+ * terminated due to an error */
+static void test_dealloc_partial(void)
+{
+ static const char text[] = "don't leak me";
+
+ UserDefTwo *ud2 = NULL;
+ Error *err = NULL;
+
+ /* create partial object */
+ {
+ QDict *ud2_dict;
+ QmpInputVisitor *qiv;
+
+ ud2_dict = qdict_new();
+ qdict_put_obj(ud2_dict, "string", QOBJECT(qstring_from_str(text)));
+
+ qiv = qmp_input_visitor_new(QOBJECT(ud2_dict));
+ visit_type_UserDefTwo(qmp_input_get_visitor(qiv), &ud2, NULL, &err);
+ qmp_input_visitor_cleanup(qiv);
+ QDECREF(ud2_dict);
+ }
+
+ /* verify partial success */
+ assert(ud2 != NULL);
+ assert(ud2->string != NULL);
+ assert(strcmp(ud2->string, text) == 0);
+ assert(ud2->dict.dict.userdef == NULL);
+
+ /* confirm & release construction error */
+ assert(err != NULL);
+ error_free(err);
+
+ /* tear down partial object */
+ qapi_free_UserDefTwo(ud2);
+}
+
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -131,6 +172,7 @@ int main(int argc, char **argv)
g_test_add_func("/0.15/dispatch_cmd_error", test_dispatch_cmd_error);
g_test_add_func("/0.15/dispatch_cmd_io", test_dispatch_cmd_io);
g_test_add_func("/0.15/dealloc_types", test_dealloc_types);
+ g_test_add_func("/0.15/dealloc_partial", test_dealloc_partial);
module_call_init(MODULE_INIT_QAPI);
g_test_run();
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index c30fdc4e59..8f5a509582 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -151,14 +151,22 @@ typedef struct TestStruct
static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
const char *name, Error **errp)
{
- visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
- errp);
-
- visit_type_int(v, &(*obj)->integer, "integer", errp);
- visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
- visit_type_str(v, &(*obj)->string, "string", errp);
-
- visit_end_struct(v, errp);
+ Error *err = NULL;
+ if (!error_is_set(errp)) {
+ visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
+ &err);
+ if (!err) {
+ visit_type_int(v, &(*obj)->integer, "integer", &err);
+ visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
+ visit_type_str(v, &(*obj)->string, "string", &err);
+
+ /* Always call end_struct if start_struct succeeded. */
+ error_propagate(errp, err);
+ err = NULL;
+ visit_end_struct(v, &err);
+ }
+ error_propagate(errp, err);
+ }
}
static void test_visitor_in_struct(TestInputVisitorData *data,