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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list