[libvirt] [PATCH] Fix job type set in qemuMigrationPrepareDirect/PrepareTunnel
by Daniel P. Berrange
The qemuMigrationPrepareDirect/PrepareTunnel methods accidentally
set the domain job to QEMU_JOB_MIGRATION_OUT when it should have
been QEMU_JOB_MIGRATION_IN. This didn't have any ill-effect, but
it is none-the-less wrong.
* src/qemu/qemu_migration.c: Fix job type
---
src/qemu/qemu_migration.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 78acffb..ca4a884 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -1110,7 +1110,7 @@ qemuMigrationPrepareTunnel(struct qemud_driver *driver,
if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
goto cleanup;
- priv->jobActive = QEMU_JOB_MIGRATION_OUT;
+ priv->jobActive = QEMU_JOB_MIGRATION_IN;
/* Domain starts inactive, even if the domain XML had an id field. */
vm->def->id = -1;
@@ -1345,7 +1345,7 @@ qemuMigrationPrepareDirect(struct qemud_driver *driver,
if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
goto cleanup;
- priv->jobActive = QEMU_JOB_MIGRATION_OUT;
+ priv->jobActive = QEMU_JOB_MIGRATION_IN;
/* Domain starts inactive, even if the domain XML had an id field. */
vm->def->id = -1;
--
1.7.4.4
13 years, 5 months
[libvirt] [PATCH] Avoid high privileges taint warning for QEMU session driver
by Daniel P. Berrange
The code emitting taint warnings was mistakenly thinking
that guests run from the QEMU session driver were tainted
for having high privileges. This is of course nonsense
since the session driver is always unprivileged
* src/qemu/qemu_domain.c: Don't warn for high privileges in
non-privileged QEMU
---
src/qemu/qemu_domain.c | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index fab316f..3af1c86 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -788,9 +788,10 @@ void qemuDomainObjCheckTaint(struct qemud_driver *driver,
{
int i;
- if (!driver->clearEmulatorCapabilities ||
- driver->user == 0 ||
- driver->group == 0)
+ if (driver->privileged &&
+ (!driver->clearEmulatorCapabilities ||
+ driver->user == 0 ||
+ driver->group == 0))
qemuDomainObjTaint(driver, obj, VIR_DOMAIN_TAINT_HIGH_PRIVILEGES, logFD);
if (obj->def->namespaceData) {
--
1.7.4.4
13 years, 5 months
[libvirt] [PATCH 0/2] Replace all remaining setgid calls with virSetUIDGID
by Jiri Denemark
Should we also ban setgid/setuid usage since we probably want to call
initgroups after them anyway?
Jiri Denemark (2):
util: Keep errno set to the root error after when returning from
virSetUIDGID
Replace all remaining setgid/setuid calls with virSetUIDGID
src/storage/storage_backend.c | 15 +----------
src/util/util.c | 53 +++++++++++++++++------------------------
2 files changed, 24 insertions(+), 44 deletions(-)
--
1.7.5.rc3
13 years, 5 months
[libvirt] [PATCH RFC] Allow a client to connect to QEMU's VNC by passing an FD via libvirt
by Daniel P. Berrange
This patch implements a new API which allows a libvirt client
application to connect to QEMU's VNC server, by passing a FD
from an anonymous socketpair, to libvirt, which then passes it
onto QEMU via the monitor. This obviously only works if the
client app is talking to a local libvirt over UNIX sockets,
not for any remote apps using TCP, or SSH tunnel.
typedef enum {
VIR_DOMAIN_CONNECT_GRAPHICS_SKIPAUTH = (1 << 0),
} virDomainConnectGraphicsFlags;
int virDomainConnectGraphics(virDomainPtr dom,
const char *devname,
int fd,
unsigned int flags);
The QEMU monitor command I proposed actually allo2s
conecting to VNC or SPICE or any character device,
so I might rename this API to replace 'Graphics'
with 'Client' or something. Or we could add a
separate API for connecting to character devices.
This does a semi-nasty hack to the remote protocol, defining
a new client -> server message type: REMOTE_CALL_WITH_FD
which is identical to REMOTE_CALL, but a UNIX FD is passed
between the header & payload. The reason I call this nasty
is that it only allows for a single FD to be passed per
RPC call. This seems like a reasonable restriction and it
makes the code utterly trivial now, but I'm afraid it might
bite us later.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h:
Add support for 'client_add' monitor command with FD passing
* src/qemu/qemu_driver.c: Implement the new virDomainConnectGraphics
API call
* include/libvirt/libvirt.h.in, src/driver.h,
src/libvirt_public.syms, src/libvirt.c: Define a new
virDomainConnectGraphics API
* daemon/dispatch.c, daemon/libvirtd.c, daemon/libvirtd.h,
daemon/remote.c, src/remote/remote_driver.c,
src/remote/remote_protocol.x: Support for FD passing
and the virDomainConnectGraphics API
---
daemon/dispatch.c | 14 +++++-
daemon/libvirtd.c | 13 +++++
daemon/libvirtd.h | 4 ++
daemon/remote.c | 44 ++++++++++++++++++
include/libvirt/libvirt.h.in | 14 +++++-
src/driver.h | 6 +++
src/libvirt.c | 50 ++++++++++++++++++++
src/libvirt_private.syms | 3 +
src/libvirt_public.syms | 1 +
src/qemu/qemu_driver.c | 59 ++++++++++++++++++++++++
src/qemu/qemu_monitor.c | 32 +++++++++++++
src/qemu/qemu_monitor.h | 6 +++
src/qemu/qemu_monitor_json.c | 31 ++++++++++++-
src/qemu/qemu_monitor_json.h | 5 ++
src/qemu/qemu_monitor_text.c | 31 +++++++++++++
src/qemu/qemu_monitor_text.h | 5 ++
src/remote/remote_driver.c | 102 +++++++++++++++++++++++++++++++++++++++++-
src/remote/remote_protocol.x | 23 +++++++++-
18 files changed, 434 insertions(+), 9 deletions(-)
diff --git a/daemon/dispatch.c b/daemon/dispatch.c
index 010be1e..723b1c7 100644
--- a/daemon/dispatch.c
+++ b/daemon/dispatch.c
@@ -29,9 +29,11 @@
#include "dispatch.h"
#include "remote.h"
-
+#include "files.h"
#include "memory.h"
+virThreadLocal clientFD;
+
/* Convert a libvirt virError object into wire format */
static void
remoteDispatchCopyError (remote_error *rerr,
@@ -391,6 +393,7 @@ remoteDispatchClientRequest(struct qemud_server *server,
switch (msg->hdr.type) {
case REMOTE_CALL:
+ case REMOTE_CALL_WITH_FD:
return remoteDispatchClientCall(server, client, msg, qemu_call);
case REMOTE_STREAM:
@@ -503,6 +506,10 @@ remoteDispatchClientCall (struct qemud_server *server,
conn = client->conn;
virMutexUnlock(&client->lock);
+ if (msg->hdr.type == REMOTE_CALL_WITH_FD) {
+ virThreadLocalSet(&clientFD, &msg->fd);
+ }
+
/*
* When the RPC handler is called:
*
@@ -515,6 +522,11 @@ remoteDispatchClientCall (struct qemud_server *server,
*/
rv = (data->fn)(server, client, conn, &msg->hdr, &rerr, &args, &ret);
+ if (msg->hdr.type == REMOTE_CALL_WITH_FD) {
+ virThreadLocalSet(&clientFD, NULL);
+ VIR_FORCE_CLOSE(msg->fd);
+ }
+
virMutexLock(&server->lock);
virMutexLock(&client->lock);
virMutexUnlock(&server->lock);
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
index 5f291ec..6d6a44b 100644
--- a/daemon/libvirtd.c
+++ b/daemon/libvirtd.c
@@ -67,6 +67,7 @@
#include "stream.h"
#include "hooks.h"
#include "virtaudit.h"
+#include "passfd.h"
#ifdef HAVE_AVAHI
# include "mdns.h"
#endif
@@ -875,6 +876,12 @@ static struct qemud_server *qemudInitialize(void) {
return NULL;
}
+ if (virThreadLocalInit(&clientFD, NULL) < 0) {
+ VIR_ERROR(_("Failed to initialize thread local"));
+ VIR_FREE(server);
+ return NULL;
+ }
+
server->privileged = geteuid() == 0 ? 1 : 0;
server->sigread = server->sigwrite = -1;
@@ -1882,6 +1889,12 @@ readmore:
qemudDispatchClientFailure(client);
}
+ if (msg->hdr.type == REMOTE_CALL_WITH_FD) {
+ VIR_ERROR("Trying to get FD");
+ msg->fd = recvfd(client->fd, O_CLOEXEC);
+ VIR_ERROR("Got %d", msg->fd);
+ }
+
/* Check if any filters match this message */
filter = client->filters;
while (filter) {
diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h
index ea00d5c..7866067 100644
--- a/daemon/libvirtd.h
+++ b/daemon/libvirtd.h
@@ -147,6 +147,8 @@ struct qemud_client_message {
remote_message_header hdr;
+ int fd;
+
struct qemud_client_message *next;
};
@@ -304,6 +306,8 @@ struct qemud_server {
# endif
};
+extern virThreadLocal clientFD;
+
void qemudLog(int priority, const char *fmt, ...)
ATTRIBUTE_FMT_PRINTF(2,3);
diff --git a/daemon/remote.c b/daemon/remote.c
index 37fbed0..700e12c 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -2778,6 +2778,50 @@ cleanup:
}
+static int
+remoteDispatchDomainConnectGraphics(struct qemud_server *server ATTRIBUTE_UNUSED,
+ struct qemud_client *client ATTRIBUTE_UNUSED,
+ virConnectPtr conn,
+ remote_message_header *hdr ATTRIBUTE_UNUSED,
+ remote_error *rerr,
+ remote_domain_connect_graphics_args *args,
+ void *ret ATTRIBUTE_UNUSED)
+{
+ virDomainPtr dom = NULL;
+ int *fd;
+ int rv = -1;
+
+ if (!conn) {
+ virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+ goto cleanup;
+ }
+
+ if (!(dom = get_nonnull_domain(conn, args->dom)))
+ goto cleanup;
+
+ fd = virThreadLocalGet(&clientFD);
+ if (!fd || *fd == -1) {
+ virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing file descriptor"));
+ goto cleanup;
+ }
+
+ if (virDomainConnectGraphics(dom,
+ args->devicestr,
+ *fd,
+ args->flags) < 0)
+ goto cleanup;
+
+ rv = 0;
+
+cleanup:
+ if (rv < 0)
+ remoteDispatchError(rerr);
+ if (dom)
+ virDomainFree(dom);
+ return rv;
+}
+
+
#include "remote_dispatch_bodies.h"
#include "qemu_dispatch_bodies.h"
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 3f634e6..49b8fc1 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -2654,6 +2654,7 @@ typedef enum {
typedef enum {
VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, /* IPv4 address */
VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, /* IPv6 address */
+ VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_UNIX, /* UNIX socket path */
} virDomainEventGraphicsAddressType;
@@ -2665,8 +2666,8 @@ typedef enum {
*/
struct _virDomainEventGraphicsAddress {
int family; /* Address family, virDomainEventGraphicsAddressType */
- const char *node; /* Address of node (eg IP address) */
- const char *service; /* Service name/number (eg TCP port) */
+ const char *node; /* Address of node (eg IP address, or UNIX path) */
+ const char *service; /* Service name/number (eg TCP port, or NULL) */
};
typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress;
typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr;
@@ -2862,6 +2863,15 @@ int virDomainOpenConsole(virDomainPtr dom,
virStreamPtr st,
unsigned int flags);
+typedef enum {
+ VIR_DOMAIN_CONNECT_GRAPHICS_SKIPAUTH = (1 << 0),
+} virDomainConnectGraphicsFlags;
+
+int virDomainConnectGraphics(virDomainPtr dom,
+ const char *devname,
+ int fd,
+ unsigned int flags);
+
int virDomainInjectNMI(virDomainPtr domain, unsigned int flags);
diff --git a/src/driver.h b/src/driver.h
index 62bbc1d..cfa4a00 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -564,6 +564,11 @@ typedef int
const char *devname,
virStreamPtr st,
unsigned int flags);
+typedef int
+ (*virDrvDomainConnectGraphics)(virDomainPtr dom,
+ const char *devname,
+ int fd,
+ unsigned int flags);
typedef int
(*virDrvDomainInjectNMI)(virDomainPtr dom, unsigned int flags);
@@ -797,6 +802,7 @@ struct _virDriver {
virDrvDomainSnapshotDelete domainSnapshotDelete;
virDrvDomainQemuMonitorCommand qemuDomainMonitorCommand;
virDrvDomainOpenConsole domainOpenConsole;
+ virDrvDomainConnectGraphics domainConnectGraphics;
virDrvDomainInjectNMI domainInjectNMI;
virDrvDomainMigrateBegin3 domainMigrateBegin3;
virDrvDomainMigratePrepare3 domainMigratePrepare3;
diff --git a/src/libvirt.c b/src/libvirt.c
index c57e0c3..bbd4157 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -15548,3 +15548,53 @@ error:
virDispatchError(dom->conn);
return -1;
}
+
+int virDomainConnectGraphics(virDomainPtr dom,
+ const char *devname,
+ int fd,
+ unsigned int flags)
+{
+ virConnectPtr conn;
+
+ VIR_DOMAIN_DEBUG(dom, "devname=%s, fd=%d, flags=%u",
+ NULLSTR(devname), fd, flags);
+
+ virResetLastError();
+
+ if (!VIR_IS_DOMAIN(dom)) {
+ virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+ virDispatchError(NULL);
+ return -1;
+ }
+
+ conn = dom->conn;
+
+ if (devname == NULL) {
+ virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+ goto error;
+ }
+
+ if (fd < 0) {
+ virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__);
+ goto error;
+ }
+
+ if (dom->conn->flags & VIR_CONNECT_RO) {
+ virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__);
+ goto error;
+ }
+
+ if (dom->conn->driver->domainConnectGraphics) {
+ int ret;
+ ret = dom->conn->driver->domainConnectGraphics(dom, devname, fd, flags);
+ if (ret < 0)
+ goto error;
+ return ret;
+ }
+
+ virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+ virDispatchError(dom->conn);
+ return -1;
+}
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 6f253ab..24f811b 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -950,6 +950,9 @@ virThreadIsSelf;
virThreadJoin;
virThreadSelf;
virThreadSelfID;
+virThreadLocalGet;
+virThreadLocalInit;
+virThreadLocalSet;
# usb.h
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index 39d4cae..2f4aa0b 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -467,6 +467,7 @@ LIBVIRT_0.9.3 {
virEventUpdateTimeout;
virNodeGetCPUStats;
virNodeGetMemoryStats;
+ virDomainConnectGraphics;
} LIBVIRT_0.9.2;
# .... define new API here using predicted next version number ....
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 01587e8..cdbd2e6 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -8314,6 +8314,64 @@ qemuDomainGetBlockPullInfo(virDomainPtr dom, const char *path,
return qemuDomainBlockPullImpl(dom, path, info, BLOCK_PULL_MODE_INFO);
}
+static int
+qemuDomainConnectGraphics(virDomainPtr dom,
+ const char *devname,
+ int fd,
+ unsigned int flags)
+{
+ struct qemud_driver *driver = dom->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ int ret = -1;
+ qemuDomainObjPrivatePtr priv;
+
+ virCheckFlags(VIR_DOMAIN_CONNECT_GRAPHICS_SKIPAUTH, -1);
+
+ qemuDriverLock(driver);
+ virUUIDFormat(dom->uuid, uuidstr);
+ vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ if (!vm) {
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"), uuidstr);
+ goto cleanup;
+ }
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto cleanup;
+ }
+
+ priv = vm->privateData;
+
+ if (STRNEQ(devname, "vnc") &&
+ STRNEQ(devname, "spice")) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot find graphics device %s"),
+ devname);
+ goto cleanup;
+ }
+
+ if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
+ goto cleanup;
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ ret = qemuMonitorConnectGraphics(priv->mon, devname, fd, "graphicsfd",
+ flags & VIR_DOMAIN_CONNECT_GRAPHICS_SKIPAUTH);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ if (qemuDomainObjEndJob(vm) == 0) {
+ vm = NULL;
+ goto cleanup;
+ }
+
+cleanup:
+ if (vm)
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
+ return ret;
+}
+
+
static virDriver qemuDriver = {
.no = VIR_DRV_QEMU,
.name = "QEMU",
@@ -8429,6 +8487,7 @@ static virDriver qemuDriver = {
.domainSnapshotDelete = qemuDomainSnapshotDelete, /* 0.8.0 */
.qemuDomainMonitorCommand = qemuDomainMonitorCommand, /* 0.8.3 */
.domainOpenConsole = qemuDomainOpenConsole, /* 0.8.6 */
+ .domainConnectGraphics = qemuDomainConnectGraphics, /* 0.9.2 */
.domainInjectNMI = qemuDomainInjectNMI, /* 0.9.2 */
.domainMigrateBegin3 = qemuDomainMigrateBegin3, /* 0.9.2 */
.domainMigratePrepare3 = qemuDomainMigratePrepare3, /* 0.9.2 */
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 89a3f64..8259b44 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -2404,3 +2404,35 @@ int qemuMonitorBlockPull(qemuMonitorPtr mon,
ret = qemuMonitorTextBlockPull(mon, path, info, mode);
return ret;
}
+
+int qemuMonitorConnectGraphics(qemuMonitorPtr mon,
+ const char *devicestr,
+ int fd,
+ const char *fdname,
+ bool skipauth)
+{
+ VIR_DEBUG("mon=%p device=%s fd=%d fdname=%s skipauth=%d",
+ mon, devicestr, fd, NULLSTR(fdname), skipauth);
+ int ret;
+
+ if (!mon) {
+ qemuReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("monitor must not be NULL"));
+ return -1;
+ }
+
+ if (qemuMonitorSendFileHandle(mon, fdname, fd) < 0)
+ return -1;
+
+ if (mon->json)
+ ret = qemuMonitorJSONConnectGraphics(mon, devicestr, fdname, skipauth);
+ else
+ ret = qemuMonitorTextConnectGraphics(mon, devicestr, fdname, skipauth);
+
+ if (ret < 0) {
+ if (qemuMonitorCloseFileHandle(mon, fdname) < 0)
+ VIR_WARN("failed to close device handle '%s'", fdname);
+ }
+
+ return ret;
+}
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 3bb0269..3fc6ec6 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -462,6 +462,12 @@ int qemuMonitorBlockPull(qemuMonitorPtr mon,
virDomainBlockPullInfoPtr info,
int mode);
+int qemuMonitorConnectGraphics(qemuMonitorPtr mon,
+ const char *devicestr,
+ int fd,
+ const char *fdname,
+ bool skipauth);
+
/**
* When running two dd process and using <> redirection, we need a
* shell that will not truncate files. These two strings serve that
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 56ec65b..f8bac98 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -615,8 +615,8 @@ static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr dat
VIR_ENUM_DECL(qemuMonitorGraphicsAddressFamily)
-VIR_ENUM_IMPL(qemuMonitorGraphicsAddressFamily, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6 + 1,
- "ipv4", "ipv6");
+VIR_ENUM_IMPL(qemuMonitorGraphicsAddressFamily, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_UNIX + 1,
+ "ipv4", "ipv6", "unix");
static void qemuMonitorJSONHandleVNC(qemuMonitorPtr mon, virJSONValuePtr data, int phase)
{
@@ -2814,3 +2814,30 @@ int qemuMonitorJSONBlockPull(qemuMonitorPtr mon,
virJSONValueFree(reply);
return ret;
}
+
+int qemuMonitorJSONConnectGraphics(qemuMonitorPtr mon,
+ const char *devicestr,
+ const char *fdname,
+ bool skipauth)
+{
+ int ret;
+ virJSONValuePtr cmd, reply = NULL;
+
+ cmd = qemuMonitorJSONMakeCommand("add_client",
+ "s:protocol", devicestr,
+ "s:fdname", fdname,
+ "b:skipauth", skipauth,
+ NULL);
+
+ if (!cmd)
+ return -1;
+
+ ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+ if (ret == 0)
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 393d8fc..468acb8 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -222,4 +222,9 @@ int qemuMonitorJSONBlockPull(qemuMonitorPtr mon,
virDomainBlockPullInfoPtr info,
int mode);
+int qemuMonitorJSONConnectGraphics(qemuMonitorPtr mon,
+ const char *devicestr,
+ const char *fdname,
+ bool skipauth);
+
#endif /* QEMU_MONITOR_JSON_H */
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index a16ea91..f11b58f 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -2906,3 +2906,34 @@ cleanup:
VIR_FREE(reply);
return ret;
}
+
+int qemuMonitorTextConnectGraphics(qemuMonitorPtr mon,
+ const char *devicestr,
+ const char *fdname,
+ bool skipauth)
+{
+ char *cmd = NULL;
+ char *reply = NULL;
+ int ret = -1;
+
+ if (virAsprintf(&cmd, "add_client %s %s %d", devicestr, fdname, skipauth ? 0 : 1) < 0){
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ "%s", _("adding graphics client failed"));
+ goto cleanup;
+ }
+
+ if (STRNEQ(reply, ""))
+ goto cleanup;
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(reply);
+ VIR_FREE(cmd);
+ return ret;
+}
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index 4fa5064..12efe4f 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -215,4 +215,9 @@ int qemuMonitorTextBlockPull(qemuMonitorPtr mon,
virDomainBlockPullInfoPtr info,
int mode);
+int qemuMonitorTextConnectGraphics(qemuMonitorPtr mon,
+ const char *devicestr,
+ const char *fdname,
+ bool skipauth);
+
#endif /* QEMU_MONITOR_TEXT_H */
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index d7ce76e..688e65d 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -83,6 +83,7 @@
#include "ignore-value.h"
#include "files.h"
#include "command.h"
+#include "passfd.h"
#define VIR_FROM_THIS VIR_FROM_REMOTE
@@ -128,6 +129,8 @@ struct remote_thread_call {
unsigned int serial;
unsigned int proc_nr;
+ int fd;
+
virCond cond;
int want_reply;
@@ -173,6 +176,7 @@ struct private_data {
int errfd; /* File handle connected to remote stderr */
int watch; /* File handle watch */
pid_t pid; /* PID of tunnel process */
+ int hasSendFD; /* Whether sendfd is possible */
int uses_tls; /* TLS enabled on socket? */
int is_secure; /* Secure if TLS or SASL or UNIX sockets */
gnutls_session_t session; /* GnuTLS session (if uses_tls != 0). */
@@ -240,6 +244,10 @@ static int call (virConnectPtr conn, struct private_data *priv,
int flags, int proc_nr,
xdrproc_t args_filter, char *args,
xdrproc_t ret_filter, char *ret);
+static int callWithFD(virConnectPtr conn, struct private_data *priv,
+ int flags, int fd, int proc_nr,
+ xdrproc_t args_filter, char *args,
+ xdrproc_t ret_filter, char *ret);
static int remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open,
virConnectAuthPtr auth, const char *authtype);
#if HAVE_SASL
@@ -722,6 +730,7 @@ doRemoteOpen (virConnectPtr conn,
addr.sun_path[0] = '\0';
autostart_retry:
+ priv->hasSendFD = 1;
priv->is_secure = 1;
priv->sock = socket (AF_UNIX, SOCK_STREAM, 0);
if (priv->sock == -1) {
@@ -4603,6 +4612,38 @@ done:
return rv;
}
+
+static int
+remoteDomainConnectGraphics(virDomainPtr dom,
+ const char *devicestr,
+ int fd,
+ unsigned int flags)
+{
+ struct private_data *priv = dom->conn->privateData;
+ int rv = -1;
+ remote_domain_connect_graphics_args args;
+
+ remoteDriverLock(priv);
+
+ make_nonnull_domain (&args.dom, dom);
+ args.devicestr = (char*)devicestr;
+ args.flags = flags;
+
+ if (callWithFD(dom->conn, priv, 0, fd, REMOTE_PROC_DOMAIN_CONNECT_GRAPHICS,
+ (xdrproc_t) xdr_remote_domain_connect_graphics_args, (char *) &args,
+ (xdrproc_t) xdr_void, NULL) == -1) {
+ goto done;
+ }
+
+ rv = 0;
+
+done:
+ remoteDriverUnlock(priv);
+
+ return rv;
+}
+
+
/*----------------------------------------------------------------------*/
static int
@@ -4995,6 +5036,7 @@ done:
static struct remote_thread_call *
prepareCall(struct private_data *priv,
int flags,
+ int fd,
int proc_nr,
xdrproc_t args_filter, char *args,
xdrproc_t ret_filter, char *ret)
@@ -5021,6 +5063,7 @@ prepareCall(struct private_data *priv,
rv->ret_filter = ret_filter;
rv->ret = ret;
rv->want_reply = 1;
+ rv->fd = fd;
if (flags & REMOTE_CALL_QEMU) {
hdr.prog = QEMU_PROGRAM;
@@ -5031,7 +5074,7 @@ prepareCall(struct private_data *priv,
hdr.vers = REMOTE_PROTOCOL_VERSION;
}
hdr.proc = proc_nr;
- hdr.type = REMOTE_CALL;
+ hdr.type = fd == -1 ? REMOTE_CALL : REMOTE_CALL_WITH_FD;
hdr.serial = rv->serial;
hdr.status = REMOTE_OK;
@@ -5221,6 +5264,21 @@ remoteIOWriteMessage(struct private_data *priv,
if (priv->saslEncodedOffset == priv->saslEncodedLength) {
priv->saslEncoded = NULL;
priv->saslEncodedOffset = priv->saslEncodedLength = 0;
+
+ if (thecall->fd != -1) {
+ if (!priv->hasSendFD) {
+ remoteError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to send file descriptors over this connection"));
+ return -1;
+ }
+ if (sendfd(priv->sock, thecall->fd) < 0) {
+ virReportSystemError(errno,
+ _("Failed to send file descriptor %d"),
+ thecall->fd);
+ return -1;
+ }
+ }
+
if (thecall->want_reply)
thecall->mode = REMOTE_MODE_WAIT_RX;
else
@@ -5238,6 +5296,21 @@ remoteIOWriteMessage(struct private_data *priv,
if (thecall->bufferOffset == thecall->bufferLength) {
thecall->bufferOffset = thecall->bufferLength = 0;
+
+ if (thecall->fd != -1) {
+ if (!priv->hasSendFD) {
+ remoteError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to send file descriptors over this connection"));
+ return -1;
+ }
+ if (sendfd(priv->sock, thecall->fd) < 0) {
+ virReportSystemError(errno,
+ _("Failed to send file descriptor %d"),
+ thecall->fd);
+ return -1;
+ }
+ }
+
if (thecall->want_reply)
thecall->mode = REMOTE_MODE_WAIT_RX;
else
@@ -6181,7 +6254,31 @@ call (virConnectPtr conn, struct private_data *priv,
struct remote_thread_call *thiscall;
int rv;
- thiscall = prepareCall(priv, flags, proc_nr, args_filter, args,
+ thiscall = prepareCall(priv, flags, -1, proc_nr, args_filter, args,
+ ret_filter, ret);
+
+ if (!thiscall) {
+ return -1;
+ }
+
+ rv = remoteIO(conn, priv, flags, thiscall);
+ ignore_value(virCondDestroy(&thiscall->cond));
+ VIR_FREE(thiscall);
+ return rv;
+}
+
+
+static int
+callWithFD(virConnectPtr conn, struct private_data *priv,
+ int flags, int fd,
+ int proc_nr,
+ xdrproc_t args_filter, char *args,
+ xdrproc_t ret_filter, char *ret)
+{
+ struct remote_thread_call *thiscall;
+ int rv;
+
+ thiscall = prepareCall(priv, flags, fd, proc_nr, args_filter, args,
ret_filter, ret);
if (!thiscall) {
@@ -6525,6 +6622,7 @@ static virDriver remote_driver = {
.domainSnapshotDelete = remoteDomainSnapshotDelete, /* 0.8.0 */
.qemuDomainMonitorCommand = remoteQemuDomainMonitorCommand, /* 0.8.3 */
.domainOpenConsole = remoteDomainOpenConsole, /* 0.8.6 */
+ .domainConnectGraphics = remoteDomainConnectGraphics, /* 0.9.2 */
.domainInjectNMI = remoteDomainInjectNMI, /* 0.9.2 */
.domainMigrateBegin3 = remoteDomainMigrateBegin3, /* 0.9.2 */
.domainMigratePrepare3 = remoteDomainMigratePrepare3, /* 0.9.2 */
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 3f8f006..3f40b1c 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -2038,6 +2038,12 @@ struct remote_domain_open_console_args {
unsigned int flags;
};
+struct remote_domain_connect_graphics_args {
+ remote_nonnull_domain dom;
+ remote_nonnull_string devicestr;
+ unsigned int flags;
+};
+
struct remote_storage_vol_upload_args {
remote_nonnull_storage_vol vol;
unsigned hyper offset;
@@ -2425,7 +2431,8 @@ enum remote_procedure {
REMOTE_PROC_DOMAIN_BLOCK_PULL_ABORT = 231, /* autogen autogen */
REMOTE_PROC_DOMAIN_GET_BLOCK_PULL_INFO = 232, /* autogen autogen */
REMOTE_PROC_DOMAIN_EVENT_BLOCK_PULL = 233, /* skipgen skipgen */
- REMOTE_PROC_DOMAIN_GET_CONTROL_INFO = 234 /* autogen autogen */
+ REMOTE_PROC_DOMAIN_GET_CONTROL_INFO = 234, /* autogen autogen */
+ REMOTE_PROC_DOMAIN_CONNECT_GRAPHICS = 235 /* skipgen skipgen */
/*
* Notice how the entries are grouped in sets of 10 ?
@@ -2453,6 +2460,7 @@ enum remote_procedure {
* Length | int | Total number of bytes in message _including_ length.
* Header | remote_message_header | Control information about procedure call
* Payload | - | Variable payload data per procedure
+ * FD | SCM_CREDS | A file descriptor OOM
*
* In header, the 'serial' field varies according to:
*
@@ -2468,6 +2476,9 @@ enum remote_procedure {
* - type == REMOTE_STREAM
* * serial matches that from the corresponding REMOTE_CALL
*
+ * - type == REMOTE_CALL_WITH_FD
+ * * As REMOTE_CALL, but a FD follows the payload
+ *
* and the 'status' field varies according to:
*
* - type == REMOTE_CALL
@@ -2485,6 +2496,9 @@ enum remote_procedure {
* * REMOTE_OK if stream is complete
* * REMOTE_ERROR if stream had an error
*
+ * - type == REMOTE_CALL_WITH_FD
+ * * As with REMOTE_CALL
+ *
* Payload varies according to type and status:
*
* - type == REMOTE_CALL
@@ -2509,6 +2523,9 @@ enum remote_procedure {
* remote_error error information
* * status == REMOTE_OK
* <empty>
+ *
+ * - type == REMOTE_CALL_WITH_FD
+ * * As with REMOTE_CALL, but SCM_CREDS data has a following FD
*/
enum remote_message_type {
/* client -> server. args from a method call */
@@ -2518,7 +2535,9 @@ enum remote_message_type {
/* either direction. async notification */
REMOTE_MESSAGE = 2,
/* either direction. stream data packet */
- REMOTE_STREAM = 3
+ REMOTE_STREAM = 3,
+ /* passing a file descriptor */
+ REMOTE_CALL_WITH_FD = 4
};
enum remote_message_status {
--
1.7.4.4
13 years, 5 months
[libvirt] [PATCH] docs: Make virConnectClose comply to apibuild.py expectations
by Matthias Bolte
apibuild.py expects a sentence that starts with "Returns"
describing the return value of a function.
---
src/libvirt.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c
index 63fe7ff..69e0ea8 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -1303,7 +1303,7 @@ error:
* matching virConnectClose, and all other references will be released
* after the corresponding operation completes.
*
- * The return value is the number of remaining references on success
+ * Returns the number of remaining references on success
* (positive implies that some other call still has a reference open,
* 0 implies that no references remain and the connection is closed),
* or -1 on failure. It is possible for the last virConnectClose to
--
1.7.0.4
13 years, 5 months
[libvirt] [PATCH] Fix compilation on non-macvtap systems
by Matthias Bolte
The refactoring in 6a5978833a5 and df3d8c362d3 was incomplete
as it accidentally moved macvtap related code out of a
#if WITH_MACVTAP in a #if __linux__ block. To fix this move
ifaceMacvtapLinkAdd and ifaceMacvtapLinkDump back under
#if WITH_MACVTAP.
Also nlComm was moved from #if WITH_MACVTAP to #if __linux__
but configure.ac was not updated to match this, as libnl is
now required on Linux always because of this.
Also related to libnl, libvirt_lxc missed LIBNL_CFLAGS in it's
CFLAGS.
---
configure.ac | 6 +++---
src/Makefile.am | 1 +
src/util/interface.c | 6 +++---
3 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/configure.ac b/configure.ac
index f816696..4c81e3b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2372,10 +2372,10 @@ dnl netlink library
LIBNL_CFLAGS=""
LIBNL_LIBS=""
-if test "$with_macvtap" = "yes"; then
+if test "$with_linux" = "yes"; then
PKG_CHECK_MODULES([LIBNL], [libnl-1 >= $LIBNL_REQUIRED], [
], [
- AC_MSG_ERROR([libnl-devel >= $LIBNL_REQUIRED is required for macvtap support])
+ AC_MSG_ERROR([libnl-devel >= $LIBNL_REQUIRED is required])
])
fi
@@ -2574,7 +2574,7 @@ AC_MSG_NOTICE([ pcap: $LIBPCAP_CFLAGS $LIBPCAP_LIBS])
else
AC_MSG_NOTICE([ pcap: no])
fi
-if test "$with_macvtap" = "yes" ; then
+if test "$with_linux" = "yes" ; then
AC_MSG_NOTICE([ nl: $LIBNL_CFLAGS $LIBNL_LIBS])
else
AC_MSG_NOTICE([ nl: no])
diff --git a/src/Makefile.am b/src/Makefile.am
index b292f9f..9e69f37 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1243,6 +1243,7 @@ libvirt_lxc_CFLAGS = \
$(CAPNG_CFLAGS) \
$(YAJL_CFLAGS) \
$(AUDIT_CFLAGS) \
+ $(LIBNL_CFLAGS) \
-I@top_srcdir@/src/conf \
$(AM_CFLAGS)
endif
diff --git a/src/util/interface.c b/src/util/interface.c
index 02544d9..03f9367 100644
--- a/src/util/interface.c
+++ b/src/util/interface.c
@@ -494,7 +494,7 @@ ifaceSetMacaddr(const char *ifname ATTRIBUTE_UNUSED,
/**
- * ifaceLinkAdd
+ * ifaceMacvtapLinkAdd
*
* @type: The type of device, i.e., "macvtap"
* @macaddress: The MAC address of the device
@@ -510,7 +510,7 @@ ifaceSetMacaddr(const char *ifname ATTRIBUTE_UNUSED,
*
* Returns 0 on success, -1 on fatal error.
*/
-#if __linux__
+#if WITH_MACVTAP
int
ifaceMacvtapLinkAdd(const char *type,
const unsigned char *macaddress, int macaddrsize,
@@ -758,7 +758,7 @@ ifaceLinkDel(const char *ifname ATTRIBUTE_UNUSED)
#endif
-#if __linux__
+#if WITH_MACVTAP
static struct nla_policy ifla_policy[IFLA_MAX + 1] =
{
[IFLA_VF_PORTS] = { .type = NLA_NESTED },
--
1.7.0.4
13 years, 5 months
[libvirt] [PATCH] qemu: Fix memory leak in qemuProcessWaitForMonitor
by Osier Yang
Move "VIR_FREE(buf) into label "closelog", so that "buf" could be
freed before returning.
---
src/qemu/qemu_process.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index b441137..7885d88 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -1122,9 +1122,9 @@ cleanup:
ret = -1;
}
+closelog:
VIR_FREE(buf);
-closelog:
if (VIR_CLOSE(logfd) < 0) {
char ebuf[1024];
VIR_WARN("Unable to close logfile: %s",
--
1.7.4
13 years, 5 months
[libvirt] [PATCH] util: Fix memory leak in virJSONParserHandleStartMap
by Osier Yang
---
src/util/json.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/src/util/json.c b/src/util/json.c
index db2727d..48521f2 100644
--- a/src/util/json.c
+++ b/src/util/json.c
@@ -799,8 +799,10 @@ static int virJSONParserHandleStartMap(void *ctx)
}
if (VIR_REALLOC_N(parser->state,
- parser->nstate + 1) < 0)
+ parser->nstate + 1) < 0) {
+ virJSONValueFree(value);
return 0;
+ }
parser->state[parser->nstate].value = value;
parser->state[parser->nstate].key = NULL;
--
1.7.4
13 years, 5 months
[libvirt] [PATCH] qemu: Only check for NUMA availability if required
by Jiri Denemark
We only care about NUMA availability if NUMA configuration is requested
in domain XML.
---
src/qemu/qemu_process.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 8e09e52..37d5a22 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -1197,17 +1197,17 @@ qemuProcessInitNumaMemoryPolicy(virDomainObjPtr vm)
int i = 0;
int maxnode = 0;
+ if (!vm->def->numatune.memory.nodemask)
+ return 0;
+
VIR_DEBUG("Setting NUMA memory policy");
if (numa_available() < 0) {
qemuReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("Host kernel is not aware of NUMA."));
return -1;
}
- if (!vm->def->numatune.memory.nodemask)
- return 0;
-
if (VIR_ALLOC(mask) < 0) {
virReportOOMError();
return -1;
--
1.7.5.3
13 years, 5 months
[libvirt] [PATCH v5 0/5] Add TXT record and hosts support for DNS service and dnsmasq command-line regression testing
by Michal Novotny
Hi,
this is the patch to introduce the TXT record and DNS hosts support
for the DNS service on the virtual network. This can be defined using
the txt-record subelement in the dns element of the network XML
description. The definition of TXT record names containing spaces
is rejected with the error message that TXT record names in DNS
doesn't support spaces.
Also, regression testing for the dnsmasq command line has been
added to test whether the dnsmasq command-line is correct or not.
The new XML definition syntax is:
<dns>
<txt name="example" value="example value" />
<host ip='192.168.122.1'>
<hostname>gateway</hostname>
<hostname>host</hostname>
</host>
</dns>
Where multiple host elements can be defined to put the aliases.
The <dns> element have to be present on the <ip> level, i.e.
one level upper than it was in version 2 of the patch since
the definition affects all the IP addresses on the network.
The patch series has been tested for the configuration and it
was working fine and also RelaxNG schema with the tests have
been both altered to add test cases to test those patches.
This is the new version for latest libvirt codebase so please
review the patchset.
Thanks,
Michal
Signed-off-by: Michal Novotny <minovotn(a)redhat.com>
Michal Novotny (5):
Add TXT record support for virtual DNS service
Network: Add regression tests for the command-line arguments
Network: Move dnsmasqContext creation to
networkSaveDnsmasqHostsfile()
Network: Add additional hosts internal infrastructure
Network: Add support for DNS hosts definition to the network XML
docs/formatnetwork.html.in | 29 ++-
docs/schemas/network.rng | 28 ++
src/Makefile.am | 11 +-
src/conf/network_conf.c | 232 ++++++++++++++++
src/conf/network_conf.h | 25 ++
src/libvirt_private.syms | 1 +
src/network/bridge_driver.c | 116 ++++++---
src/network/bridge_driver.h | 3 +
src/util/dnsmasq.c | 285 ++++++++++++++++++--
src/util/dnsmasq.h | 22 ++-
tests/Makefile.am | 10 +
tests/networkxml2argvdata/isolated-network.argv | 1 +
tests/networkxml2argvdata/isolated-network.xml | 11 +
.../nat-network-dns-txt-record.argv | 1 +
.../nat-network-dns-txt-record.xml | 24 ++
tests/networkxml2argvdata/nat-network.argv | 1 +
tests/networkxml2argvdata/nat-network.xml | 21 ++
tests/networkxml2argvdata/netboot-network.argv | 1 +
tests/networkxml2argvdata/netboot-network.xml | 14 +
.../networkxml2argvdata/netboot-proxy-network.argv | 1 +
.../networkxml2argvdata/netboot-proxy-network.xml | 13 +
tests/networkxml2argvdata/routed-network.argv | 1 +
tests/networkxml2argvdata/routed-network.xml | 9 +
tests/networkxml2argvtest.c | 108 ++++++++
tests/networkxml2xmlin/nat-network-dns-hosts.xml | 14 +
.../nat-network-dns-txt-record.xml | 24 ++
tests/networkxml2xmlout/nat-network-dns-hosts.xml | 14 +
.../nat-network-dns-txt-record.xml | 24 ++
tests/networkxml2xmltest.c | 2 +
29 files changed, 992 insertions(+), 54 deletions(-)
create mode 100644 tests/networkxml2argvdata/isolated-network.argv
create mode 100644 tests/networkxml2argvdata/isolated-network.xml
create mode 100644 tests/networkxml2argvdata/nat-network-dns-txt-record.argv
create mode 100644 tests/networkxml2argvdata/nat-network-dns-txt-record.xml
create mode 100644 tests/networkxml2argvdata/nat-network.argv
create mode 100644 tests/networkxml2argvdata/nat-network.xml
create mode 100644 tests/networkxml2argvdata/netboot-network.argv
create mode 100644 tests/networkxml2argvdata/netboot-network.xml
create mode 100644 tests/networkxml2argvdata/netboot-proxy-network.argv
create mode 100644 tests/networkxml2argvdata/netboot-proxy-network.xml
create mode 100644 tests/networkxml2argvdata/routed-network.argv
create mode 100644 tests/networkxml2argvdata/routed-network.xml
create mode 100644 tests/networkxml2argvtest.c
create mode 100644 tests/networkxml2xmlin/nat-network-dns-hosts.xml
create mode 100644 tests/networkxml2xmlin/nat-network-dns-txt-record.xml
create mode 100644 tests/networkxml2xmlout/nat-network-dns-hosts.xml
create mode 100644 tests/networkxml2xmlout/nat-network-dns-txt-record.xml
--
1.7.3.2
13 years, 5 months