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