Merge remote-tracking branch 'stefanha/net' into staging

* stefanha/net:
  remove unused QemuOpts parameter from net init functions
  convert net_init_bridge() to NetClientOptions
  convert net_init_tap() to NetClientOptions
  convert net_init_vde() to NetClientOptions
  convert net_init_socket() to NetClientOptions
  convert net_init_slirp() to NetClientOptions
  convert net_init_dump() to NetClientOptions
  convert net_init_nic() to NetClientOptions
  convert net_client_init() to OptsVisitor
  hw, net: "net_client_type" -> "NetClientOptionsKind" (qapi-generated)
  qapi schema: add Netdev types
  qapi schema: remove trailing whitespace
  qapi: introduce OptsVisitor
  expose QemuOpt and QemuOpts struct definitions to interested parties
  qapi: introduce "size" type
  qapi: generate C types for fixed-width integers
  qapi: add test case for deallocating traversal of incomplete structure
  qapi: fix error propagation
  MAINTAINERS: Replace net maintainer Mark McLoughlin with Stefan Hajnoczi
stable-1.2
Anthony Liguori 2012-07-23 13:15:34 -05:00
commit a21143486b
60 changed files with 1363 additions and 782 deletions

View File

@ -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>

View File

@ -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 ===

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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");

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

510
net.c
View File

@ -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)
{
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;
}
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;
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
strcmp(type, "socket") != 0) {
#ifdef CONFIG_NET_BRIDGE
case NET_CLIENT_OPTIONS_KIND_BRIDGE:
#endif
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 (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_opt_get(opts, "name")) {
error_set(errp, QERR_INVALID_PARAMETER, "name");
return -1;
}
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;
}
}
name = qemu_opts_id(opts);
if (!name) {
name = qemu_opt_get(opts, "name");
}
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;
}
/* 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);
}
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;
}
}
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
"a network client type");
return -1;
return 0;
}
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);
}
}
int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
{
void *object = NULL;
Error *err = NULL;
int ret = -1;
{
OptsVisitor *ov = opts_visitor_new(opts);
net_visit(opts_get_visitor(ov), is_netdev, &object, &err);
opts_visitor_cleanup(ov);
}
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);
}
}

16
net.h
View File

@ -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;

View File

@ -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);
}

View File

@ -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 */

View File

@ -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;
while (fwd) {
struct slirp_config_str *config;
if (strcmp(name, "hostfwd") != 0 && strcmp(name, "guestfwd") != 0) {
return 0;
config = g_malloc0(sizeof(*config));
pstrcpy(config->str, sizeof(config->str), fwd->value->str);
config->flags = flags;
config->next = slirp_configs;
slirp_configs = config;
fwd = fwd->next;
}
config = g_malloc0(sizeof(*config));
pstrcpy(config->str, sizeof(config->str), value);
if (!strcmp(name, "hostfwd")) {
config->flags = SLIRP_CFG_HOSTFWD;
}
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");
assert(opts->kind == NET_CLIENT_OPTIONS_KIND_USER);
user = opts->user;
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;
}
}
vnet = user->has_net ? g_strdup(user->net) :
user->has_ip ? g_strdup_printf("%s/24", user->ip) :
NULL;
if (qemu_opt_get(opts, "ip")) {
const char *ip = qemu_opt_get(opts, "ip");
int l = strlen(ip) + strlen("/24") + 1;
/* all optional fields are initialized to "all bits zero" */
vnet = g_malloc(l);
net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD);
net_init_slirp_configs(user->guestfwd, 0);
/* 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"));
}
qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 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;

View File

@ -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);

View File

@ -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")) {
const NetdevSocketOptions *sock;
assert(opts->kind == NET_CLIENT_OPTIONS_KIND_SOCKET);
sock = opts->socket;
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 (sock->has_localaddr && !sock->has_mcast && !sock->has_udp) {
error_report("localaddr= is only valid with mcast= or udp=");
return -1;
}
if (sock->has_fd) {
int fd;
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=");
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;
}
fd = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "fd"));
if (fd == -1) {
if (sock->has_listen) {
if (net_socket_listen_init(vlan, "socket", name, sock->listen) == -1) {
return -1;
}
return 0;
}
if (!net_socket_fd_init(vlan, "socket", name, fd, 1)) {
if (sock->has_connect) {
if (net_socket_connect_init(vlan, "socket", name, sock->connect) ==
-1) {
return -1;
}
} else if (qemu_opt_get(opts, "listen")) {
const char *listen;
return 0;
}
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=");
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;
}
listen = qemu_opt_get(opts, "listen");
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=");
return -1;
}
connect = qemu_opt_get(opts, "connect");
if (net_socket_connect_init(vlan, "socket", name, connect) == -1) {
return -1;
}
} else if (qemu_opt_get(opts, "mcast")) {
const char *mcast, *localaddr;
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=");
return -1;
}
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=");
return -1;
}
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;

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

152
net/tap.c
View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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' } }
@ -1871,6 +1871,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:
#

View File

@ -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

427
qapi/opts-visitor.c 100644
View File

@ -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;
}

View File

@ -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

View File

@ -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)) {

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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;
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_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);
}
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;
}
end:
visit_end_struct(m, errp);
''')
pop_indent()
ret += mcgen('''
/* Always call end_struct if start_struct succeeded. */
visit_end_struct(m, &err);
}
error_propagate(errp, err);
}
''')
pop_indent();
ret += mcgen('''
}
''')

View File

@ -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':

View File

@ -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();

View File

@ -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);
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);
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);
/* 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,