mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-08-26 18:41:17 +00:00
Support networking in UML driver
This commit is contained in:
parent
7cce4768ae
commit
c0d74ed43b
10
ChangeLog
10
ChangeLog
@ -1,3 +1,13 @@
|
|||||||
|
Wed Jun 3 12:03:52 BST 2009 Daniel P. Berrange <berrange@redhat.com>
|
||||||
|
|
||||||
|
Support networking in UML driver
|
||||||
|
* src/bridge.c: Add new brDeleteTap function. Allow brAddTap
|
||||||
|
to create a persistent tap devices.
|
||||||
|
* src/bridge.h, src/libvirt_bridge.syms: Add brDeleteTap
|
||||||
|
* src/domain_conf.c: Fix missing 'break' in network XML formatter
|
||||||
|
* src/uml_conf.c, src/uml_conf.h, src/uml_driver.c: Add support
|
||||||
|
for bridge, network, mcast and user mode network interfaces
|
||||||
|
|
||||||
Wed Jun 3 11:53:52 BST 2009 Daniel P. Berrange <berrange@redhat.com>
|
Wed Jun 3 11:53:52 BST 2009 Daniel P. Berrange <berrange@redhat.com>
|
||||||
|
|
||||||
Misc User Mode Linux startup/shutdown bugs
|
Misc User Mode Linux startup/shutdown bugs
|
||||||
|
52
src/bridge.c
52
src/bridge.c
@ -451,8 +451,11 @@ brProbeVnetHdr(int tapfd)
|
|||||||
*
|
*
|
||||||
* This function creates a new tap device on a bridge. @ifname can be either
|
* This function creates a new tap device on a bridge. @ifname can be either
|
||||||
* a fixed name or a name template with '%d' for dynamic name allocation.
|
* a fixed name or a name template with '%d' for dynamic name allocation.
|
||||||
* in either case the final name for the bridge will be stored in @ifname
|
* in either case the final name for the bridge will be stored in @ifname.
|
||||||
* and the associated file descriptor in @tapfd.
|
* If the @tapfd parameter is supplied, the open tap device file
|
||||||
|
* descriptor will be returned, otherwise the TAP device will be made
|
||||||
|
* persistent and closed. The caller must use brDeleteTap to remove
|
||||||
|
* a persistent TAP devices when it is no longer needed.
|
||||||
*
|
*
|
||||||
* Returns 0 in case of success or an errno code in case of failure.
|
* Returns 0 in case of success or an errno code in case of failure.
|
||||||
*/
|
*/
|
||||||
@ -465,7 +468,7 @@ brAddTap(brControl *ctl,
|
|||||||
{
|
{
|
||||||
int id, subst, fd;
|
int id, subst, fd;
|
||||||
|
|
||||||
if (!ctl || !ctl->fd || !bridge || !ifname || !tapfd)
|
if (!ctl || !ctl->fd || !bridge || !ifname)
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
|
||||||
subst = id = 0;
|
subst = id = 0;
|
||||||
@ -520,10 +523,14 @@ brAddTap(brControl *ctl,
|
|||||||
goto error;
|
goto error;
|
||||||
if ((errno = brSetInterfaceUp(ctl, try.ifr_name, 1)))
|
if ((errno = brSetInterfaceUp(ctl, try.ifr_name, 1)))
|
||||||
goto error;
|
goto error;
|
||||||
|
if (!tapfd &&
|
||||||
|
(errno = ioctl(fd, TUNSETPERSIST, 1)))
|
||||||
|
goto error;
|
||||||
VIR_FREE(*ifname);
|
VIR_FREE(*ifname);
|
||||||
if (!(*ifname = strdup(try.ifr_name)))
|
if (!(*ifname = strdup(try.ifr_name)))
|
||||||
goto error;
|
goto error;
|
||||||
*tapfd = fd;
|
if (tapfd)
|
||||||
|
*tapfd = fd;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,6 +543,43 @@ brAddTap(brControl *ctl,
|
|||||||
return errno;
|
return errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int brDeleteTap(brControl *ctl,
|
||||||
|
const char *ifname)
|
||||||
|
{
|
||||||
|
struct ifreq try;
|
||||||
|
int len;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (!ctl || !ctl->fd || !ifname)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
memset(&try, 0, sizeof(struct ifreq));
|
||||||
|
try.ifr_flags = IFF_TAP|IFF_NO_PI;
|
||||||
|
|
||||||
|
len = strlen(ifname);
|
||||||
|
if (len >= BR_IFNAME_MAXLEN - 1) {
|
||||||
|
errno = EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(try.ifr_name, ifname, len);
|
||||||
|
try.ifr_name[len] = '\0';
|
||||||
|
|
||||||
|
if (ioctl(fd, TUNSETIFF, &try) == 0) {
|
||||||
|
if ((errno = ioctl(fd, TUNSETPERSIST, 0)))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* brSetInterfaceUp:
|
* brSetInterfaceUp:
|
||||||
* @ctl: bridge control pointer
|
* @ctl: bridge control pointer
|
||||||
|
10
src/bridge.h
10
src/bridge.h
@ -60,12 +60,20 @@ int brDeleteInterface (brControl *ctl,
|
|||||||
const char *bridge,
|
const char *bridge,
|
||||||
const char *iface);
|
const char *iface);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
BR_TAP_VNET_HDR = (1 << 0),
|
||||||
|
BR_TAP_PERSIST = (1 << 1),
|
||||||
|
};
|
||||||
|
|
||||||
int brAddTap (brControl *ctl,
|
int brAddTap (brControl *ctl,
|
||||||
const char *bridge,
|
const char *bridge,
|
||||||
char **ifname,
|
char **ifname,
|
||||||
int vnet_hdr,
|
int features,
|
||||||
int *tapfd);
|
int *tapfd);
|
||||||
|
|
||||||
|
int brDeleteTap (brControl *ctl,
|
||||||
|
const char *ifname);
|
||||||
|
|
||||||
int brSetInterfaceUp (brControl *ctl,
|
int brSetInterfaceUp (brControl *ctl,
|
||||||
const char *ifname,
|
const char *ifname,
|
||||||
int up);
|
int up);
|
||||||
|
@ -3146,6 +3146,7 @@ virDomainNetDefFormat(virConnectPtr conn,
|
|||||||
else
|
else
|
||||||
virBufferVSprintf(buf, " <source port='%d'/>\n",
|
virBufferVSprintf(buf, " <source port='%d'/>\n",
|
||||||
def->data.socket.port);
|
def->data.socket.port);
|
||||||
|
break;
|
||||||
|
|
||||||
case VIR_DOMAIN_NET_TYPE_INTERNAL:
|
case VIR_DOMAIN_NET_TYPE_INTERNAL:
|
||||||
virBufferEscapeString(buf, " <source name='%s'/>\n",
|
virBufferEscapeString(buf, " <source name='%s'/>\n",
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
brAddBridge;
|
brAddBridge;
|
||||||
brAddInterface;
|
brAddInterface;
|
||||||
brAddTap;
|
brAddTap;
|
||||||
|
brDeleteTap;
|
||||||
brDeleteBridge;
|
brDeleteBridge;
|
||||||
brHasBridge;
|
brHasBridge;
|
||||||
brInit;
|
brInit;
|
||||||
|
187
src/uml_conf.c
187
src/uml_conf.c
@ -44,6 +44,7 @@
|
|||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "nodeinfo.h"
|
#include "nodeinfo.h"
|
||||||
#include "verify.h"
|
#include "verify.h"
|
||||||
|
#include "bridge.h"
|
||||||
|
|
||||||
#define VIR_FROM_THIS VIR_FROM_UML
|
#define VIR_FROM_THIS VIR_FROM_UML
|
||||||
|
|
||||||
@ -91,6 +92,172 @@ virCapsPtr umlCapsInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
umlConnectTapDevice(virConnectPtr conn,
|
||||||
|
virDomainNetDefPtr net,
|
||||||
|
const char *bridge)
|
||||||
|
{
|
||||||
|
int tapfd = -1;
|
||||||
|
int err;
|
||||||
|
brControl *brctl = NULL;
|
||||||
|
|
||||||
|
if (!net->ifname ||
|
||||||
|
STRPREFIX(net->ifname, "vnet") ||
|
||||||
|
strchr(net->ifname, '%')) {
|
||||||
|
VIR_FREE(net->ifname);
|
||||||
|
if (!(net->ifname = strdup("vnet%d")))
|
||||||
|
goto no_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = brInit(&brctl))) {
|
||||||
|
char ebuf[1024];
|
||||||
|
umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("cannot initialize bridge support: %s"),
|
||||||
|
virStrerror(err, ebuf, sizeof ebuf));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = brAddTap(brctl, bridge,
|
||||||
|
&net->ifname, BR_TAP_PERSIST, &tapfd))) {
|
||||||
|
if (errno == ENOTSUP) {
|
||||||
|
/* In this particular case, give a better diagnostic. */
|
||||||
|
umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("Failed to add tap interface to bridge. "
|
||||||
|
"%s is not a bridge device"), bridge);
|
||||||
|
} else {
|
||||||
|
char ebuf[1024];
|
||||||
|
umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("Failed to add tap interface '%s' "
|
||||||
|
"to bridge '%s' : %s"),
|
||||||
|
net->ifname, bridge, virStrerror(err, ebuf, sizeof ebuf));
|
||||||
|
}
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
close(tapfd);
|
||||||
|
|
||||||
|
brShutdown(brctl);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
no_memory:
|
||||||
|
virReportOOMError(conn);
|
||||||
|
error:
|
||||||
|
brShutdown(brctl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
umlBuildCommandLineNet(virConnectPtr conn,
|
||||||
|
virDomainNetDefPtr def,
|
||||||
|
int idx)
|
||||||
|
{
|
||||||
|
char *ret;
|
||||||
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||||
|
|
||||||
|
/* General format: ethNN=type,options */
|
||||||
|
|
||||||
|
virBufferVSprintf(&buf, "eth%d=", idx);
|
||||||
|
|
||||||
|
switch (def->type) {
|
||||||
|
case VIR_DOMAIN_NET_TYPE_USER:
|
||||||
|
/* ethNNN=slirp,macaddr */
|
||||||
|
virBufferAddLit(&buf, "slirp");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_DOMAIN_NET_TYPE_ETHERNET:
|
||||||
|
/* ethNNN=tuntap,tapname,macaddr,gateway */
|
||||||
|
virBufferAddLit(&buf, "tuntap");
|
||||||
|
if (def->data.ethernet.ipaddr) {
|
||||||
|
umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
|
_("IP address not supported for ethernet inteface"));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (def->data.ethernet.script) {
|
||||||
|
umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
|
_("script execution not supported for ethernet inteface"));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_DOMAIN_NET_TYPE_SERVER:
|
||||||
|
umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
|
_("TCP server networking type not supported"));
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
case VIR_DOMAIN_NET_TYPE_CLIENT:
|
||||||
|
umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
|
_("TCP client networking type not supported"));
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
case VIR_DOMAIN_NET_TYPE_MCAST:
|
||||||
|
/* ethNNN=tuntap,macaddr,ipaddr,port */
|
||||||
|
virBufferAddLit(&buf, "mcast");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_DOMAIN_NET_TYPE_NETWORK:
|
||||||
|
{
|
||||||
|
char *bridge;
|
||||||
|
virNetworkPtr network = virNetworkLookupByName(conn,
|
||||||
|
def->data.network.name);
|
||||||
|
if (!network) {
|
||||||
|
umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("Network '%s' not found"),
|
||||||
|
def->data.network.name);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
bridge = virNetworkGetBridgeName(network);
|
||||||
|
virNetworkFree(network);
|
||||||
|
if (bridge == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (umlConnectTapDevice(conn, def, bridge) < 0) {
|
||||||
|
VIR_FREE(bridge);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ethNNN=tuntap,tapname,macaddr,gateway */
|
||||||
|
virBufferVSprintf(&buf, "tuntap,%s", def->ifname);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VIR_DOMAIN_NET_TYPE_BRIDGE:
|
||||||
|
if (umlConnectTapDevice(conn, def, def->data.bridge.brname) < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* ethNNN=tuntap,tapname,macaddr,gateway */
|
||||||
|
virBufferVSprintf(&buf, "tuntap,%s", def->ifname);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_DOMAIN_NET_TYPE_INTERNAL:
|
||||||
|
umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
|
_("internal networking type not supported"));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
virBufferVSprintf(&buf, ",%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
def->mac[0], def->mac[1], def->mac[2],
|
||||||
|
def->mac[3], def->mac[4], def->mac[5]);
|
||||||
|
|
||||||
|
if (def->type == VIR_DOMAIN_NET_TYPE_MCAST) {
|
||||||
|
virBufferVSprintf(&buf, ",%s,%d",
|
||||||
|
def->data.socket.address,
|
||||||
|
def->data.socket.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virBufferError(&buf)) {
|
||||||
|
virReportOOMError(conn);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return virBufferContentAndReset(&buf);
|
||||||
|
|
||||||
|
error:
|
||||||
|
ret = virBufferContentAndReset(&buf);
|
||||||
|
VIR_FREE(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
umlBuildCommandLineChr(virConnectPtr conn,
|
umlBuildCommandLineChr(virConnectPtr conn,
|
||||||
virDomainChrDefPtr def,
|
virDomainChrDefPtr def,
|
||||||
@ -166,9 +333,8 @@ int umlBuildCommandLine(virConnectPtr conn,
|
|||||||
struct uml_driver *driver ATTRIBUTE_UNUSED,
|
struct uml_driver *driver ATTRIBUTE_UNUSED,
|
||||||
virDomainObjPtr vm,
|
virDomainObjPtr vm,
|
||||||
const char ***retargv,
|
const char ***retargv,
|
||||||
const char ***retenv,
|
const char ***retenv)
|
||||||
int **tapfds,
|
{
|
||||||
int *ntapfds) {
|
|
||||||
int i, j;
|
int i, j;
|
||||||
char memory[50];
|
char memory[50];
|
||||||
struct utsname ut;
|
struct utsname ut;
|
||||||
@ -277,6 +443,13 @@ int umlBuildCommandLine(virConnectPtr conn,
|
|||||||
ADD_ARG_PAIR(disk->dst, disk->src);
|
ADD_ARG_PAIR(disk->dst, disk->src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0 ; i < vm->def->nnets ; i++) {
|
||||||
|
char *ret = umlBuildCommandLineNet(conn, vm->def->nets[i], i);
|
||||||
|
if (!ret)
|
||||||
|
goto error;
|
||||||
|
ADD_ARG(ret);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0 ; i < UML_MAX_CHAR_DEVICE ; i++) {
|
for (i = 0 ; i < UML_MAX_CHAR_DEVICE ; i++) {
|
||||||
char *ret;
|
char *ret;
|
||||||
if (i == 0 && vm->def->console)
|
if (i == 0 && vm->def->console)
|
||||||
@ -311,13 +484,7 @@ int umlBuildCommandLine(virConnectPtr conn,
|
|||||||
no_memory:
|
no_memory:
|
||||||
virReportOOMError(conn);
|
virReportOOMError(conn);
|
||||||
error:
|
error:
|
||||||
if (tapfds &&
|
|
||||||
*tapfds) {
|
|
||||||
for (i = 0; i < *ntapfds; i++)
|
|
||||||
close((*tapfds)[i]);
|
|
||||||
VIR_FREE(*tapfds);
|
|
||||||
*ntapfds = 0;
|
|
||||||
}
|
|
||||||
if (qargv) {
|
if (qargv) {
|
||||||
for (i = 0 ; i < qargc ; i++)
|
for (i = 0 ; i < qargc ; i++)
|
||||||
VIR_FREE((qargv)[i]);
|
VIR_FREE((qargv)[i]);
|
||||||
|
@ -70,8 +70,6 @@ int umlBuildCommandLine (virConnectPtr conn,
|
|||||||
struct uml_driver *driver,
|
struct uml_driver *driver,
|
||||||
virDomainObjPtr dom,
|
virDomainObjPtr dom,
|
||||||
const char ***retargv,
|
const char ***retargv,
|
||||||
const char ***retenv,
|
const char ***retenv);
|
||||||
int **tapfds,
|
|
||||||
int *ntapfds);
|
|
||||||
|
|
||||||
#endif /* __UML_CONF_H */
|
#endif /* __UML_CONF_H */
|
||||||
|
@ -722,6 +722,35 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int umlCleanupTapDevices(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||||
|
virDomainObjPtr vm) {
|
||||||
|
int i;
|
||||||
|
int err;
|
||||||
|
int ret = 0;
|
||||||
|
brControl *brctl = NULL;
|
||||||
|
VIR_ERROR0("Cleanup tap");
|
||||||
|
if (brInit(&brctl) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (i = 0 ; i < vm->def->nnets ; i++) {
|
||||||
|
virDomainNetDefPtr def = vm->def->nets[i];
|
||||||
|
|
||||||
|
if (def->type != VIR_DOMAIN_NET_TYPE_BRIDGE &&
|
||||||
|
def->type != VIR_DOMAIN_NET_TYPE_NETWORK)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
VIR_ERROR("Cleanup '%s'", def->ifname);
|
||||||
|
err = brDeleteTap(brctl, def->ifname);
|
||||||
|
if (err) {
|
||||||
|
VIR_ERROR("Cleanup failed %d", err);
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VIR_ERROR0("Cleanup tap done");
|
||||||
|
brShutdown(brctl);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int umlStartVMDaemon(virConnectPtr conn,
|
static int umlStartVMDaemon(virConnectPtr conn,
|
||||||
struct uml_driver *driver,
|
struct uml_driver *driver,
|
||||||
virDomainObjPtr vm) {
|
virDomainObjPtr vm) {
|
||||||
@ -732,8 +761,6 @@ static int umlStartVMDaemon(virConnectPtr conn,
|
|||||||
char *logfile;
|
char *logfile;
|
||||||
int logfd = -1;
|
int logfd = -1;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
int *tapfds = NULL;
|
|
||||||
int ntapfds = 0;
|
|
||||||
fd_set keepfd;
|
fd_set keepfd;
|
||||||
char ebuf[1024];
|
char ebuf[1024];
|
||||||
|
|
||||||
@ -792,9 +819,9 @@ static int umlStartVMDaemon(virConnectPtr conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (umlBuildCommandLine(conn, driver, vm,
|
if (umlBuildCommandLine(conn, driver, vm,
|
||||||
&argv, &progenv,
|
&argv, &progenv) < 0) {
|
||||||
&tapfds, &ntapfds) < 0) {
|
|
||||||
close(logfd);
|
close(logfd);
|
||||||
|
umlCleanupTapDevices(conn, vm);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -824,9 +851,6 @@ static int umlStartVMDaemon(virConnectPtr conn,
|
|||||||
|
|
||||||
vm->monitor = -1;
|
vm->monitor = -1;
|
||||||
|
|
||||||
for (i = 0 ; i < ntapfds ; i++)
|
|
||||||
FD_SET(tapfds[i], &keepfd);
|
|
||||||
|
|
||||||
ret = virExecDaemonize(conn, argv, progenv, &keepfd, &pid,
|
ret = virExecDaemonize(conn, argv, progenv, &keepfd, &pid,
|
||||||
-1, &logfd, &logfd,
|
-1, &logfd, &logfd,
|
||||||
0, NULL, NULL, NULL);
|
0, NULL, NULL, NULL);
|
||||||
@ -840,15 +864,14 @@ static int umlStartVMDaemon(virConnectPtr conn,
|
|||||||
VIR_FREE(progenv[i]);
|
VIR_FREE(progenv[i]);
|
||||||
VIR_FREE(progenv);
|
VIR_FREE(progenv);
|
||||||
|
|
||||||
if (tapfds) {
|
if (ret < 0)
|
||||||
for (i = 0 ; i < ntapfds ; i++) {
|
umlCleanupTapDevices(conn, vm);
|
||||||
close(tapfds[i]);
|
|
||||||
}
|
|
||||||
VIR_FREE(tapfds);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NB we don't mark it running here - we do that async
|
/* NB we don't mark it running here - we do that async
|
||||||
with inotify */
|
with inotify */
|
||||||
|
/* XXX what if someone else tries to start it again
|
||||||
|
before we get the inotification ? Sounds like
|
||||||
|
trouble.... */
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -879,6 +902,8 @@ static void umlShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|||||||
VIR_FREE(vm->vcpupids);
|
VIR_FREE(vm->vcpupids);
|
||||||
vm->nvcpupids = 0;
|
vm->nvcpupids = 0;
|
||||||
|
|
||||||
|
umlCleanupTapDevices(conn, vm);
|
||||||
|
|
||||||
if (vm->newDef) {
|
if (vm->newDef) {
|
||||||
virDomainDefFree(vm->def);
|
virDomainDefFree(vm->def);
|
||||||
vm->def = vm->newDef;
|
vm->def = vm->newDef;
|
||||||
|
Loading…
Reference in New Issue
Block a user