[libvirt] [PATCH 0/8 v3] Allow a client to connect to QEMU's VNC by passing an FD via libvirt

An update to https://www.redhat.com/archives/libvir-list/2011-October/msg01040.html New in v3: - Add feature probing before using new RPC calls - Set O_CLOSEXEC in various places - Misc typo fixes - Fix RPC protocol tests - Add docs about protocol extension

From: "Daniel P. Berrange" <berrange@redhat.com> The virDomainOpenGraphics API allows a libvirt client to pass in a file descriptor for an open socket pair, and get it connected to the graphics display of the guest. This is limited to working with local libvirt hypervisors connected over a UNIX domain socket, since it will use UNIX FD passing * include/libvirt/libvirt.h.in: Define virDomainOpenGraphics * src/driver.h: Define driver for virDomainOpenGraphics * src/libvirt_public.syms, src/libvirt.c: Entry point for virDomainOpenGraphics * src/libvirt_internal.h: VIR_DRV_FEATURE_FD_PASSING --- include/libvirt/libvirt.h.in | 9 ++++ src/driver.h | 6 +++ src/libvirt.c | 85 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_internal.h | 5 ++ src/libvirt_public.syms | 1 + 5 files changed, 106 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7102bce..917c64d 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3140,6 +3140,15 @@ int virDomainOpenConsole(virDomainPtr dom, virStreamPtr st, unsigned int flags); +typedef enum { + VIR_DOMAIN_OPEN_GRAPHICS_SKIPAUTH = (1 << 0), +} virDomainOpenGraphicsFlags; + +int virDomainOpenGraphics(virDomainPtr dom, + unsigned int idx, + int fd, + unsigned int flags); + int virDomainInjectNMI(virDomainPtr domain, unsigned int flags); diff --git a/src/driver.h b/src/driver.h index b899d0e..4c14aaa 100644 --- a/src/driver.h +++ b/src/driver.h @@ -632,6 +632,11 @@ typedef int const char *dev_name, virStreamPtr st, unsigned int flags); +typedef int + (*virDrvDomainOpenGraphics)(virDomainPtr dom, + unsigned int idx, + int fd, + unsigned int flags); typedef int (*virDrvDomainInjectNMI)(virDomainPtr dom, unsigned int flags); @@ -881,6 +886,7 @@ struct _virDriver { virDrvDomainQemuMonitorCommand qemuDomainMonitorCommand; virDrvDomainQemuAttach qemuDomainAttach; virDrvDomainOpenConsole domainOpenConsole; + virDrvDomainOpenGraphics domainOpenGraphics; virDrvDomainInjectNMI domainInjectNMI; virDrvDomainMigrateBegin3 domainMigrateBegin3; virDrvDomainMigratePrepare3 domainMigratePrepare3; diff --git a/src/libvirt.c b/src/libvirt.c index a6bcee6..e9d1a29 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -16964,3 +16964,88 @@ error: virDispatchError(dom->conn); return -1; } + + +/** + * virDomainOpenGraphics: + * @dom: pointer to domain object + * @idx: index of graphics config to open + * @fd: file descriptor to attach graphics to + * @flags: flags to control open operation + * + * This will attempt to connect the file descriptor @fd, to + * the graphics backend of @dom. If @dom has multiple graphics + * backends configured, then @idx will determine which one is + * opened, starting from @idx 0. + * + * To disable any authentication, pass the VIR_DOMAIN_OPEN_GRAPHICS_SKIPAUTH + * constant for @flags. + * + * The caller should use an anonymous socketpair to open + * @fd before invocation. + * + * This method can only be used when connected to a local + * libvirt hypervisor, over a UNIX domain socket. Attempts + * to use this method over a TCP connection will always fail + * + * Returns 0 on success, -1 on failure + */ +int virDomainOpenGraphics(virDomainPtr dom, + unsigned int idx, + int fd, + unsigned int flags) +{ + struct stat sb; + VIR_DOMAIN_DEBUG(dom, "idx=%u, fd=%d, flags=%x", + idx, fd, flags); + + virResetLastError(); + + if (!VIR_IS_DOMAIN(dom)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (fd < 0) { + virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (fstat(fd, &sb) < 0) { + virReportSystemError(errno, + _("Unable to access file descriptor %d"), fd); + goto error; + } + + if (!S_ISSOCK(sb.st_mode)) { + virLibDomainError(VIR_ERR_INVALID_ARG, + _("File descriptor %d must be a socket"), fd); + goto error; + } + + if (dom->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (!VIR_DRV_SUPPORTS_FEATURE(dom->conn->driver, dom->conn, + VIR_DRV_FEATURE_FD_PASSING)) { + virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + + if (dom->conn->driver->domainOpenGraphics) { + int ret; + ret = dom->conn->driver->domainOpenGraphics(dom, idx, 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_internal.h b/src/libvirt_internal.h index 6e44341..0117c5b 100644 --- a/src/libvirt_internal.h +++ b/src/libvirt_internal.h @@ -79,6 +79,11 @@ enum { * to domain configuration, i.e., starting from Begin3 and not Perform3. */ VIR_DRV_FEATURE_MIGRATE_CHANGE_PROTECTION = 7, + + /* + * Support for file descriptor passing + */ + VIR_DRV_FEATURE_FD_PASSING = 8 }; diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 9762fc4..caa21a2 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -495,6 +495,7 @@ LIBVIRT_0.9.7 { virDomainSnapshotGetParent; virDomainSnapshotListChildrenNames; virDomainSnapshotNumChildren; + virDomainOpenGraphics; } LIBVIRT_0.9.5; # .... define new API here using predicted next version number .... -- 1.7.6.4

On 10/25/2011 10:03 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
The virDomainOpenGraphics API allows a libvirt client to pass in a file descriptor for an open socket pair, and get it connected to the graphics display of the guest. This is limited to working with local libvirt hypervisors connected over a UNIX domain socket, since it will use UNIX FD passing
We might someday be able to extend it to also work over TCP, by connecting the user's UNIX fd to libvirt_iohelper, which in turn feeds a pipe into an fdstream over RPC, then feeds another libvirt_iohelper invocation on the client that converts the data back into a server-side UNIX fd passed to qemu. But that's future extension.
+++ b/src/libvirt_public.syms @@ -495,6 +495,7 @@ LIBVIRT_0.9.7 { virDomainSnapshotGetParent; virDomainSnapshotListChildrenNames; virDomainSnapshotNumChildren; + virDomainOpenGraphics; } LIBVIRT_0.9.5;
I would have sorted this, but it's not the end of the world if you don't. ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> Not all VNC/SPICE servers use a TCP socket for their connections. It is possible to configure a UNIX socket server. The graphics event must thus include a UNIX socket address type. * include/libvirt/libvirt.h.in: Add UNIX socket address type for graphics event * src/qemu/qemu_monitor_json.c: Add 'unix' string to address type enum --- include/libvirt/libvirt.h.in | 5 +++-- src/qemu/qemu_monitor_json.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 917c64d..c51c7c5 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2894,6 +2894,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; @@ -2905,8 +2906,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; diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index cd8f1e5..46f98ee 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) { -- 1.7.6.4

On 10/25/2011 10:03 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
Not all VNC/SPICE servers use a TCP socket for their connections. It is possible to configure a UNIX socket server. The graphics event must thus include a UNIX socket address type.
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> The QEMU monitor command 'add_client' can be used to connect to a VNC or SPICE graphics display. This allows for implementation of the virDomainOpenGraphics API * src/qemu/qemu_driver.c: Implement virDomainOpenGraphics * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h, src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add binding for 'add_client' command --- src/qemu/qemu_driver.c | 72 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 33 +++++++++++++++++++ src/qemu/qemu_monitor.h | 6 +++ src/qemu/qemu_monitor_json.c | 27 ++++++++++++++++ src/qemu/qemu_monitor_json.h | 5 +++ src/qemu/qemu_monitor_text.c | 32 ++++++++++++++++++ src/qemu/qemu_monitor_text.h | 5 +++ 7 files changed, 180 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f93b784..ac6321e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -919,6 +919,7 @@ qemudSupportsFeature (virConnectPtr conn ATTRIBUTE_UNUSED, int feature) case VIR_DRV_FEATURE_MIGRATION_V3: case VIR_DRV_FEATURE_MIGRATION_P2P: case VIR_DRV_FEATURE_MIGRATE_CHANGE_PROTECTION: + case VIR_DRV_FEATURE_FD_PASSING: return 1; default: return 0; @@ -10666,6 +10667,76 @@ qemuDomainBlockPull(virDomainPtr dom, const char *path, unsigned long bandwidth, return ret; } +static int +qemuDomainOpenGraphics(virDomainPtr dom, + unsigned int idx, + 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; + const char *protocol; + + virCheckFlags(VIR_DOMAIN_OPEN_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 (idx >= vm->def->ngraphics) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("No graphics backend with index %d"), idx); + goto cleanup; + } + switch (vm->def->graphics[idx]->type) { + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + protocol = "vnc"; + break; + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + protocol = "spice"; + break; + default: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Can only open VNC or SPICE graphics backends, not %s"), + virDomainGraphicsTypeToString(vm->def->graphics[idx]->type)); + goto cleanup; + } + + if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorOpenGraphics(priv->mon, protocol, fd, "graphicsfd", + (flags & VIR_DOMAIN_OPEN_GRAPHICS_SKIPAUTH) != 0); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (qemuDomainObjEndJob(driver, vm) == 0) { + vm = NULL; + goto cleanup; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + + static virDriver qemuDriver = { .no = VIR_DRV_QEMU, .name = "QEMU", @@ -10796,6 +10867,7 @@ static virDriver qemuDriver = { .qemuDomainMonitorCommand = qemuDomainMonitorCommand, /* 0.8.3 */ .qemuDomainAttach = qemuDomainAttach, /* 0.9.4 */ .domainOpenConsole = qemuDomainOpenConsole, /* 0.8.6 */ + .domainOpenGraphics = qemuDomainOpenGraphics, /* 0.9.7 */ .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 182e63d..f7cea2e 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2591,3 +2591,36 @@ int qemuMonitorVMStatusToPausedReason(const char *status) } return VIR_DOMAIN_PAUSED_UNKNOWN; } + + +int qemuMonitorOpenGraphics(qemuMonitorPtr mon, + const char *protocol, + int fd, + const char *fdname, + bool skipauth) +{ + VIR_DEBUG("mon=%p protocol=%s fd=%d fdname=%s skipauth=%d", + mon, protocol, 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 = qemuMonitorJSONOpenGraphics(mon, protocol, fdname, skipauth); + else + ret = qemuMonitorTextOpenGraphics(mon, protocol, 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 90e7b45..883e0aa 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -515,6 +515,12 @@ int qemuMonitorBlockJob(qemuMonitorPtr mon, virDomainBlockJobInfoPtr info, int mode); +int qemuMonitorOpenGraphics(qemuMonitorPtr mon, + const char *protocol, + 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 46f98ee..ced6b72 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3245,3 +3245,30 @@ int qemuMonitorJSONBlockJob(qemuMonitorPtr mon, virJSONValueFree(reply); return ret; } + +int qemuMonitorJSONOpenGraphics(qemuMonitorPtr mon, + const char *protocol, + const char *fdname, + bool skipauth) +{ + int ret; + virJSONValuePtr cmd, reply = NULL; + + cmd = qemuMonitorJSONMakeCommand("add_client", + "s:protocol", protocol, + "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 a638b41..f10d7d2 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -250,4 +250,9 @@ int qemuMonitorJSONSetLink(qemuMonitorPtr mon, const char *name, enum virDomainNetInterfaceLinkState state); +int qemuMonitorJSONOpenGraphics(qemuMonitorPtr mon, + const char *protocol, + 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 4774df9..03b6b44 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -3394,3 +3394,35 @@ cleanup: VIR_FREE(reply); return ret; } + + +int qemuMonitorTextOpenGraphics(qemuMonitorPtr mon, + const char *protocol, + const char *fdname, + bool skipauth) +{ + char *cmd = NULL; + char *reply = NULL; + int ret = -1; + + if (virAsprintf(&cmd, "add_client %s %s %d", protocol, 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 cc2a252..f32fce0 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -243,4 +243,9 @@ int qemuMonitorTextSetLink(qemuMonitorPtr mon, const char *name, enum virDomainNetInterfaceLinkState state); +int qemuMonitorTextOpenGraphics(qemuMonitorPtr mon, + const char *protocol, + const char *fdname, + bool skipauth); + #endif /* QEMU_MONITOR_TEXT_H */ -- 1.7.6.4

On 10/25/2011 10:03 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
The QEMU monitor command 'add_client' can be used to connect to a VNC or SPICE graphics display. This allows for implementation of the virDomainOpenGraphics API
* src/qemu/qemu_driver.c: Implement virDomainOpenGraphics * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h, src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add binding for 'add_client' command
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> Add APIs to the virNetSocket object, to allow file descriptors to be sent/received over UNIX domain socket connections * src/rpc/virnetsocket.c, src/rpc/virnetsocket.h, src/libvirt_private.syms: Add APIs for FD send/recv --- examples/systemtap/rpc-monitor.stp | 10 ++++++ src/libvirt_private.syms | 4 ++ src/probes.d | 2 + src/rpc/virnetsocket.c | 62 ++++++++++++++++++++++++++++++++++++ src/rpc/virnetsocket.h | 5 +++ 5 files changed, 83 insertions(+), 0 deletions(-) diff --git a/examples/systemtap/rpc-monitor.stp b/examples/systemtap/rpc-monitor.stp index f246571..69d7593 100755 --- a/examples/systemtap/rpc-monitor.stp +++ b/examples/systemtap/rpc-monitor.stp @@ -155,3 +155,13 @@ probe libvirt.rpc.server_client_free { delete serverSocks[pid(), client]; } } + + +probe libvirt.rpc.socket_send_fd { + print_ts(sprintf("= %-16p send fd=%d", sock, fd)); +} + + +probe libvirt.rpc.socket_recv_fd { + print_ts(sprintf("= %-16p recv fd=%d", sock, fd)); +} diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a81966d..6a42d1b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1252,9 +1252,13 @@ virNetServerProgramSendStreamError; virNetSocketDupFD; virNetSocketFree; virNetSocketGetFD; +virNetSocketHasPassFD; +virNetSocketIsLocal; virNetSocketListen; virNetSocketNewConnectTCP; virNetSocketNewListenUNIX; +virNetSocketRecvFD; +virNetSocketSendFD; # virnettlscontext.h diff --git a/src/probes.d b/src/probes.d index 6d95c84..2f5d059 100644 --- a/src/probes.d +++ b/src/probes.d @@ -19,6 +19,8 @@ provider libvirt { # file: src/rpc/virnetsocket.c # prefix: rpc probe rpc_socket_new(void *sock, int refs, int fd, int errfd, int pid, const char *localAddr, const char *remoteAddr); + probe rpc_socket_send_fd(void *sock, int fd); + probe rpc_socket_recv_fd(void *sock, int fd); probe rpc_socket_ref(void *sock, int refs); probe rpc_socket_free(void *sock, int refs); diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c index e4eff49..ab88e19 100644 --- a/src/rpc/virnetsocket.c +++ b/src/rpc/virnetsocket.c @@ -43,6 +43,8 @@ #include "event.h" #include "threads.h" +#include "passfd.h" + #define VIR_FROM_THIS VIR_FROM_RPC #define virNetError(code, ...) \ @@ -791,6 +793,17 @@ bool virNetSocketIsLocal(virNetSocketPtr sock) } +bool virNetSocketHasPassFD(virNetSocketPtr sock) +{ + bool hasPassFD = false; + virMutexLock(&sock->lock); + if (sock->localAddr.data.sa.sa_family == AF_UNIX) + hasPassFD = true; + virMutexUnlock(&sock->lock); + return hasPassFD; +} + + int virNetSocketGetPort(virNetSocketPtr sock) { int port; @@ -1128,6 +1141,55 @@ ssize_t virNetSocketWrite(virNetSocketPtr sock, const char *buf, size_t len) } +int virNetSocketSendFD(virNetSocketPtr sock, int fd) +{ + int ret = -1; + if (!virNetSocketHasPassFD(sock)) { + virNetError(VIR_ERR_INTERNAL_ERROR, + _("Sending file descriptors is not supported on this socket")); + return -1; + } + virMutexLock(&sock->lock); + PROBE(RPC_SOCKET_SEND_FD, + "sock=%p fd=%d", sock, fd); + if (sendfd(sock->fd, fd) < 0) { + virReportSystemError(errno, + _("Failed to send file descriptor %d"), + fd); + goto cleanup; + } + ret = 0; + +cleanup: + virMutexUnlock(&sock->lock); + return ret; +} + + +int virNetSocketRecvFD(virNetSocketPtr sock) +{ + int ret = -1; + if (!virNetSocketHasPassFD(sock)) { + virNetError(VIR_ERR_INTERNAL_ERROR, + _("Receiving file descriptors is not supported on this socket")); + return -1; + } + virMutexLock(&sock->lock); + + if ((ret = recvfd(sock->fd, O_CLOEXEC)) < 0) { + virReportSystemError(errno, "%s", + _("Failed to recv file descriptor")); + goto cleanup; + } + PROBE(RPC_SOCKET_RECV_FD, + "sock=%p fd=%d", sock, ret); + +cleanup: + virMutexUnlock(&sock->lock); + return ret; +} + + int virNetSocketListen(virNetSocketPtr sock, int backlog) { virMutexLock(&sock->lock); diff --git a/src/rpc/virnetsocket.h b/src/rpc/virnetsocket.h index 9c4f112..13cbb14 100644 --- a/src/rpc/virnetsocket.h +++ b/src/rpc/virnetsocket.h @@ -82,6 +82,8 @@ int virNetSocketDupFD(virNetSocketPtr sock, bool cloexec); bool virNetSocketIsLocal(virNetSocketPtr sock); +bool virNetSocketHasPassFD(virNetSocketPtr sock); + int virNetSocketGetPort(virNetSocketPtr sock); int virNetSocketGetLocalIdentity(virNetSocketPtr sock, @@ -94,6 +96,9 @@ int virNetSocketSetBlocking(virNetSocketPtr sock, ssize_t virNetSocketRead(virNetSocketPtr sock, char *buf, size_t len); ssize_t virNetSocketWrite(virNetSocketPtr sock, const char *buf, size_t len); +int virNetSocketSendFD(virNetSocketPtr sock, int fd); +int virNetSocketRecvFD(virNetSocketPtr sock); + void virNetSocketSetTLSSession(virNetSocketPtr sock, virNetTLSSessionPtr sess); # ifdef HAVE_SASL -- 1.7.6.4

On 10/25/2011 10:03 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
Add APIs to the virNetSocket object, to allow file descriptors to be sent/received over UNIX domain socket connections
* src/rpc/virnetsocket.c, src/rpc/virnetsocket.h, src/libvirt_private.syms: Add APIs for FD send/recv --- examples/systemtap/rpc-monitor.stp | 10 ++++++ src/libvirt_private.syms | 4 ++ src/probes.d | 2 + src/rpc/virnetsocket.c | 62 ++++++++++++++++++++++++++++++++++++ src/rpc/virnetsocket.h | 5 +++ 5 files changed, 83 insertions(+), 0 deletions(-)
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> Define two new RPC message types VIR_NET_CALL_WITH_FDS and VIR_NET_REPLY_WITH_FDS. These message types are equivalent to VIR_NET_CALL and VIR_NET_REPLY, except that between the message header, and payload there is a 32-bit integer field specifying how many file descriptors have been passed. The actual file descriptors are sent/recv'd out of band. * src/rpc/virnetmessage.c, src/rpc/virnetmessage.h, src/libvirt_private.syms: Add support for handling passed file descriptors * src/rpc/virnetprotocol.x: Extend protocol for FD passing --- docs/internals/rpc.html.in | 33 +++++++++++++ src/libvirt_private.syms | 2 + src/rpc/virnetmessage.c | 111 +++++++++++++++++++++++++++++++++++++++++++- src/rpc/virnetmessage.h | 9 ++++ src/rpc/virnetprotocol.x | 23 +++++++++- src/virnetprotocol-structs | 2 + 6 files changed, 177 insertions(+), 3 deletions(-) diff --git a/docs/internals/rpc.html.in b/docs/internals/rpc.html.in index 2c0c983..e746431 100644 --- a/docs/internals/rpc.html.in +++ b/docs/internals/rpc.html.in @@ -163,8 +163,11 @@ <ul> <li>type=call: the in parameters for the method call, XDR encoded</li> + <li>type=call-with-fds: number of file handles, then the in parameters for the method call, XDR encoded, followed by the file handles</li> <li>type=reply+status=ok: the return value and/or out parameters for the method call, XDR encoded</li> <li>type=reply+status=error: the error information for the method, a virErrorPtr XDR encoded</li> + <li>type=reply-with-fds+status=ok: number of file handles, the return value and/or out parameters for the method call, XDR encoded, followed by the file handles</li> + <li>type=reply-with-fds+status=error: number of file handles, the error information for the method, a virErrorPtr XDR encoded, followed by the file handles</li> <li>type=event: the parameters for the event, XDR encoded</li> <li>type=stream+status=ok: no payload</li> <li>type=stream+status=error: the error information for the method, a virErrorPtr XDR encoded</li> @@ -172,6 +175,15 @@ </ul> <p> + With the two packet types that support passing file descriptors, in + between the header and the payload there will be a 4-byte integer + specifying the number of file descriptors which are being sent. + The actual file handles are sent after the payload has been sent. + Each file handle has a single dummy byte transmitted as a carrier + for the out of band file descriptor. + </p> + + <p> For the exact payload information for each procedure, consult the XDR protocol definition for the program+version in question </p> @@ -339,6 +351,27 @@ +--+-----------------------+--------+ </pre> + <h4><a name="wireexamplescallfd">Method call with passed FD</a></h4> + + <p> + A single method call with 2 passed file descriptors and successful + reply, for a program=8, version=1, procedure=3, which 10 bytes worth + of input args, and 4 bytes worth of return values. The number of + file descriptors is encoded as a 32-bit int. Each file descriptor + then has a 1 byte dummy payload. The overall input + packet length is 4 + 24 + 4 + 2 + 10 == 44, and output packet length 32. + </p> + + <pre> + +--+-----------------------+---------------+-------+ + C --> |44| 8 | 1 | 3 | 0 | 1 | 0 | 2 | .o.oOo.o. | 0 | 0 | --> S (call) + +--+-----------------------+---------------+-------+ + + +--+-----------------------+--------+ + C <-- |32| 8 | 1 | 3 | 1 | 1 | 0 | .o.oOo | <-- S (reply) + +--+-----------------------+--------+ + </pre> + <h2><a name="security">RPC security</a></h2> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6a42d1b..924c2c3 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1185,8 +1185,10 @@ virFileFdopen; # virnetmessage.h virNetMessageClear; +virNetMessageDecodeNumFDs; virNetMessageEncodeHeader; virNetMessageEncodePayload; +virNetMessageEncodeNumFDs; virNetMessageFree; virNetMessageNew; virNetMessageQueuePush; diff --git a/src/rpc/virnetmessage.c b/src/rpc/virnetmessage.c index a1ae9c4..a833c90 100644 --- a/src/rpc/virnetmessage.c +++ b/src/rpc/virnetmessage.c @@ -21,11 +21,13 @@ #include <config.h> #include <stdlib.h> +#include <unistd.h> #include "virnetmessage.h" #include "memory.h" #include "virterror_internal.h" #include "logging.h" +#include "virfile.h" #define VIR_FROM_THIS VIR_FROM_RPC #define virNetError(code, ...) \ @@ -51,6 +53,13 @@ virNetMessagePtr virNetMessageNew(bool tracked) void virNetMessageClear(virNetMessagePtr msg) { bool tracked = msg->tracked; + size_t i; + + VIR_DEBUG("msg=%p nfds=%zu", msg, msg->nfds); + + for (i = 0 ; i < msg->nfds ; i++) + VIR_FORCE_CLOSE(msg->fds[i]); + VIR_FREE(msg->fds); memset(msg, 0, sizeof(*msg)); msg->tracked = tracked; } @@ -58,14 +67,18 @@ void virNetMessageClear(virNetMessagePtr msg) void virNetMessageFree(virNetMessagePtr msg) { + size_t i; if (!msg) return; + VIR_DEBUG("msg=%p nfds=%zu cb=%p", msg, msg->nfds, msg->cb); + if (msg->cb) msg->cb(msg, msg->opaque); - VIR_DEBUG("msg=%p", msg); - + for (i = 0 ; i < msg->nfds ; i++) + VIR_FORCE_CLOSE(msg->fds[i]); + VIR_FREE(msg->fds); VIR_FREE(msg); } @@ -239,6 +252,78 @@ cleanup: } +int virNetMessageEncodeNumFDs(virNetMessagePtr msg) +{ + XDR xdr; + unsigned int numFDs = msg->nfds; + int ret = -1; + + xdrmem_create(&xdr, msg->buffer + msg->bufferOffset, + msg->bufferLength - msg->bufferOffset, XDR_ENCODE); + + if (numFDs > VIR_NET_MESSAGE_NUM_FDS_MAX) { + virNetError(VIR_ERR_RPC, + _("Too many FDs to send %d, expected %d maximum"), + numFDs, VIR_NET_MESSAGE_NUM_FDS_MAX); + goto cleanup; + } + + if (!xdr_u_int(&xdr, &numFDs)) { + virNetError(VIR_ERR_RPC, "%s", _("Unable to encode number of FDs")); + goto cleanup; + } + msg->bufferOffset += xdr_getpos(&xdr); + + VIR_DEBUG("Send %zu FDs to peer", msg->nfds); + + ret = 0; + +cleanup: + xdr_destroy(&xdr); + return ret; +} + + +int virNetMessageDecodeNumFDs(virNetMessagePtr msg) +{ + XDR xdr; + unsigned int numFDs; + int ret = -1; + size_t i; + + xdrmem_create(&xdr, msg->buffer + msg->bufferOffset, + msg->bufferLength - msg->bufferOffset, XDR_DECODE); + if (!xdr_u_int(&xdr, &numFDs)) { + virNetError(VIR_ERR_RPC, "%s", _("Unable to decode number of FDs")); + goto cleanup; + } + msg->bufferOffset += xdr_getpos(&xdr); + + if (numFDs > VIR_NET_MESSAGE_NUM_FDS_MAX) { + virNetError(VIR_ERR_RPC, + _("Received too many FDs %d, expected %d maximum"), + numFDs, VIR_NET_MESSAGE_NUM_FDS_MAX); + goto cleanup; + } + + msg->nfds = numFDs; + if (VIR_ALLOC_N(msg->fds, msg->nfds) < 0) { + virReportOOMError(); + goto cleanup; + } + for (i = 0 ; i < msg->nfds ; i++) + msg->fds[i] = -1; + + VIR_DEBUG("Got %zu FDs from peer", msg->nfds); + + ret = 0; + +cleanup: + xdr_destroy(&xdr); + return ret; +} + + int virNetMessageEncodePayload(virNetMessagePtr msg, xdrproc_t filter, void *data) @@ -403,3 +488,25 @@ void virNetMessageSaveError(virNetMessageErrorPtr rerr) rerr->level = VIR_ERR_ERROR; } } + + +int virNetMessageDupFD(virNetMessagePtr msg, + size_t slot) +{ + int fd; + + if (slot >= msg->nfds) { + virNetError(VIR_ERR_INTERNAL_ERROR, + _("No FD available at slot %zu"), slot); + return -1; + } + + if ((fd = dup(msg->fds[slot])) < 0) { + virReportSystemError(errno, + _("Unable to duplicate FD %d"), + msg->fds[slot]); + return -1; + } + + return fd; +} diff --git a/src/rpc/virnetmessage.h b/src/rpc/virnetmessage.h index 307a041..ad63409 100644 --- a/src/rpc/virnetmessage.h +++ b/src/rpc/virnetmessage.h @@ -46,6 +46,9 @@ struct _virNetMessage { virNetMessageFreeCallback cb; void *opaque; + size_t nfds; + int *fds; + virNetMessagePtr next; }; @@ -78,6 +81,9 @@ int virNetMessageDecodePayload(virNetMessagePtr msg, void *data) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +int virNetMessageEncodeNumFDs(virNetMessagePtr msg); +int virNetMessageDecodeNumFDs(virNetMessagePtr msg); + int virNetMessageEncodePayloadRaw(virNetMessagePtr msg, const char *buf, size_t len) @@ -88,4 +94,7 @@ int virNetMessageEncodePayloadEmpty(virNetMessagePtr msg) void virNetMessageSaveError(virNetMessageErrorPtr rerr) ATTRIBUTE_NONNULL(1); +int virNetMessageDupFD(virNetMessagePtr msg, + size_t slot); + #endif /* __VIR_NET_MESSAGE_H__ */ diff --git a/src/rpc/virnetprotocol.x b/src/rpc/virnetprotocol.x index 306fafa..4520e38 100644 --- a/src/rpc/virnetprotocol.x +++ b/src/rpc/virnetprotocol.x @@ -62,6 +62,11 @@ const VIR_NET_MESSAGE_LEN_MAX = 4; */ const VIR_NET_MESSAGE_STRING_MAX = 65536; +/* Limit on number of File Descriptors allowed to be + * passed per message + */ +const VIR_NET_MESSAGE_NUM_FDS_MAX = 32; + /* * RPC wire format * @@ -126,6 +131,18 @@ const VIR_NET_MESSAGE_STRING_MAX = 65536; * remote_error error information * * status == VIR_NET_OK * <empty> + * + * - type == VIR_NET_CALL_WITH_FDS + * int8 - number of FDs + * XXX_args for procedure + * + * - type == VIR_NET_REPLY_WITH_FDS + * int8 - number of FDs + * * status == VIR_NET_OK + * XXX_ret for procedure + * * status == VIR_NET_ERROR + * remote_error Error information + * */ enum virNetMessageType { /* client -> server. args from a method call */ @@ -135,7 +152,11 @@ enum virNetMessageType { /* either direction. async notification */ VIR_NET_MESSAGE = 2, /* either direction. stream data packet */ - VIR_NET_STREAM = 3 + VIR_NET_STREAM = 3, + /* client -> server. args from a method call, with passed FDs */ + VIR_NET_CALL_WITH_FDS = 4, + /* server -> client. reply/error from a method call, with passed FDs */ + VIR_NET_REPLY_WITH_FDS = 5 }; enum virNetMessageStatus { diff --git a/src/virnetprotocol-structs b/src/virnetprotocol-structs index 4d97e9c..af4526c 100644 --- a/src/virnetprotocol-structs +++ b/src/virnetprotocol-structs @@ -4,6 +4,8 @@ enum virNetMessageType { VIR_NET_REPLY = 1, VIR_NET_MESSAGE = 2, VIR_NET_STREAM = 3, + VIR_NET_CALL_WITH_FDS = 4, + VIR_NET_REPLY_WITH_FDS = 5, }; enum virNetMessageStatus { VIR_NET_OK = 0, -- 1.7.6.4

On 10/25/2011 10:03 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
Define two new RPC message types VIR_NET_CALL_WITH_FDS and VIR_NET_REPLY_WITH_FDS. These message types are equivalent to VIR_NET_CALL and VIR_NET_REPLY, except that between the message header, and payload there is a 32-bit integer field specifying how many file descriptors have been passed.
The actual file descriptors are sent/recv'd out of band.
* src/rpc/virnetmessage.c, src/rpc/virnetmessage.h, src/libvirt_private.syms: Add support for handling passed file descriptors * src/rpc/virnetprotocol.x: Extend protocol for FD passing --- docs/internals/rpc.html.in | 33 +++++++++++++
Thanks; this fixes my biggest concern from the last round.
<p> + With the two packet types that support passing file descriptors, in + between the header and the payload there will be a 4-byte integer + specifying the number of file descriptors which are being sent. + The actual file handles are sent after the payload has been sent. + Each file handle has a single dummy byte transmitted as a carrier + for the out of band file descriptor.
gnulib recvfd() ignores this byte value. While sendfd() sends NUL, it is conceivable that a 3rd-party implementation of the wire protocol might pick a different value (or that gnulib might change in the future). Should we add any documentation along the lines of 'sender should send 0' and 'receiver must ignore the dummy byte, even if it is not 0', just for robustness sake?
+ + +int virNetMessageDupFD(virNetMessagePtr msg, + size_t slot) +{ + int fd; + + if (slot>= msg->nfds) { + virNetError(VIR_ERR_INTERNAL_ERROR, + _("No FD available at slot %zu"), slot); + return -1; + } + + if ((fd = dup(msg->fds[slot]))< 0) { + virReportSystemError(errno, + _("Unable to duplicate FD %d"), + msg->fds[slot]); + return -1; + }
Do we want to be using gnulib's dup_cloexec ("cloexec.h") or fcntl(fd, F_DUPFD_CLOEXEC, 0) here, so that our dup doesn't leak into a third-party child if we are linked into some larger multithreaded app? ACK with that resolved (and I'm okay if the resolution is saving the conversion of dup to dup_cloexec for another day, especially since I didn't raise the issue on the last round of reviews, but want this to go in before code freeze). -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Thu, Oct 27, 2011 at 05:15:40PM -0600, Eric Blake wrote:
On 10/25/2011 10:03 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
Define two new RPC message types VIR_NET_CALL_WITH_FDS and VIR_NET_REPLY_WITH_FDS. These message types are equivalent to VIR_NET_CALL and VIR_NET_REPLY, except that between the message header, and payload there is a 32-bit integer field specifying how many file descriptors have been passed.
The actual file descriptors are sent/recv'd out of band.
* src/rpc/virnetmessage.c, src/rpc/virnetmessage.h, src/libvirt_private.syms: Add support for handling passed file descriptors * src/rpc/virnetprotocol.x: Extend protocol for FD passing --- docs/internals/rpc.html.in | 33 +++++++++++++
Thanks; this fixes my biggest concern from the last round.
<p> + With the two packet types that support passing file descriptors, in + between the header and the payload there will be a 4-byte integer + specifying the number of file descriptors which are being sent. + The actual file handles are sent after the payload has been sent. + Each file handle has a single dummy byte transmitted as a carrier + for the out of band file descriptor.
gnulib recvfd() ignores this byte value. While sendfd() sends NUL, it is conceivable that a 3rd-party implementation of the wire protocol might pick a different value (or that gnulib might change in the future). Should we add any documentation along the lines of 'sender should send 0' and 'receiver must ignore the dummy byte, even if it is not 0', just for robustness sake?
+ + +int virNetMessageDupFD(virNetMessagePtr msg, + size_t slot) +{ + int fd; + + if (slot>= msg->nfds) { + virNetError(VIR_ERR_INTERNAL_ERROR, + _("No FD available at slot %zu"), slot); + return -1; + } + + if ((fd = dup(msg->fds[slot]))< 0) { + virReportSystemError(errno, + _("Unable to duplicate FD %d"), + msg->fds[slot]); + return -1; + }
Do we want to be using gnulib's dup_cloexec ("cloexec.h") or fcntl(fd, F_DUPFD_CLOEXEC, 0) here, so that our dup doesn't leak into a third-party child if we are linked into some larger multithreaded app?
dup3() is the new glibc API for this ? Does gnulib support that yet ? Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 10/28/2011 02:28 AM, Daniel P. Berrange wrote:
+ if ((fd = dup(msg->fds[slot]))< 0) { + virReportSystemError(errno, + _("Unable to duplicate FD %d"), + msg->fds[slot]); + return -1; + }
Do we want to be using gnulib's dup_cloexec ("cloexec.h") or fcntl(fd, F_DUPFD_CLOEXEC, 0) here, so that our dup doesn't leak into a third-party child if we are linked into some larger multithreaded app?
dup3() is the new glibc API for this ? Does gnulib support that yet ?
Gnulib supports dup3(). But dup3() is not the same as dup(); it is the same as dup2(). That is: dup(fd) maps to fcntl(fd, F_DUPFD_CLOEXEC,0) == dup_cloexec(fd) dup2(fd1, fd2) maps to dup3(fd1, fd2, O_CLOEXEC) -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Fri, Oct 28, 2011 at 05:49:59AM -0600, Eric Blake wrote:
On 10/28/2011 02:28 AM, Daniel P. Berrange wrote:
+ if ((fd = dup(msg->fds[slot]))< 0) { + virReportSystemError(errno, + _("Unable to duplicate FD %d"), + msg->fds[slot]); + return -1; + }
Do we want to be using gnulib's dup_cloexec ("cloexec.h") or fcntl(fd, F_DUPFD_CLOEXEC, 0) here, so that our dup doesn't leak into a third-party child if we are linked into some larger multithreaded app?
dup3() is the new glibc API for this ? Does gnulib support that yet ?
Gnulib supports dup3(). But dup3() is not the same as dup(); it is the same as dup2(). That is:
dup(fd) maps to fcntl(fd, F_DUPFD_CLOEXEC,0) == dup_cloexec(fd) dup2(fd1, fd2) maps to dup3(fd1, fd2, O_CLOEXEC)
Ah ok, I see what you mean. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

From: "Daniel P. Berrange" <berrange@redhat.com> Extend the RPC client code to allow file descriptors to be sent to the server with calls, and received back with replies. * src/remote/remote_driver.c: Stub extra args * src/libvirt_private.syms, src/rpc/virnetclient.c, src/rpc/virnetclient.h, src/rpc/virnetclientprogram.c, src/rpc/virnetclientprogram.h: Extend APIs to allow FD passing --- src/libvirt_private.syms | 4 +++ src/remote/remote_driver.c | 1 + src/rpc/virnetclient.c | 25 +++++++++++++++++ src/rpc/virnetclient.h | 2 + src/rpc/virnetclientprogram.c | 58 +++++++++++++++++++++++++++++++++++++++- src/rpc/virnetclientprogram.h | 4 +++ 6 files changed, 92 insertions(+), 2 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 924c2c3..94520eb 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1183,6 +1183,10 @@ virFileFclose; virFileFdopen; +# virnetclient.h +virNetClientHasPassFD; + + # virnetmessage.h virNetMessageClear; virNetMessageDecodeNumFDs; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index e98ebd7..382bb42 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -4152,6 +4152,7 @@ call (virConnectPtr conn ATTRIBUTE_UNUSED, client, counter, proc_nr, + 0, NULL, NULL, NULL, args_filter, args, ret_filter, ret); remoteDriverLock(priv); diff --git a/src/rpc/virnetclient.c b/src/rpc/virnetclient.c index 085dc8d..2b5f67c 100644 --- a/src/rpc/virnetclient.c +++ b/src/rpc/virnetclient.c @@ -258,6 +258,16 @@ int virNetClientDupFD(virNetClientPtr client, bool cloexec) } +bool virNetClientHasPassFD(virNetClientPtr client) +{ + bool hasPassFD; + virNetClientLock(client); + hasPassFD = virNetSocketHasPassFD(client->sock); + virNetClientUnlock(client); + return hasPassFD; +} + + void virNetClientFree(virNetClientPtr client) { int i; @@ -684,6 +694,7 @@ static int virNetClientCallDispatchStream(virNetClientPtr client) static int virNetClientCallDispatch(virNetClientPtr client) { + size_t i; if (virNetMessageDecodeHeader(&client->msg) < 0) return -1; @@ -697,6 +708,15 @@ virNetClientCallDispatch(virNetClientPtr client) case VIR_NET_REPLY: /* Normal RPC replies */ return virNetClientCallDispatchReply(client); + case VIR_NET_REPLY_WITH_FDS: /* Normal RPC replies with FDs */ + if (virNetMessageDecodeNumFDs(&client->msg) < 0) + return -1; + for (i = 0 ; i < client->msg.nfds ; i++) { + if ((client->msg.fds[i] = virNetSocketRecvFD(client->sock)) < 0) + return -1; + } + return virNetClientCallDispatchReply(client); + case VIR_NET_MESSAGE: /* Async notifications */ return virNetClientCallDispatchMessage(client); @@ -728,6 +748,11 @@ virNetClientIOWriteMessage(virNetClientPtr client, thecall->msg->bufferOffset += ret; if (thecall->msg->bufferOffset == thecall->msg->bufferLength) { + size_t i; + for (i = 0 ; i < thecall->msg->nfds ; i++) { + if (virNetSocketSendFD(client->sock, thecall->msg->fds[i]) < 0) + return -1; + } thecall->msg->bufferOffset = thecall->msg->bufferLength = 0; if (thecall->expectReply) thecall->mode = VIR_NET_CLIENT_MODE_WAIT_RX; diff --git a/src/rpc/virnetclient.h b/src/rpc/virnetclient.h index 1fabcfd..fb679e8 100644 --- a/src/rpc/virnetclient.h +++ b/src/rpc/virnetclient.h @@ -56,6 +56,8 @@ void virNetClientRef(virNetClientPtr client); int virNetClientGetFD(virNetClientPtr client); int virNetClientDupFD(virNetClientPtr client, bool cloexec); +bool virNetClientHasPassFD(virNetClientPtr client); + int virNetClientAddProgram(virNetClientPtr client, virNetClientProgramPtr prog); diff --git a/src/rpc/virnetclientprogram.c b/src/rpc/virnetclientprogram.c index 33fa507..b6c063d 100644 --- a/src/rpc/virnetclientprogram.c +++ b/src/rpc/virnetclientprogram.c @@ -22,6 +22,8 @@ #include <config.h> +#include <unistd.h> + #include "virnetclientprogram.h" #include "virnetclient.h" #include "virnetprotocol.h" @@ -29,6 +31,7 @@ #include "memory.h" #include "virterror_internal.h" #include "logging.h" +#include "util.h" #define VIR_FROM_THIS VIR_FROM_RPC #define virNetError(code, ...) \ @@ -267,10 +270,15 @@ int virNetClientProgramCall(virNetClientProgramPtr prog, virNetClientPtr client, unsigned serial, int proc, + size_t noutfds, + int *outfds, + size_t *ninfds, + int **infds, xdrproc_t args_filter, void *args, xdrproc_t ret_filter, void *ret) { virNetMessagePtr msg; + size_t i; if (!(msg = virNetMessageNew(false))) return -1; @@ -278,13 +286,36 @@ int virNetClientProgramCall(virNetClientProgramPtr prog, msg->header.prog = prog->program; msg->header.vers = prog->version; msg->header.status = VIR_NET_OK; - msg->header.type = VIR_NET_CALL; + msg->header.type = noutfds ? VIR_NET_CALL_WITH_FDS : VIR_NET_CALL; msg->header.serial = serial; msg->header.proc = proc; + msg->nfds = noutfds; + if (VIR_ALLOC_N(msg->fds, msg->nfds) < 0) { + virReportOOMError(); + goto error; + } + for (i = 0 ; i < msg->nfds ; i++) { + if ((msg->fds[i] = dup(outfds[i])) < 0) { + virReportSystemError(errno, + _("Cannot duplicate FD %d"), + outfds[i]); + goto error; + } + if (virSetInherit(msg->fds[i], false) < 0) { + virReportSystemError(errno, + _("Cannot set close-on-exec %d"), + msg->fds[i]); + goto error; + } + } if (virNetMessageEncodeHeader(msg) < 0) goto error; + if (msg->nfds && + virNetMessageEncodeNumFDs(msg) < 0) + goto error; + if (virNetMessageEncodePayload(msg, args_filter, args) < 0) goto error; @@ -295,7 +326,8 @@ int virNetClientProgramCall(virNetClientProgramPtr prog, * virNetClientSend should have validated the reply, * but it doesn't hurt to check again. */ - if (msg->header.type != VIR_NET_REPLY) { + if (msg->header.type != VIR_NET_REPLY && + msg->header.type != VIR_NET_REPLY_WITH_FDS) { virNetError(VIR_ERR_INTERNAL_ERROR, _("Unexpected message type %d"), msg->header.type); goto error; @@ -315,6 +347,28 @@ int virNetClientProgramCall(virNetClientProgramPtr prog, switch (msg->header.status) { case VIR_NET_OK: + if (infds && ninfds) { + *ninfds = msg->nfds; + if (VIR_ALLOC_N(*infds, *ninfds) < 0) { + virReportOOMError(); + goto error; + } + for (i = 0 ; i < *ninfds ; i++) { + if ((*infds[i] = dup(msg->fds[i])) < 0) { + virReportSystemError(errno, + _("Cannot duplicate FD %d"), + msg->fds[i]); + goto error; + } + if (virSetInherit(*infds[i], false) < 0) { + virReportSystemError(errno, + _("Cannot set close-on-exec %d"), + *infds[i]); + goto error; + } + } + + } if (virNetMessageDecodePayload(msg, ret_filter, ret) < 0) goto error; break; diff --git a/src/rpc/virnetclientprogram.h b/src/rpc/virnetclientprogram.h index 82ae2c6..14a4c96 100644 --- a/src/rpc/virnetclientprogram.h +++ b/src/rpc/virnetclientprogram.h @@ -77,6 +77,10 @@ int virNetClientProgramCall(virNetClientProgramPtr prog, virNetClientPtr client, unsigned serial, int proc, + size_t noutfds, + int *outfds, + size_t *ninfds, + int **infds, xdrproc_t args_filter, void *args, xdrproc_t ret_filter, void *ret); -- 1.7.6.4

On 10/25/2011 10:03 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
Extend the RPC client code to allow file descriptors to be sent to the server with calls, and received back with replies.
* src/remote/remote_driver.c: Stub extra args * src/libvirt_private.syms, src/rpc/virnetclient.c, src/rpc/virnetclient.h, src/rpc/virnetclientprogram.c, src/rpc/virnetclientprogram.h: Extend APIs to allow FD passing --- src/libvirt_private.syms | 4 +++ src/remote/remote_driver.c | 1 + src/rpc/virnetclient.c | 25 +++++++++++++++++ src/rpc/virnetclient.h | 2 + src/rpc/virnetclientprogram.c | 58 +++++++++++++++++++++++++++++++++++++++- src/rpc/virnetclientprogram.h | 4 +++ 6 files changed, 92 insertions(+), 2 deletions(-)
msg->header.serial = serial; msg->header.proc = proc; + msg->nfds = noutfds; + if (VIR_ALLOC_N(msg->fds, msg->nfds)< 0) { + virReportOOMError(); + goto error; + } + for (i = 0 ; i< msg->nfds ; i++) { + if ((msg->fds[i] = dup(outfds[i]))< 0) { + virReportSystemError(errno, + _("Cannot duplicate FD %d"), + outfds[i]); + goto error; + } + if (virSetInherit(msg->fds[i], false)< 0) { + virReportSystemError(errno, + _("Cannot set close-on-exec %d"), + msg->fds[i]);
That works, but has a slight race window when compared with gnulib's dup_cloexec (in "cloexec.h"), or fcntl(fd, F_DUPFD_CLOEXEC, 0). But it goes back to my comment earlier in the series that auditing the code for atomic cloexec support is a much bigger task for later, so I'm okay with this as-is for now. ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> The RPC server classes are extended to allow FDs to be received from clients with calls. There is not currently any way for a procedure to pass FDs back to the client with replies * daemon/remote.c, src/rpc/gendispatch.pl: Change virNetMessageHeaderPtr param to virNetMessagePtr in dispatcher impls * src/rpc/virnetserver.c, src/rpc/virnetserverclient.c, src/rpc/virnetserverprogram.c, src/rpc/virnetserverprogram.h: Extend to support FD passing --- daemon/remote.c | 125 +++++++++++++++++++++++++++-------------- src/libvirt_private.syms | 1 + src/remote/remote_protocol.x | 2 +- src/rpc/gendispatch.pl | 12 ++-- src/rpc/virnetserver.c | 3 +- src/rpc/virnetserverclient.c | 24 ++++++++ src/rpc/virnetserverprogram.c | 29 +++++++++- src/rpc/virnetserverprogram.h | 2 +- 8 files changed, 143 insertions(+), 55 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 9d70163..f0dd63f 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -586,7 +586,7 @@ int remoteClientInitHook(virNetServerPtr srv ATTRIBUTE_UNUSED, static int remoteDispatchOpen(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, struct remote_open_args *args) { @@ -633,7 +633,7 @@ cleanup: static int remoteDispatchClose(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED) { virNetServerClientDelayedClose(client); @@ -644,7 +644,7 @@ remoteDispatchClose(virNetServerPtr server ATTRIBUTE_UNUSED, static int remoteDispatchDomainGetSchedulerType(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_get_scheduler_type_args *args, remote_domain_get_scheduler_type_ret *ret) @@ -819,7 +819,7 @@ cleanup: static int remoteDispatchDomainGetSchedulerParameters(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_get_scheduler_parameters_args *args, remote_domain_get_scheduler_parameters_ret *ret) @@ -872,7 +872,7 @@ no_memory: static int remoteDispatchDomainGetSchedulerParametersFlags(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_get_scheduler_parameters_flags_args *args, remote_domain_get_scheduler_parameters_flags_ret *ret) @@ -926,7 +926,7 @@ no_memory: static int remoteDispatchDomainMemoryStats(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_memory_stats_args *args, remote_domain_memory_stats_ret *ret) @@ -988,7 +988,7 @@ cleanup: static int remoteDispatchDomainBlockPeek(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_block_peek_args *args, remote_domain_block_peek_ret *ret) @@ -1045,7 +1045,7 @@ cleanup: static int remoteDispatchDomainBlockStatsFlags(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_block_stats_flags_args *args, remote_domain_block_stats_flags_ret *ret) @@ -1116,7 +1116,7 @@ cleanup: static int remoteDispatchDomainMemoryPeek(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_memory_peek_args *args, remote_domain_memory_peek_ret *ret) @@ -1171,7 +1171,7 @@ cleanup: static int remoteDispatchDomainGetSecurityLabel(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_get_security_label_args *args, remote_domain_get_security_label_ret *ret) @@ -1220,7 +1220,7 @@ cleanup: static int remoteDispatchNodeGetSecurityModel(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_node_get_security_model_ret *ret) { @@ -1263,7 +1263,7 @@ cleanup: static int remoteDispatchDomainGetVcpuPinInfo(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_get_vcpu_pin_info_args *args, remote_domain_get_vcpu_pin_info_ret *ret) @@ -1333,7 +1333,7 @@ no_memory: static int remoteDispatchDomainGetVcpus(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_get_vcpus_args *args, remote_domain_get_vcpus_ret *ret) @@ -1418,7 +1418,7 @@ no_memory: static int remoteDispatchDomainMigratePrepare(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_migrate_prepare_args *args, remote_domain_migrate_prepare_ret *ret) @@ -1475,7 +1475,7 @@ cleanup: static int remoteDispatchDomainMigratePrepare2(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_migrate_prepare2_args *args, remote_domain_migrate_prepare2_ret *ret) @@ -1527,7 +1527,7 @@ cleanup: static int remoteDispatchDomainGetMemoryParameters(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_get_memory_parameters_args *args, remote_domain_get_memory_parameters_ret *ret) @@ -1590,7 +1590,7 @@ cleanup: static int remoteDispatchDomainGetBlkioParameters(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_get_blkio_parameters_args *args, remote_domain_get_blkio_parameters_ret *ret) @@ -1653,7 +1653,7 @@ cleanup: static int remoteDispatchNodeGetCPUStats(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_node_get_cpu_stats_args *args, remote_node_get_cpu_stats_ret *ret) @@ -1731,7 +1731,7 @@ no_memory: static int remoteDispatchNodeGetMemoryStats(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_node_get_memory_stats_args *args, remote_node_get_memory_stats_ret *ret) @@ -1809,7 +1809,7 @@ no_memory: static int remoteDispatchDomainGetBlockJobInfo(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_get_block_job_info_args *args, remote_domain_get_block_job_info_ret *ret) @@ -1853,7 +1853,7 @@ cleanup: static int remoteDispatchAuthList(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_auth_list_ret *ret) { @@ -1923,7 +1923,7 @@ cleanup: static int remoteDispatchAuthSaslInit(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_auth_sasl_init_ret *ret) { @@ -2047,7 +2047,7 @@ error: static int remoteDispatchAuthSaslStart(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret) @@ -2145,7 +2145,7 @@ error: static int remoteDispatchAuthSaslStep(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret) @@ -2242,7 +2242,7 @@ error: static int remoteDispatchAuthSaslInit(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_auth_sasl_init_ret *ret ATTRIBUTE_UNUSED) { @@ -2255,7 +2255,7 @@ remoteDispatchAuthSaslInit(virNetServerPtr server ATTRIBUTE_UNUSED, static int remoteDispatchAuthSaslStart(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_auth_sasl_start_args *args ATTRIBUTE_UNUSED, remote_auth_sasl_start_ret *ret ATTRIBUTE_UNUSED) @@ -2269,7 +2269,7 @@ remoteDispatchAuthSaslStart(virNetServerPtr server ATTRIBUTE_UNUSED, static int remoteDispatchAuthSaslStep(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_auth_sasl_step_args *args ATTRIBUTE_UNUSED, remote_auth_sasl_step_ret *ret ATTRIBUTE_UNUSED) @@ -2288,7 +2288,7 @@ remoteDispatchAuthSaslStep(virNetServerPtr server ATTRIBUTE_UNUSED, static int remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_auth_polkit_ret *ret) { @@ -2388,7 +2388,7 @@ authdeny: static int remoteDispatchAuthPolkit(virNetServerPtr server, virNetServerClientPtr client, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_auth_polkit_ret *ret) { @@ -2527,7 +2527,7 @@ authdeny: static int remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_auth_polkit_ret *ret ATTRIBUTE_UNUSED) { @@ -2547,7 +2547,7 @@ remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED, static int remoteDispatchNodeDeviceGetParent(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_node_device_get_parent_args *args, remote_node_device_get_parent_ret *ret) @@ -2602,7 +2602,7 @@ cleanup: static int remoteDispatchDomainEventsRegister(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, remote_domain_events_register_ret *ret ATTRIBUTE_UNUSED) { @@ -2644,7 +2644,7 @@ cleanup: static int remoteDispatchDomainEventsDeregister(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, remote_domain_events_deregister_ret *ret ATTRIBUTE_UNUSED) { @@ -2718,7 +2718,7 @@ cleanup: static int remoteDispatchSecretGetValue(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_secret_get_value_args *args, remote_secret_get_value_ret *ret) @@ -2757,7 +2757,7 @@ cleanup: static int remoteDispatchDomainGetState(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_get_state_args *args, remote_domain_get_state_ret *ret) @@ -2791,7 +2791,7 @@ cleanup: static int remoteDispatchDomainEventsRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, remote_domain_events_register_any_args *args) { @@ -2840,7 +2840,7 @@ cleanup: static int remoteDispatchDomainEventsDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, remote_domain_events_deregister_any_args *args) { @@ -2885,7 +2885,7 @@ cleanup: static int qemuDispatchMonitorCommand(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, qemu_monitor_command_args *args, qemu_monitor_command_ret *ret) @@ -2921,7 +2921,7 @@ cleanup: static int remoteDispatchDomainMigrateBegin3(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_migrate_begin3_args *args, remote_domain_migrate_begin3_ret *ret) @@ -2973,7 +2973,7 @@ cleanup: static int remoteDispatchDomainMigratePrepare3(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_migrate_prepare3_args *args, remote_domain_migrate_prepare3_ret *ret) @@ -3031,7 +3031,7 @@ cleanup: static int remoteDispatchDomainMigratePerform3(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_migrate_perform3_args *args, remote_domain_migrate_perform3_ret *ret) @@ -3087,7 +3087,7 @@ cleanup: static int remoteDispatchDomainMigrateFinish3(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_migrate_finish3_args *args, remote_domain_migrate_finish3_ret *ret) @@ -3141,7 +3141,7 @@ cleanup: static int remoteDispatchDomainMigrateConfirm3(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, - virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr, remote_domain_migrate_confirm3_args *args) { @@ -3175,6 +3175,45 @@ cleanup: } +static int remoteDispatchSupportsFeature( + virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_supports_feature_args *args, + remote_supports_feature_ret *ret) +{ + int rv = -1; + int supported; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + switch (args->feature) { + case VIR_DRV_FEATURE_FD_PASSING: + supported = 1; + break; + + default: + if ((supported = virDrvSupportsFeature(priv->conn, args->feature)) < 0) + goto cleanup; + break; + } + + ret->supported = supported; + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + return rv; +} + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 94520eb..9e4a669 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1190,6 +1190,7 @@ virNetClientHasPassFD; # virnetmessage.h virNetMessageClear; virNetMessageDecodeNumFDs; +virNetMessageDupFD; virNetMessageEncodeHeader; virNetMessageEncodePayload; virNetMessageEncodeNumFDs; diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index d135653..47b8957 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2348,7 +2348,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_SCHEDULER_PARAMETERS = 57, /* skipgen autogen */ REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS = 58, /* autogen autogen */ REMOTE_PROC_GET_HOSTNAME = 59, /* autogen autogen priority:high */ - REMOTE_PROC_SUPPORTS_FEATURE = 60, /* autogen autogen priority:high */ + REMOTE_PROC_SUPPORTS_FEATURE = 60, /* skipgen autogen priority:high */ REMOTE_PROC_DOMAIN_MIGRATE_PREPARE = 61, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_MIGRATE_PERFORM = 62, /* autogen autogen */ diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index b7ac3c8..56af258 100755 --- a/src/rpc/gendispatch.pl +++ b/src/rpc/gendispatch.pl @@ -298,7 +298,7 @@ elsif ($opt_b) { print "static int ${name}(\n"; print " virNetServerPtr server,\n"; print " virNetServerClientPtr client,\n"; - print " virNetMessageHeaderPtr hdr,\n"; + print " virNetMessagePtr msg,\n"; print " virNetMessageErrorPtr rerr"; if ($argtype ne "void") { print ",\n $argtype *args"; @@ -315,13 +315,13 @@ elsif ($opt_b) { print "static int ${name}Helper(\n"; print " virNetServerPtr server,\n"; print " virNetServerClientPtr client,\n"; - print " virNetMessageHeaderPtr hdr,\n"; + print " virNetMessagePtr msg,\n"; print " virNetMessageErrorPtr rerr,\n"; print " void *args$argann,\n"; print " void *ret$retann)\n"; print "{\n"; - print " VIR_DEBUG(\"server=%p client=%p hdr=%p rerr=%p args=%p ret=%p\", server, client, hdr, rerr, args, ret);\n"; - print " return $name(server, client, hdr, rerr"; + print " VIR_DEBUG(\"server=%p client=%p msg=%p rerr=%p args=%p ret=%p\", server, client, msg, rerr, args, ret);\n"; + print " return $name(server, client, msg, rerr"; if ($argtype ne "void") { print ", args"; } @@ -750,7 +750,7 @@ elsif ($opt_b) { print "static int $name(\n"; print " virNetServerPtr server ATTRIBUTE_UNUSED,\n"; print " virNetServerClientPtr client,\n"; - print " virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED,\n"; + print " virNetMessagePtr msg ATTRIBUTE_UNUSED,\n"; print " virNetMessageErrorPtr rerr"; if ($argtype ne "void") { print ",\n $argtype *args"; @@ -809,7 +809,7 @@ elsif ($opt_b) { print " if (!(st = virStreamNew(priv->conn, VIR_STREAM_NONBLOCK)))\n"; print " goto cleanup;\n"; print "\n"; - print " if (!(stream = daemonCreateClientStream(client, st, remoteProgram, hdr)))\n"; + print " if (!(stream = daemonCreateClientStream(client, st, remoteProgram, &msg->header)))\n"; print " goto cleanup;\n"; print "\n"; } diff --git a/src/rpc/virnetserver.c b/src/rpc/virnetserver.c index f739743..6533b5a 100644 --- a/src/rpc/virnetserver.c +++ b/src/rpc/virnetserver.c @@ -138,7 +138,8 @@ static void virNetServerHandleJob(void *jobOpaque, void *opaque) * message types are not expecting replies, so we * must just log it & drop them */ - if (job->msg->header.type == VIR_NET_CALL) { + if (job->msg->header.type == VIR_NET_CALL || + job->msg->header.type == VIR_NET_CALL_WITH_FDS) { if (virNetServerProgramUnknownError(job->client, job->msg, &job->msg->header) < 0) diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c index 05077d6..1256f0f 100644 --- a/src/rpc/virnetserverclient.c +++ b/src/rpc/virnetserverclient.c @@ -770,6 +770,7 @@ readmore: /* Grab the completed message */ virNetMessagePtr msg = virNetMessageQueueServe(&client->rx); virNetServerClientFilterPtr filter; + size_t i; /* Decode the header so we can use it for routing decisions */ if (virNetMessageDecodeHeader(msg) < 0) { @@ -778,6 +779,20 @@ readmore: return; } + if (msg->header.type == VIR_NET_CALL_WITH_FDS && + virNetMessageDecodeNumFDs(msg) < 0) { + virNetMessageFree(msg); + client->wantClose = true; + return; + } + for (i = 0 ; i < msg->nfds ; i++) { + if ((msg->fds[i] = virNetSocketRecvFD(client->sock)) < 0) { + virNetMessageFree(msg); + client->wantClose = true; + return; + } + } + PROBE(RPC_SERVER_CLIENT_MSG_RX, "client=%p len=%zu prog=%u vers=%u proc=%u type=%u status=%u serial=%u", client, msg->bufferLength, @@ -883,6 +898,15 @@ virNetServerClientDispatchWrite(virNetServerClientPtr client) if (client->tx->bufferOffset == client->tx->bufferLength) { virNetMessagePtr msg; + size_t i; + + for (i = 0 ; i < client->tx->nfds ; i++) { + if (virNetSocketSendFD(client->sock, client->tx->fds[i]) < 0) { + client->wantClose = true; + return; + } + } + #if HAVE_SASL /* Completed this 'tx' operation, so now read for all * future rx/tx to be under a SASL SSF layer diff --git a/src/rpc/virnetserverprogram.c b/src/rpc/virnetserverprogram.c index 47b7ded..bbd2d65 100644 --- a/src/rpc/virnetserverprogram.c +++ b/src/rpc/virnetserverprogram.c @@ -29,6 +29,7 @@ #include "memory.h" #include "virterror_internal.h" #include "logging.h" +#include "virfile.h" #define VIR_FROM_THIS VIR_FROM_RPC #define virNetError(code, ...) \ @@ -284,6 +285,7 @@ int virNetServerProgramDispatch(virNetServerProgramPtr prog, switch (msg->header.type) { case VIR_NET_CALL: + case VIR_NET_CALL_WITH_FDS: ret = virNetServerProgramDispatchCall(prog, server, client, msg); break; @@ -314,7 +316,8 @@ int virNetServerProgramDispatch(virNetServerProgramPtr prog, return ret; error: - if (msg->header.type == VIR_NET_CALL) { + if (msg->header.type == VIR_NET_CALL || + msg->header.type == VIR_NET_CALL_WITH_FDS) { ret = virNetServerProgramSendReplyError(prog, client, msg, &rerr, &msg->header); } else { /* Send a dummy reply to free up 'msg' & unblock client rx */ @@ -355,6 +358,7 @@ virNetServerProgramDispatchCall(virNetServerProgramPtr prog, int rv = -1; virNetServerProgramProcPtr dispatcher; virNetMessageError rerr; + size_t i; memset(&rerr, 0, sizeof(rerr)); @@ -409,7 +413,20 @@ virNetServerProgramDispatchCall(virNetServerProgramPtr prog, * * 'args and 'ret' */ - rv = (dispatcher->func)(server, client, &msg->header, &rerr, arg, ret); + rv = (dispatcher->func)(server, client, msg, &rerr, arg, ret); + + /* + * Clear out the FDs we got from the client, we don't + * want to send them back ! + * + * XXX we don't have a way to let dispatcher->func + * return any FDs. Fortunately we don't need this + * capability just yet + */ + for (i = 0 ; i < msg->nfds ; i++) + VIR_FORCE_CLOSE(msg->fds[i]); + VIR_FREE(msg->fds); + msg->nfds = 0; xdr_free(dispatcher->arg_filter, arg); @@ -421,7 +438,7 @@ virNetServerProgramDispatchCall(virNetServerProgramPtr prog, /*msg->header.prog = msg->header.prog;*/ /*msg->header.vers = msg->header.vers;*/ /*msg->header.proc = msg->header.proc;*/ - msg->header.type = VIR_NET_REPLY; + msg->header.type = msg->nfds ? VIR_NET_REPLY_WITH_FDS : VIR_NET_REPLY; /*msg->header.serial = msg->header.serial;*/ msg->header.status = VIR_NET_OK; @@ -430,6 +447,12 @@ virNetServerProgramDispatchCall(virNetServerProgramPtr prog, goto error; } + if (msg->nfds && + virNetMessageEncodeNumFDs(msg) < 0) { + xdr_free(dispatcher->ret_filter, ret); + goto error; + } + if (virNetMessageEncodePayload(msg, dispatcher->ret_filter, ret) < 0) { xdr_free(dispatcher->ret_filter, ret); goto error; diff --git a/src/rpc/virnetserverprogram.h b/src/rpc/virnetserverprogram.h index 9dabf92..aa9f3cf 100644 --- a/src/rpc/virnetserverprogram.h +++ b/src/rpc/virnetserverprogram.h @@ -41,7 +41,7 @@ typedef virNetServerProgramProc *virNetServerProgramProcPtr; typedef int (*virNetServerProgramDispatchFunc)(virNetServerPtr server, virNetServerClientPtr client, - virNetMessageHeaderPtr hdr, + virNetMessagePtr msg, virNetMessageErrorPtr rerr, void *args, void *ret); -- 1.7.6.4

On 10/25/2011 10:03 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
The RPC server classes are extended to allow FDs to be received from clients with calls. There is not currently any way for a procedure to pass FDs back to the client with replies
* daemon/remote.c, src/rpc/gendispatch.pl: Change virNetMessageHeaderPtr param to virNetMessagePtr in dispatcher impls * src/rpc/virnetserver.c, src/rpc/virnetserverclient.c, src/rpc/virnetserverprogram.c, src/rpc/virnetserverprogram.h: Extend to support FD passing --- daemon/remote.c | 125 +++++++++++++++++++++++++++-------------- src/libvirt_private.syms | 1 + src/remote/remote_protocol.x | 2 +- src/rpc/gendispatch.pl | 12 ++-- src/rpc/virnetserver.c | 3 +- src/rpc/virnetserverclient.c | 24 ++++++++ src/rpc/virnetserverprogram.c | 29 +++++++++- src/rpc/virnetserverprogram.h | 2 +- 8 files changed, 143 insertions(+), 55 deletions(-)
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> Since it needs to access file descriptors passed in the msg, the RPC driver for virDomainOpenGraphics needs to be manually implemented. * daemon/remote.c: RPC server dispatcher * src/remote/remote_driver.c: RPC client dispatcher * src/remote/remote_protocol.x: Define protocol --- daemon/remote.c | 43 ++++++++++++++++++++++++ src/remote/remote_driver.c | 73 +++++++++++++++++++++++++++++++++++------ src/remote/remote_protocol.x | 9 ++++- src/remote_protocol-structs | 6 +++ 4 files changed, 119 insertions(+), 12 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index f0dd63f..f5141b9 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -44,6 +44,7 @@ #include "intprops.h" #include "virnetserverservice.h" #include "virnetserver.h" +#include "virfile.h" #include "remote_protocol.h" #include "qemu_protocol.h" @@ -3214,6 +3215,48 @@ cleanup: } +static int +remoteDispatchDomainOpenGraphics(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg, + virNetMessageErrorPtr rerr, + remote_domain_open_graphics_args *args) +{ + virDomainPtr dom = NULL; + int rv = -1; + int fd = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + if ((fd = virNetMessageDupFD(msg, 0)) < 0) + goto cleanup; + + if (virDomainOpenGraphics(dom, + args->idx, + fd, + args->flags) < 0) + goto cleanup; + + rv = 0; + +cleanup: + VIR_FORCE_CLOSE(fd); + if (rv < 0) + virNetMessageSaveError(rerr); + if (dom) + virDomainFree(dom); + return rv; +} + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 382bb42..ea7fb24 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -103,10 +103,14 @@ static void remoteDriverUnlock(struct private_data *driver) virMutexUnlock(&driver->lock); } -static int call (virConnectPtr conn, struct private_data *priv, - unsigned int flags, int proc_nr, - xdrproc_t args_filter, char *args, - xdrproc_t ret_filter, char *ret); +static int call(virConnectPtr conn, struct private_data *priv, + unsigned 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, + unsigned 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, virConnectAuthPtr auth, const char *authtype); #if HAVE_SASL @@ -4122,6 +4126,36 @@ done: } +static int +remoteDomainOpenGraphics(virDomainPtr dom, + unsigned int idx, + int fd, + unsigned int flags) +{ + int rv = -1; + remote_domain_open_graphics_args args; + struct private_data *priv = dom->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain (&args.dom, dom); + args.idx = idx; + args.flags = flags; + + if (callWithFD(dom->conn, priv, 0, fd, REMOTE_PROC_DOMAIN_OPEN_GRAPHICS, + (xdrproc_t) xdr_remote_domain_open_graphics_args, (char *) &args, + (xdrproc_t) xdr_void, NULL) == -1) + goto done; + + rv = 0; + +done: + remoteDriverUnlock(priv); + + return rv; +} + + #include "remote_client_bodies.h" #include "qemu_client_bodies.h" @@ -4130,17 +4164,20 @@ done: * send that to the server and wait for reply */ static int -call (virConnectPtr conn ATTRIBUTE_UNUSED, - struct private_data *priv, - unsigned int flags, - int proc_nr, - xdrproc_t args_filter, char *args, - xdrproc_t ret_filter, char *ret) +callWithFD(virConnectPtr conn ATTRIBUTE_UNUSED, + struct private_data *priv, + unsigned int flags, + int fd, + int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret) { int rv; virNetClientProgramPtr prog = flags & REMOTE_CALL_QEMU ? priv->qemuProgram : priv->remoteProgram; int counter = priv->counter++; virNetClientPtr client = priv->client; + int fds[] = { fd }; + size_t nfds = fd == -1 ? 0 : 1; priv->localUses++; /* Unlock, so that if we get any async events/stream data @@ -4152,7 +4189,7 @@ call (virConnectPtr conn ATTRIBUTE_UNUSED, client, counter, proc_nr, - 0, NULL, NULL, NULL, + nfds, nfds ? fds : NULL, NULL, NULL, args_filter, args, ret_filter, ret); remoteDriverLock(priv); @@ -4161,6 +4198,19 @@ call (virConnectPtr conn ATTRIBUTE_UNUSED, return rv; } +static int +call (virConnectPtr conn, + struct private_data *priv, + unsigned int flags, + int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret) +{ + return callWithFD(conn, priv, flags, -1, proc_nr, + args_filter, args, + ret_filter, ret); +} + static void remoteDomainEventDispatchFunc(virConnectPtr conn, virDomainEventPtr event, @@ -4463,6 +4513,7 @@ static virDriver remote_driver = { .qemuDomainMonitorCommand = remoteQemuDomainMonitorCommand, /* 0.8.3 */ .qemuDomainAttach = qemuDomainAttach, /* 0.9.4 */ .domainOpenConsole = remoteDomainOpenConsole, /* 0.8.6 */ + .domainOpenGraphics = remoteDomainOpenGraphics, /* 0.9.7 */ .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 47b8957..a174af8 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2261,6 +2261,12 @@ struct remote_domain_get_control_info_ret { /* insert@1 */ unsigned hyper stateTime; }; +struct remote_domain_open_graphics_args { + remote_nonnull_domain dom; + unsigned int idx; + unsigned int flags; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -2555,7 +2561,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_RESET = 245, /* autogen autogen */ REMOTE_PROC_DOMAIN_SNAPSHOT_NUM_CHILDREN = 246, /* autogen autogen priority:high */ REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_CHILDREN_NAMES = 247, /* autogen autogen priority:high */ - REMOTE_PROC_DOMAIN_EVENT_DISK_CHANGE = 248 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_EVENT_DISK_CHANGE = 248, /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_OPEN_GRAPHICS = 249 /* skipgen skipgen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 569fcb3..12cedef 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1750,6 +1750,11 @@ struct remote_domain_get_control_info_ret { u_int details; uint64_t stateTime; }; +struct remote_domain_open_graphics_args { + remote_nonnull_domain dom; + u_int idx; + u_int flags; +}; enum remote_procedure { REMOTE_PROC_OPEN = 1, REMOTE_PROC_CLOSE = 2, @@ -1999,4 +2004,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SNAPSHOT_NUM_CHILDREN = 246, REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_CHILDREN_NAMES = 247, REMOTE_PROC_DOMAIN_EVENT_DISK_CHANGE = 248, + REMOTE_PROC_DOMAIN_OPEN_GRAPHICS = 249, }; -- 1.7.6.4

On 10/25/2011 10:03 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
Since it needs to access file descriptors passed in the msg, the RPC driver for virDomainOpenGraphics needs to be manually implemented.
* daemon/remote.c: RPC server dispatcher * src/remote/remote_driver.c: RPC client dispatcher * src/remote/remote_protocol.x: Define protocol --- daemon/remote.c | 43 ++++++++++++++++++++++++ src/remote/remote_driver.c | 73 +++++++++++++++++++++++++++++++++++------ src/remote/remote_protocol.x | 9 ++++- src/remote_protocol-structs | 6 +++ 4 files changed, 119 insertions(+), 12 deletions(-)
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
participants (2)
-
Daniel P. Berrange
-
Eric Blake