From c1b226447781ba8367606f760dde6a88414382a3 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 1 Dec 2010 16:46:36 +0000 Subject: [PATCH] Convert the remote driver to new RPC client APIs This guts the current remote driver, removing all its networking handling code. Instead it calls out to the new virClientPtr and virClientProgramPtr APIs for all RPC & networking work. * src/Makefile.am: Link remote driver with generic RPC code * src/remote/remote_driver.c: Gut code, replacing with RPC API calls * src/rpc/gendispatch.pl: Update for changes in the way streams are handled --- src/Makefile.am | 4 +- src/remote/remote_driver.c | 3451 ++++++------------------------------ src/rpc/gendispatch.pl | 14 +- 3 files changed, 581 insertions(+), 2888 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 4dbf7b1de5..83d267fc90 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -554,11 +554,11 @@ libvirt_la_BUILT_LIBADD += libvirt_driver_remote.la endif libvirt_driver_remote_la_CFLAGS = \ $(GNUTLS_CFLAGS) \ - $(SASL_CFLAGS) $(XDR_CFLAGS) \ -I@top_srcdir@/src/conf \ + -I@top_srcdir@/src/rpc \ $(AM_CFLAGS) libvirt_driver_remote_la_LDFLAGS = $(AM_LDFLAGS) -libvirt_driver_remote_la_LIBADD = $(GNUTLS_LIBS) $(SASL_LIBS) +libvirt_driver_remote_la_LIBADD = $(GNUTLS_LIBS) libvirt-net-rpc-client.la libvirt-net-rpc.la if WITH_DRIVER_MODULES libvirt_driver_remote_la_LIBADD += ../gnulib/lib/libgnu.la libvirt_driver_remote_la_LDFLAGS += -module -avoid-version diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index f0d42a3bcc..2ac87c8de0 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -23,51 +23,14 @@ #include -#include -#include #include -#include #include -#include -#include -#include -#include -#include -#include - -/* Windows socket compatibility functions. */ -#include -#include - -#ifndef HAVE_WINSOCK2_H /* Unix & Cygwin. */ -# include -# include -# include -# include -#endif - -#ifdef HAVE_PWD_H -# include -#endif - -#ifdef HAVE_PATHS_H -# include -#endif -#include -#include -#include -#include -#include "gnutls_1_0_compat.h" -#if HAVE_SASL -# include -#endif #include -#include - -#include - +#include "virnetclient.h" +#include "virnetclientprogram.h" +#include "virnetclientstream.h" #include "virterror_internal.h" #include "logging.h" #include "datatypes.h" @@ -80,7 +43,6 @@ #include "qemu_protocol.h" #include "memory.h" #include "util.h" -#include "ignore-value.h" #include "files.h" #include "command.h" #include "intprops.h" @@ -107,119 +69,27 @@ static int inside_daemon = 0; -struct remote_thread_call; - - -enum { - REMOTE_MODE_WAIT_TX, - REMOTE_MODE_WAIT_RX, - REMOTE_MODE_COMPLETE, - REMOTE_MODE_ERROR, -}; - -struct remote_thread_call { - int mode; - - /* Buffer for outgoing data packet - * 4 byte length, followed by RPC message header+body */ - char buffer[4 + REMOTE_MESSAGE_MAX]; - unsigned int bufferLength; - unsigned int bufferOffset; - - unsigned int serial; - unsigned int proc_nr; - - virCond cond; - - int want_reply; - xdrproc_t ret_filter; - char *ret; - - remote_error err; - - struct remote_thread_call *next; -}; - -struct private_stream_data { - unsigned int has_error : 1; - remote_error err; - - unsigned int serial; - unsigned int proc_nr; +struct private_data { + virMutex lock; - virStreamEventCallback cb; - void *cbOpaque; - virFreeCallback cbFree; - int cbEvents; - int cbTimer; - int cbDispatch; - - /* XXX this is potentially unbounded if the client - * app has domain events registered, since packets - * may be read off wire, while app isn't ready to - * recv them. Figure out how to address this some - * time.... - */ - char *incoming; - unsigned int incomingOffset; - unsigned int incomingLength; + virNetClientPtr client; + virNetClientProgramPtr remoteProgram; + virNetClientProgramPtr qemuProgram; - struct private_stream_data *next; -}; + int counter; /* Serial number for RPC */ -struct private_data { - virMutex lock; + virNetTLSContextPtr tls; - int sock; /* Socket. */ - int errfd; /* File handle connected to remote stderr */ - int watch; /* File handle watch */ - pid_t pid; /* PID of tunnel process */ - 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). */ char *type; /* Cached return from remoteType. */ - int counter; /* Generates serial numbers for RPC. */ int localUses; /* Ref count for private data */ char *hostname; /* Original hostname */ - FILE *debugLog; /* Debug remote protocol */ - -#if HAVE_SASL - sasl_conn_t *saslconn; /* SASL context */ - - const char *saslDecoded; - unsigned int saslDecodedLength; - unsigned int saslDecodedOffset; - - const char *saslEncoded; - unsigned int saslEncodedLength; - unsigned int saslEncodedOffset; - - char saslTemporary[8192]; /* temorary holds data to be decoded */ -#endif - - /* Buffer for incoming data packets - * 4 byte length, followed by RPC message header+body */ - char buffer[4 + REMOTE_MESSAGE_MAX]; - unsigned int bufferLength; - unsigned int bufferOffset; virDomainEventStatePtr domainEventState; - - /* Self-pipe to wakeup threads waiting in poll() */ - int wakeupSendFD; - int wakeupReadFD; - - /* List of threads currently waiting for dispatch */ - struct remote_thread_call *waitDispatch; - - struct private_stream_data *streams; }; enum { - REMOTE_CALL_IN_OPEN = (1 << 0), - REMOTE_CALL_QUIET_MISSING_RPC = (1 << 1), - REMOTE_CALL_QEMU = (1 << 2), - REMOTE_CALL_NONBLOCK = (1 << 3), + REMOTE_CALL_QEMU = (1 << 0), }; @@ -233,22 +103,18 @@ static void remoteDriverUnlock(struct private_data *driver) virMutexUnlock(&driver->lock); } -static int remoteIO(virConnectPtr conn, - struct private_data *priv, - int flags, - struct remote_thread_call *thiscall); 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 remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open, +static int remoteAuthenticate (virConnectPtr conn, struct private_data *priv, virConnectAuthPtr auth, const char *authtype); #if HAVE_SASL -static int remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, +static int remoteAuthSASL (virConnectPtr conn, struct private_data *priv, virConnectAuthPtr auth, const char *mech); #endif #if HAVE_POLKIT -static int remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open, +static int remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, virConnectAuthPtr auth); #endif /* HAVE_POLKIT */ @@ -273,18 +139,13 @@ static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virSt static void make_nonnull_secret (remote_nonnull_secret *secret_dst, virSecretPtr secret_src); static void make_nonnull_nwfilter (remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr nwfilter_src); static void make_nonnull_domain_snapshot (remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src); -void remoteDomainEventFired(int watch, int fd, int event, void *data); -void remoteDomainEventQueueFlush(int timer, void *opaque); -void remoteDomainEventQueue(struct private_data *priv, virDomainEventPtr event); +static void remoteDomainEventQueueFlush(int timer, void *opaque); +static void remoteDomainEventQueue(struct private_data *priv, virDomainEventPtr event); /*----------------------------------------------------------------------*/ /* Helper functions for remoteOpen. */ static char *get_transport_from_scheme (char *scheme); -/* GnuTLS functions used by remoteOpen. */ -static int initialize_gnutls(char *pkipath, int flags); -static gnutls_session_t negotiate_gnutls_on_connection (virConnectPtr conn, struct private_data *priv, int no_verify); - #ifdef WITH_LIBVIRTD static int remoteStartup(int privileged ATTRIBUTE_UNUSED) @@ -299,7 +160,7 @@ remoteStartup(int privileged ATTRIBUTE_UNUSED) #ifndef WIN32 /** - * remoteFindServerPath: + * remoteFindDaemonPath: * * Tries to find the path to the libvirtd binary. * @@ -326,37 +187,76 @@ remoteFindDaemonPath(void) } return NULL; } +#endif -/** - * qemuForkDaemon: - * - * Forks and try to launch the libvirtd daemon - * - * Returns 0 in case of success or -1 in case of detected error. - */ -static int -remoteForkDaemon(void) -{ - const char *daemonPath = remoteFindDaemonPath(); - virCommandPtr cmd = NULL; - int ret; - - if (!daemonPath) { - remoteError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to find libvirtd binary")); - return -1; - } - - cmd = virCommandNewArgList(daemonPath, "--timeout", "30", NULL); - virCommandClearCaps(cmd); - virCommandDaemonize(cmd); - - ret = virCommandRun(cmd, NULL); - virCommandFree(cmd); - return ret; -} -#endif +static void +remoteDomainBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque); +static void +remoteDomainBuildEventReboot(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque); +static void +remoteDomainBuildEventRTCChange(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); +static void +remoteDomainBuildEventWatchdog(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); +static void +remoteDomainBuildEventIOError(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); +static void +remoteDomainBuildEventIOErrorReason(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); +static void +remoteDomainBuildEventGraphics(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); +static void +remoteDomainBuildEventControlError(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + +static virNetClientProgramEvent remoteDomainEvents[] = { + { REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE, + remoteDomainBuildEventRTCChange, + sizeof(remote_domain_event_rtc_change_msg), + (xdrproc_t)xdr_remote_domain_event_rtc_change_msg }, + { REMOTE_PROC_DOMAIN_EVENT_REBOOT, + remoteDomainBuildEventReboot, + sizeof(remote_domain_event_reboot_msg), + (xdrproc_t)xdr_remote_domain_event_reboot_msg }, + { REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE, + remoteDomainBuildEventLifecycle, + sizeof(remote_domain_event_lifecycle_msg), + (xdrproc_t)xdr_remote_domain_event_lifecycle_msg }, + { REMOTE_PROC_DOMAIN_EVENT_WATCHDOG, + remoteDomainBuildEventWatchdog, + sizeof(remote_domain_event_watchdog_msg), + (xdrproc_t)xdr_remote_domain_event_watchdog_msg}, + { REMOTE_PROC_DOMAIN_EVENT_IO_ERROR, + remoteDomainBuildEventIOError, + sizeof(remote_domain_event_io_error_msg), + (xdrproc_t)xdr_remote_domain_event_io_error_msg }, + { REMOTE_PROC_DOMAIN_EVENT_IO_ERROR_REASON, + remoteDomainBuildEventIOErrorReason, + sizeof(remote_domain_event_io_error_reason_msg), + (xdrproc_t)xdr_remote_domain_event_io_error_reason_msg }, + { REMOTE_PROC_DOMAIN_EVENT_GRAPHICS, + remoteDomainBuildEventGraphics, + sizeof(remote_domain_event_graphics_msg), + (xdrproc_t)xdr_remote_domain_event_graphics_msg }, + { REMOTE_PROC_DOMAIN_EVENT_CONTROL_ERROR, + remoteDomainBuildEventControlError, + sizeof(remote_domain_event_control_error_msg), + (xdrproc_t)xdr_remote_domain_event_control_error_msg }, +}; enum virDrvOpenRemoteFlags { VIR_DRV_OPEN_REMOTE_RO = (1 << 0), @@ -389,7 +289,6 @@ doRemoteOpen (virConnectPtr conn, int flags) { struct qparam_set *vars = NULL; - int wakeupFD[2] = { -1, -1 }; char *transport_str = NULL; enum { trans_tls, @@ -445,7 +344,6 @@ doRemoteOpen (virConnectPtr conn, char *port = NULL, *authtype = NULL, *username = NULL; int no_verify = 0, no_tty = 0; char *pkipath = NULL; - virCommandPtr cmd = NULL; /* Return code from this function, and the private data. */ int retcode = VIR_DRV_OPEN_ERROR; @@ -524,12 +422,6 @@ doRemoteOpen (virConnectPtr conn, } else if (STRCASEEQ (var->name, "no_tty")) { no_tty = atoi (var->value); var->ignore = 1; - } else if (STRCASEEQ (var->name, "debug")) { - if (var->value && - STRCASEEQ (var->value, "stdout")) - priv->debugLog = stdout; - else - priv->debugLog = stderr; } else if (STRCASEEQ(var->name, "pkipath")) { VIR_FREE(pkipath); pkipath = strdup(var->value); @@ -601,89 +493,34 @@ doRemoteOpen (virConnectPtr conn, goto failed; } + + VIR_DEBUG("Connecting with transport %d", transport); /* Connect to the remote service. */ switch (transport) { case trans_tls: - if (initialize_gnutls(pkipath, flags) == -1) goto failed; - priv->uses_tls = 1; + priv->tls = virNetTLSContextNewClientPath(pkipath, + geteuid() != 0 ? true : false, + no_verify ? false : true); + if (!priv->tls) + goto failed; priv->is_secure = 1; /*FALLTHROUGH*/ - case trans_tcp: { - /* http://people.redhat.com/drepper/userapi-ipv6.html */ - struct addrinfo *res, *r; - struct addrinfo hints; - int saved_errno = EINVAL; - memset (&hints, 0, sizeof hints); - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_ADDRCONFIG; - int e = getaddrinfo (priv->hostname, port, &hints, &res); - if (e != 0) { - remoteError(VIR_ERR_SYSTEM_ERROR, - _("unable to resolve hostname '%s': %s"), - priv->hostname, gai_strerror (e)); + case trans_tcp: + priv->client = virNetClientNewTCP(priv->hostname, port); + if (!priv->client) goto failed; - } - - /* Try to connect to each returned address in turn. */ - /* XXX This loop contains a subtle problem. In the case - * where a host is accessible over IPv4 and IPv6, it will - * try the IPv4 and IPv6 addresses in turn. However it - * should be able to present different client certificates - * (because the commonName field in a client cert contains - * the client IP address, which is different for IPv4 and - * IPv6). At the moment we only have a single client - * certificate, and no way to specify what address family - * that certificate belongs to. - */ - for (r = res; r; r = r->ai_next) { - int no_slow_start = 1; - - priv->sock = socket (r->ai_family, SOCK_STREAM, 0); - if (priv->sock == -1) { - saved_errno = errno; - continue; - } - - /* Disable Nagle - Dan Berrange. */ - setsockopt (priv->sock, - IPPROTO_TCP, TCP_NODELAY, (void *)&no_slow_start, - sizeof no_slow_start); - - if (connect (priv->sock, r->ai_addr, r->ai_addrlen) == -1) { - saved_errno = errno; - VIR_FORCE_CLOSE(priv->sock); - continue; - } - if (priv->uses_tls) { - priv->session = - negotiate_gnutls_on_connection - (conn, priv, no_verify); - if (!priv->session) { - VIR_FORCE_CLOSE(priv->sock); - goto failed; - } - } - goto tcp_connected; + if (priv->tls) { + VIR_DEBUG("Starting TLS session"); + if (virNetClientSetTLSSession(priv->client, priv->tls) < 0) + goto failed; } - freeaddrinfo (res); - virReportSystemError(saved_errno, - _("unable to connect to libvirtd at '%s'"), - priv->hostname); - goto failed; - - tcp_connected: - freeaddrinfo (res); - - /* NB. All versioning is done by the RPC headers, so we don't - * need to worry (at this point anyway) about versioning. */ break; - } #ifndef WIN32 - case trans_unix: { + case trans_unix: if (!sockname) { if (flags & VIR_DRV_OPEN_REMOTE_USER) { char *userdir = virGetUserDirectory(getuid()); @@ -698,131 +535,59 @@ doRemoteOpen (virConnectPtr conn, VIR_FREE(userdir); } else { if (flags & VIR_DRV_OPEN_REMOTE_RO) - sockname = strdup (LIBVIRTD_PRIV_UNIX_SOCKET_RO); + sockname = strdup(LIBVIRTD_PRIV_UNIX_SOCKET_RO); else - sockname = strdup (LIBVIRTD_PRIV_UNIX_SOCKET); + sockname = strdup(LIBVIRTD_PRIV_UNIX_SOCKET); if (sockname == NULL) goto out_of_memory; } + VIR_DEBUG("Proceeding with sockname %s", sockname); } -# ifndef UNIX_PATH_MAX -# define UNIX_PATH_MAX(addr) (sizeof (addr).sun_path) -# endif - struct sockaddr_un addr; - int trials = 0; - - memset (&addr, 0, sizeof addr); - addr.sun_family = AF_UNIX; - if (virStrcpyStatic(addr.sun_path, sockname) == NULL) { - remoteError(VIR_ERR_INTERNAL_ERROR, - _("Socket %s too big for destination"), sockname); + if (!(priv->client = virNetClientNewUNIX(sockname, + flags & VIR_DRV_OPEN_REMOTE_AUTOSTART, + remoteFindDaemonPath()))) goto failed; - } - if (addr.sun_path[0] == '@') - addr.sun_path[0] = '\0'; - autostart_retry: priv->is_secure = 1; - priv->sock = socket (AF_UNIX, SOCK_STREAM, 0); - if (priv->sock == -1) { - virReportSystemError(errno, "%s", - _("unable to create socket")); - goto failed; - } - if (connect (priv->sock, (struct sockaddr *) &addr, sizeof addr) == -1) { - /* We might have to autostart the daemon in some cases.... - * It takes a short while for the daemon to startup, hence we - * have a number of retries, with a small sleep. This will - * sometimes cause multiple daemons to be started - this is - * ok because the duplicates will fail to bind to the socket - * and immediately exit, leaving just one daemon. - */ - if (errno == ECONNREFUSED && - flags & VIR_DRV_OPEN_REMOTE_AUTOSTART && - trials < 20) { - VIR_FORCE_CLOSE(priv->sock); - if (trials > 0 || - remoteForkDaemon() == 0) { - trials++; - usleep(1000 * 100 * trials); - goto autostart_retry; - } - } - virReportSystemError(errno, - _("unable to connect to '%s', libvirtd may need to be started"), - sockname); - goto failed; - } - break; - } - - case trans_ssh: { - cmd = virCommandNew(command ? command : "ssh"); - /* Generate the final command argv[] array. - * ssh [-p $port] [-l $username] $hostname $netcat -U $sockname */ + case trans_ssh: + command = command ? command : strdup ("ssh"); + if (command == NULL) + goto out_of_memory; - if (port) { - virCommandAddArgList(cmd, "-p", port, NULL); - } - if (username) { - virCommandAddArgList(cmd, "-l", username, NULL); - } - if (no_tty) { - virCommandAddArgList(cmd, "-T", "-o", "BatchMode=yes", "-e", - "none", NULL); + if (!sockname) { + if (flags & VIR_DRV_OPEN_REMOTE_RO) + sockname = strdup(LIBVIRTD_PRIV_UNIX_SOCKET_RO); + else + sockname = strdup(LIBVIRTD_PRIV_UNIX_SOCKET); + if (sockname == NULL) + goto out_of_memory; } - virCommandAddArgList(cmd, priv->hostname, netcat ? netcat : "nc", - "-U", (sockname ? sockname : - (flags & VIR_CONNECT_RO - ? LIBVIRTD_PRIV_UNIX_SOCKET_RO - : LIBVIRTD_PRIV_UNIX_SOCKET)), NULL); - - priv->is_secure = 1; - } - /*FALLTHROUGH*/ - case trans_ext: { - pid_t pid; - int sv[2]; - int errfd[2]; - - /* Fork off the external process. Use socketpair to create a private - * (unnamed) Unix domain socket to the child process so we don't have - * to faff around with two file descriptors (a la 'pipe(2)'). - */ - if (socketpair (PF_UNIX, SOCK_STREAM, 0, sv) == -1) { - virReportSystemError(errno, "%s", - _("unable to create socket pair")); + if (!(priv->client = virNetClientNewSSH(priv->hostname, + port, + command, + username, + no_tty, + netcat ? netcat : "nc", + sockname))) goto failed; - } - if (pipe(errfd) == -1) { - virReportSystemError(errno, "%s", - _("unable to create socket pair")); - goto failed; - } + priv->is_secure = 1; + break; - virCommandSetInputFD(cmd, sv[1]); - virCommandSetOutputFD(cmd, &(sv[1])); - virCommandSetErrorFD(cmd, &(errfd[1])); - virCommandClearCaps(cmd); - if (virCommandRunAsync(cmd, &pid) < 0) + case trans_ext: { + char const *cmd_argv[] = { command, NULL }; + if (!(priv->client = virNetClientNewExternal(cmd_argv))) goto failed; - /* Parent continues here. */ - VIR_FORCE_CLOSE(sv[1]); - VIR_FORCE_CLOSE(errfd[1]); - priv->sock = sv[0]; - priv->errfd = errfd[0]; - priv->pid = pid; - /* Do not set 'is_secure' flag since we can't guarentee * an external program is secure, and this flag must be * pessimistic */ - } + } break; + #else /* WIN32 */ case trans_unix: @@ -834,38 +599,36 @@ doRemoteOpen (virConnectPtr conn, goto failed; #endif /* WIN32 */ - } /* switch (transport) */ - if (virSetNonBlock(priv->sock) < 0) { - virReportSystemError(errno, "%s", - _("unable to make socket non-blocking")); + if (!(priv->remoteProgram = virNetClientProgramNew(REMOTE_PROGRAM, + REMOTE_PROTOCOL_VERSION, + remoteDomainEvents, + ARRAY_CARDINALITY(remoteDomainEvents), + conn))) goto failed; - } - - if ((priv->errfd != -1) && virSetNonBlock(priv->errfd) < 0) { - virReportSystemError(errno, "%s", - _("unable to make socket non-blocking")); + if (!(priv->qemuProgram = virNetClientProgramNew(QEMU_PROGRAM, + QEMU_PROTOCOL_VERSION, + NULL, + 0, + NULL))) goto failed; - } - if (pipe(wakeupFD) < 0) { - virReportSystemError(errno, "%s", - _("unable to make pipe")); + if (virNetClientAddProgram(priv->client, priv->remoteProgram) < 0 || + virNetClientAddProgram(priv->client, priv->qemuProgram) < 0) goto failed; - } - priv->wakeupReadFD = wakeupFD[0]; - priv->wakeupSendFD = wakeupFD[1]; /* Try and authenticate with server */ - if (remoteAuthenticate(conn, priv, 1, auth, authtype) == -1) + VIR_DEBUG("Trying authentication"); + if (remoteAuthenticate(conn, priv, auth, authtype) == -1) goto failed; /* Finally we can call the remote side's open function. */ { remote_open_args args = { &name, flags }; - if (call (conn, priv, REMOTE_CALL_IN_OPEN, REMOTE_PROC_OPEN, + VIR_DEBUG("Trying to open URI %s", name); + if (call (conn, priv, 0, REMOTE_PROC_OPEN, (xdrproc_t) xdr_remote_open_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) goto failed; @@ -874,26 +637,14 @@ doRemoteOpen (virConnectPtr conn, /* Now try and find out what URI the daemon used */ if (conn->uri == NULL) { remote_get_uri_ret uriret; - int urierr; + VIR_DEBUG("Trying to query remote URI"); memset (&uriret, 0, sizeof uriret); - urierr = call (conn, priv, - REMOTE_CALL_IN_OPEN | REMOTE_CALL_QUIET_MISSING_RPC, - REMOTE_PROC_GET_URI, - (xdrproc_t) xdr_void, (char *) NULL, - (xdrproc_t) xdr_remote_get_uri_ret, (char *) &uriret); - if (urierr == -2) { - /* Should not really happen, since we only probe local libvirtd's, - & the library should always match the daemon. Only case is post - RPM upgrade where an old daemon instance is still running with - new client. Too bad. It is not worth the hassle to fix this */ - remoteError(VIR_ERR_INTERNAL_ERROR, "%s", - _("unable to auto-detect URI")); - goto failed; - } - if (urierr == -1) { + if (call (conn, priv, 0, + REMOTE_PROC_GET_URI, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_remote_get_uri_ret, (char *) &uriret) < 0) goto failed; - } VIR_DEBUG("Auto-probed URI is %s", uriret.uri); conn->uri = xmlParseURI(uriret.uri); @@ -904,27 +655,11 @@ doRemoteOpen (virConnectPtr conn, } } - /* Set up a callback to listen on the socket data */ - if ((priv->watch = virEventAddHandle(priv->sock, - VIR_EVENT_HANDLE_READABLE, - remoteDomainEventFired, - conn, NULL)) < 0) { - VIR_DEBUG("virEventAddHandle failed: No addHandleImpl defined." - " continuing without events."); - priv->watch = -1; - } - - priv->domainEventState = virDomainEventStateNew(remoteDomainEventQueueFlush, - conn, - NULL, - false); - if (!priv->domainEventState) { + if (!(priv->domainEventState = virDomainEventStateNew(remoteDomainEventQueueFlush, + conn, + NULL, + false))) goto failed; - } - if (priv->domainEventState->timer < 0 && priv->watch != -1) { - virEventRemoveHandle(priv->watch); - priv->watch = -1; - } /* Successful. */ retcode = VIR_DRV_OPEN_SUCCESS; @@ -938,7 +673,6 @@ doRemoteOpen (virConnectPtr conn, VIR_FREE(netcat); VIR_FREE(username); VIR_FREE(port); - virCommandFree(cmd); VIR_FREE(pkipath); return retcode; @@ -949,30 +683,8 @@ doRemoteOpen (virConnectPtr conn, free_qparam_set (vars); failed: - /* Close the socket if we failed. */ - VIR_FORCE_CLOSE(priv->errfd); - - if (priv->sock >= 0) { - if (priv->uses_tls && priv->session) { - gnutls_bye (priv->session, GNUTLS_SHUT_RDWR); - gnutls_deinit (priv->session); - } - VIR_FORCE_CLOSE(priv->sock); -#ifndef WIN32 - if (priv->pid > 0) { - pid_t reap; - do { -retry: - reap = waitpid(priv->pid, NULL, 0); - if (reap == -1 && errno == EINTR) - goto retry; - } while (reap != -1 && reap != priv->pid); - } -#endif - } - - VIR_FORCE_CLOSE(wakeupFD[0]); - VIR_FORCE_CLOSE(wakeupFD[1]); + virNetClientFree(priv->client); + priv->client = NULL; VIR_FREE(priv->hostname); goto cleanup; @@ -995,9 +707,6 @@ remoteAllocPrivateData(void) } remoteDriverLock(priv); priv->localUses = 1; - priv->watch = -1; - priv->sock = -1; - priv->errfd = -1; return priv; } @@ -1109,577 +818,140 @@ get_transport_from_scheme (char *scheme) return p ? p+1 : 0; } -/* GnuTLS functions used by remoteOpen. */ -static gnutls_certificate_credentials_t x509_cred; +/*----------------------------------------------------------------------*/ static int -check_cert_file(const char *type, const char *file) +doRemoteClose (virConnectPtr conn, struct private_data *priv) { - if (access(file, R_OK)) { - virReportSystemError(errno, - _("Cannot access %s '%s'"), - type, file); + if (call (conn, priv, 0, REMOTE_PROC_CLOSE, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_void, (char *) NULL) == -1) return -1; - } - return 0; -} + virNetTLSContextFree(priv->tls); + priv->tls = NULL; + virNetClientFree(priv->client); + priv->client = NULL; + virNetClientProgramFree(priv->remoteProgram); + virNetClientProgramFree(priv->qemuProgram); + priv->remoteProgram = priv->qemuProgram = NULL; + + /* Free hostname copy */ + VIR_FREE(priv->hostname); + + /* See comment for remoteType. */ + VIR_FREE(priv->type); + + virDomainEventStateFree(priv->domainEventState); + priv->domainEventState = NULL; -static void remote_debug_gnutls_log(int level, const char* str) { - VIR_DEBUG("%d %s", level, str); + return 0; } static int -initialize_gnutls(char *pkipath, int flags) +remoteClose (virConnectPtr conn) { - static int initialized = 0; - int err; - char *gnutlsdebug; - char *libvirt_cacert = NULL; - char *libvirt_clientkey = NULL; - char *libvirt_clientcert = NULL; - int ret = -1; - char *userdir = NULL; - char *user_pki_path = NULL; - - if (initialized) return 0; - - gnutls_global_init (); + int ret = 0; + struct private_data *priv = conn->privateData; - if ((gnutlsdebug = getenv("LIBVIRT_GNUTLS_DEBUG")) != NULL) { - int val; - if (virStrToLong_i(gnutlsdebug, NULL, 10, &val) < 0) - val = 10; - gnutls_global_set_log_level(val); - gnutls_global_set_log_function(remote_debug_gnutls_log); + remoteDriverLock(priv); + priv->localUses--; + if (!priv->localUses) { + ret = doRemoteClose(conn, priv); + conn->privateData = NULL; + remoteDriverUnlock(priv); + virMutexDestroy(&priv->lock); + VIR_FREE (priv); } + if (priv) + remoteDriverUnlock(priv); - /* X509 stuff */ - err = gnutls_certificate_allocate_credentials (&x509_cred); - if (err) { - remoteError(VIR_ERR_GNUTLS_ERROR, - _("unable to allocate TLS credentials: %s"), - gnutls_strerror (err)); - return -1; - } + return ret; +} - if (pkipath) { - if ((virAsprintf(&libvirt_cacert, "%s/%s", pkipath, - "cacert.pem")) < 0) - goto out_of_memory; - if ((virAsprintf(&libvirt_clientkey, "%s/%s", pkipath, - "clientkey.pem")) < 0) - goto out_of_memory; +/* Unfortunately this function is defined to return a static string. + * Since the remote end always answers with the same type (for a + * single connection anyway) we cache the type in the connection's + * private data, and free it when we close the connection. + * + * See also: + * http://www.redhat.com/archives/libvir-list/2007-February/msg00096.html + */ +static const char * +remoteType (virConnectPtr conn) +{ + char *rv = NULL; + remote_get_type_ret ret; + struct private_data *priv = conn->privateData; - if ((virAsprintf(&libvirt_clientcert, "%s/%s", pkipath, - "clientcert.pem")) < 0) - goto out_of_memory; - } else if (flags & VIR_DRV_OPEN_REMOTE_USER || getuid() > 0) { - userdir = virGetUserDirectory(getuid()); + remoteDriverLock(priv); - if (!userdir) - goto out_of_memory; + /* Cached? */ + if (priv->type) { + rv = priv->type; + goto done; + } - if (virAsprintf(&user_pki_path, "%s/.pki/libvirt", userdir) < 0) - goto out_of_memory; + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_GET_TYPE, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_remote_get_type_ret, (char *) &ret) == -1) + goto done; - if ((virAsprintf(&libvirt_cacert, "%s/%s", user_pki_path, - "cacert.pem")) < 0) - goto out_of_memory; + /* Stash. */ + rv = priv->type = ret.type; - if ((virAsprintf(&libvirt_clientkey, "%s/%s", user_pki_path, - "clientkey.pem")) < 0) - goto out_of_memory; +done: + remoteDriverUnlock(priv); + return rv; +} - if ((virAsprintf(&libvirt_clientcert, "%s/%s", user_pki_path, - "clientcert.pem")) < 0) - goto out_of_memory; +static int remoteIsSecure(virConnectPtr conn) +{ + int rv = -1; + struct private_data *priv = conn->privateData; + remote_is_secure_ret ret; + remoteDriverLock(priv); - /* Use the default location of the CA certificate if it - * cannot be found in $HOME/.pki/libvirt - */ - if (!virFileExists(libvirt_cacert)) { - VIR_FREE(libvirt_cacert); + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_IS_SECURE, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_remote_is_secure_ret, (char *) &ret) == -1) + goto done; - libvirt_cacert = strdup(LIBVIRT_CACERT); - if (!libvirt_cacert) goto out_of_memory; - } + /* We claim to be secure, if the remote driver + * transport itself is secure, and the remote + * HV connection is secure + * + * ie, we don't want to claim to be secure if the + * remote driver is used to connect to a XenD + * driver using unencrypted HTTP:/// access + */ + rv = priv->is_secure && ret.secure ? 1 : 0; - /* Use default location as long as one of - * client key, and client certificate cannot be found in - * $HOME/.pki/libvirt, we don't want to make user confused - * with one file is here, the other is there. - */ - if (!virFileExists(libvirt_clientkey) || - !virFileExists(libvirt_clientcert)) { - VIR_FREE(libvirt_clientkey); - VIR_FREE(libvirt_clientcert); - - libvirt_clientkey = strdup(LIBVIRT_CLIENTKEY); - if (!libvirt_clientkey) goto out_of_memory; - - libvirt_clientcert = strdup(LIBVIRT_CLIENTCERT); - if (!libvirt_clientcert) goto out_of_memory; - } - } else { - libvirt_cacert = strdup(LIBVIRT_CACERT); - if (!libvirt_cacert) goto out_of_memory; +done: + remoteDriverUnlock(priv); + return rv; +} - libvirt_clientkey = strdup(LIBVIRT_CLIENTKEY); - if (!libvirt_clientkey) goto out_of_memory; +static int remoteIsEncrypted(virConnectPtr conn) +{ + int rv = -1; + int encrypted = 0; + struct private_data *priv = conn->privateData; + remote_is_secure_ret ret; + remoteDriverLock(priv); - libvirt_clientcert = strdup(LIBVIRT_CLIENTCERT); - if (!libvirt_clientcert) goto out_of_memory; - } - - if (check_cert_file("CA certificate", libvirt_cacert) < 0) - goto error; - if (check_cert_file("client key", libvirt_clientkey) < 0) - goto error; - if (check_cert_file("client certificate", libvirt_clientcert) < 0) - goto error; - - /* Set the trusted CA cert. */ - VIR_DEBUG("loading CA file %s", libvirt_cacert); - err = - gnutls_certificate_set_x509_trust_file (x509_cred, libvirt_cacert, - GNUTLS_X509_FMT_PEM); - if (err < 0) { - remoteError(VIR_ERR_GNUTLS_ERROR, - _("unable to load CA certificate '%s': %s"), - libvirt_cacert, gnutls_strerror (err)); - goto error; - } - - /* Set the client certificate and private key. */ - VIR_DEBUG("loading client cert and key from files %s and %s", - libvirt_clientcert, libvirt_clientkey); - err = - gnutls_certificate_set_x509_key_file (x509_cred, - libvirt_clientcert, - libvirt_clientkey, - GNUTLS_X509_FMT_PEM); - if (err < 0) { - remoteError(VIR_ERR_GNUTLS_ERROR, - _("unable to load private key '%s' and/or " - "certificate '%s': %s"), libvirt_clientkey, - libvirt_clientcert, gnutls_strerror (err)); - goto error; - } - - initialized = 1; - ret = 0; - -cleanup: - VIR_FREE(libvirt_cacert); - VIR_FREE(libvirt_clientkey); - VIR_FREE(libvirt_clientcert); - VIR_FREE(userdir); - VIR_FREE(user_pki_path); - return ret; - -error: - ret = -1; - goto cleanup; - -out_of_memory: - ret = -1; - virReportOOMError(); - goto cleanup; -} - -static int verify_certificate (virConnectPtr conn, struct private_data *priv, gnutls_session_t session); - -#if HAVE_WINSOCK2_H -static ssize_t -custom_gnutls_push(void *s, const void *buf, size_t len) -{ - return send((size_t)s, buf, len, 0); -} - -static ssize_t -custom_gnutls_pull(void *s, void *buf, size_t len) -{ - return recv((size_t)s, buf, len, 0); -} -#endif - -static gnutls_session_t -negotiate_gnutls_on_connection (virConnectPtr conn, - struct private_data *priv, - int no_verify) -{ - bool success = false; - int err; - gnutls_session_t session; - - /* Initialize TLS session - */ - err = gnutls_init (&session, GNUTLS_CLIENT); - if (err) { - remoteError(VIR_ERR_GNUTLS_ERROR, - _("unable to initialize TLS client: %s"), - gnutls_strerror (err)); - return NULL; - } - - /* Use default priorities */ - err = gnutls_set_default_priority (session); - if (err) { - remoteError(VIR_ERR_GNUTLS_ERROR, - _("unable to set TLS algorithm priority: %s"), - gnutls_strerror (err)); - goto cleanup; - } - - /* put the x509 credentials to the current session - */ - err = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred); - if (err) { - remoteError(VIR_ERR_GNUTLS_ERROR, - _("unable to set session credentials: %s"), - gnutls_strerror (err)); - goto cleanup; - } - - gnutls_transport_set_ptr (session, - (gnutls_transport_ptr_t) (long) priv->sock); - -#if HAVE_WINSOCK2_H - /* Make sure GnuTLS uses gnulib's replacment functions for send() and - * recv() on Windows */ - gnutls_transport_set_push_function(session, custom_gnutls_push); - gnutls_transport_set_pull_function(session, custom_gnutls_pull); -#endif - - /* Perform the TLS handshake. */ - again: - err = gnutls_handshake (session); - if (err < 0) { - if (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) - goto again; - remoteError(VIR_ERR_GNUTLS_ERROR, - _("unable to complete TLS handshake: %s"), - gnutls_strerror (err)); - goto cleanup; - } - - /* Verify certificate. */ - if (verify_certificate (conn, priv, session) == -1) { - VIR_DEBUG("failed to verify peer's certificate"); - if (!no_verify) - goto cleanup; - } - - /* At this point, the server is verifying _our_ certificate, IP address, - * etc. If we make the grade, it will send us a '\1' byte. - */ - char buf[1]; - int len; - again_2: - len = gnutls_record_recv (session, buf, 1); - if (len < 0 && len != GNUTLS_E_UNEXPECTED_PACKET_LENGTH) { - if (len == GNUTLS_E_AGAIN || len == GNUTLS_E_INTERRUPTED) - goto again_2; - remoteError(VIR_ERR_GNUTLS_ERROR, - _("unable to complete TLS initialization: %s"), - gnutls_strerror (len)); - goto cleanup; - } - if (len != 1 || buf[0] != '\1') { - remoteError(VIR_ERR_RPC, "%s", - _("server verification (of our certificate or IP " - "address) failed")); - goto cleanup; - } - -#if 0 - /* Print session info. */ - print_info (session); -#endif - - success = true; - -cleanup: - if (!success) { - gnutls_deinit(session); - session = NULL; - } - - return session; -} - -static int -verify_certificate (virConnectPtr conn ATTRIBUTE_UNUSED, - struct private_data *priv, - gnutls_session_t session) -{ - int ret; - unsigned int status; - const gnutls_datum_t *certs; - unsigned int nCerts, i; - time_t now; - - if ((ret = gnutls_certificate_verify_peers2 (session, &status)) < 0) { - remoteError(VIR_ERR_GNUTLS_ERROR, - _("unable to verify server certificate: %s"), - gnutls_strerror (ret)); - return -1; - } - - if ((now = time(NULL)) == ((time_t)-1)) { - virReportSystemError(errno, "%s", - _("cannot get current time")); - return -1; - } - - if (status != 0) { - const char *reason = _("Invalid certificate"); - - if (status & GNUTLS_CERT_INVALID) - reason = _("The certificate is not trusted."); - - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) - reason = _("The certificate hasn't got a known issuer."); - - if (status & GNUTLS_CERT_REVOKED) - reason = _("The certificate has been revoked."); - -#ifndef GNUTLS_1_0_COMPAT - if (status & GNUTLS_CERT_INSECURE_ALGORITHM) - reason = _("The certificate uses an insecure algorithm"); -#endif - - remoteError(VIR_ERR_RPC, - _("server certificate failed validation: %s"), - reason); - return -1; - } - - if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) { - remoteError(VIR_ERR_RPC, "%s",_("Certificate type is not X.509")); - return -1; - } - - if (!(certs = gnutls_certificate_get_peers(session, &nCerts))) { - remoteError(VIR_ERR_RPC, "%s",_("gnutls_certificate_get_peers failed")); - return -1; - } - - for (i = 0 ; i < nCerts ; i++) { - gnutls_x509_crt_t cert; - - ret = gnutls_x509_crt_init (&cert); - if (ret < 0) { - remoteError(VIR_ERR_GNUTLS_ERROR, - _("unable to initialize certificate: %s"), - gnutls_strerror (ret)); - return -1; - } - - ret = gnutls_x509_crt_import (cert, &certs[i], GNUTLS_X509_FMT_DER); - if (ret < 0) { - remoteError(VIR_ERR_GNUTLS_ERROR, - _("unable to import certificate: %s"), - gnutls_strerror (ret)); - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (gnutls_x509_crt_get_expiration_time (cert) < now) { - remoteError(VIR_ERR_RPC, "%s", _("The certificate has expired")); - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (gnutls_x509_crt_get_activation_time (cert) > now) { - remoteError(VIR_ERR_RPC, "%s", - _("The certificate is not yet activated")); - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (i == 0) { - if (!gnutls_x509_crt_check_hostname (cert, priv->hostname)) { - remoteError(VIR_ERR_RPC, - _("Certificate's owner does not match the hostname (%s)"), - priv->hostname); - gnutls_x509_crt_deinit (cert); - return -1; - } - } - } - - return 0; -} - -/*----------------------------------------------------------------------*/ - - -static int -doRemoteClose (virConnectPtr conn, struct private_data *priv) -{ - /* Remove timer before closing the connection, to avoid possible - * remoteDomainEventFired with a free'd connection */ - if (priv->domainEventState->timer >= 0) { - virEventRemoveTimeout(priv->domainEventState->timer); - virEventRemoveHandle(priv->watch); - priv->watch = -1; - priv->domainEventState->timer = -1; - } - - if (call (conn, priv, 0, REMOTE_PROC_CLOSE, - (xdrproc_t) xdr_void, (char *) NULL, - (xdrproc_t) xdr_void, (char *) NULL) == -1) - return -1; - - /* Close socket. */ - if (priv->uses_tls && priv->session) { - gnutls_bye (priv->session, GNUTLS_SHUT_RDWR); - gnutls_deinit (priv->session); - } -#if HAVE_SASL - if (priv->saslconn) - sasl_dispose (&priv->saslconn); -#endif - VIR_FORCE_CLOSE(priv->sock); - VIR_FORCE_CLOSE(priv->errfd); - -#ifndef WIN32 - if (priv->pid > 0) { - pid_t reap; - do { -retry: - reap = waitpid(priv->pid, NULL, 0); - if (reap == -1 && errno == EINTR) - goto retry; - } while (reap != -1 && reap != priv->pid); - } -#endif - VIR_FORCE_CLOSE(priv->wakeupReadFD); - VIR_FORCE_CLOSE(priv->wakeupSendFD); - - - /* Free hostname copy */ - VIR_FREE(priv->hostname); - - /* See comment for remoteType. */ - VIR_FREE(priv->type); - - virDomainEventStateFree(priv->domainEventState); - - return 0; -} - -static int -remoteClose (virConnectPtr conn) -{ - int ret = 0; - struct private_data *priv = conn->privateData; - - remoteDriverLock(priv); - priv->localUses--; - if (!priv->localUses) { - ret = doRemoteClose(conn, priv); - conn->privateData = NULL; - remoteDriverUnlock(priv); - virMutexDestroy(&priv->lock); - VIR_FREE (priv); - } - if (priv) - remoteDriverUnlock(priv); - - return ret; -} - -/* Unfortunately this function is defined to return a static string. - * Since the remote end always answers with the same type (for a - * single connection anyway) we cache the type in the connection's - * private data, and free it when we close the connection. - * - * See also: - * http://www.redhat.com/archives/libvir-list/2007-February/msg00096.html - */ -static const char * -remoteType (virConnectPtr conn) -{ - char *rv = NULL; - remote_get_type_ret ret; - struct private_data *priv = conn->privateData; - - remoteDriverLock(priv); - - /* Cached? */ - if (priv->type) { - rv = priv->type; - goto done; - } - - memset (&ret, 0, sizeof ret); - if (call (conn, priv, 0, REMOTE_PROC_GET_TYPE, - (xdrproc_t) xdr_void, (char *) NULL, - (xdrproc_t) xdr_remote_get_type_ret, (char *) &ret) == -1) - goto done; - - /* Stash. */ - rv = priv->type = ret.type; - -done: - remoteDriverUnlock(priv); - return rv; -} - -static int remoteIsSecure(virConnectPtr conn) -{ - int rv = -1; - struct private_data *priv = conn->privateData; - remote_is_secure_ret ret; - remoteDriverLock(priv); - - memset (&ret, 0, sizeof ret); - if (call (conn, priv, 0, REMOTE_PROC_IS_SECURE, - (xdrproc_t) xdr_void, (char *) NULL, - (xdrproc_t) xdr_remote_is_secure_ret, (char *) &ret) == -1) - goto done; - - /* We claim to be secure, if the remote driver - * transport itself is secure, and the remote - * HV connection is secure - * - * ie, we don't want to claim to be secure if the - * remote driver is used to connect to a XenD - * driver using unencrypted HTTP:/// access - */ - rv = priv->is_secure && ret.secure ? 1 : 0; - -done: - remoteDriverUnlock(priv); - return rv; -} - -static int remoteIsEncrypted(virConnectPtr conn) -{ - int rv = -1; - int encrypted = 0; - struct private_data *priv = conn->privateData; - remote_is_secure_ret ret; - remoteDriverLock(priv); - - memset (&ret, 0, sizeof ret); - if (call (conn, priv, 0, REMOTE_PROC_IS_SECURE, - (xdrproc_t) xdr_void, (char *) NULL, - (xdrproc_t) xdr_remote_is_secure_ret, (char *) &ret) == -1) - goto done; - - if (priv->uses_tls) - encrypted = 1; -#if HAVE_SASL - else if (priv->saslconn) - encrypted = 1; -#endif + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_IS_SECURE, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_remote_is_secure_ret, (char *) &ret) == -1) + goto done; + if (virNetClientIsEncrypted(priv->client)) + encrypted = 1; /* We claim to be encrypted, if the remote driver * transport itself is encrypted, and the remote @@ -2967,7 +2239,6 @@ remoteNWFilterClose(virConnectPtr conn) static int remoteAuthenticate (virConnectPtr conn, struct private_data *priv, - int in_open ATTRIBUTE_UNUSED, virConnectAuthPtr auth ATTRIBUTE_UNUSED, const char *authtype) { @@ -2975,16 +2246,19 @@ remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int err, type = REMOTE_AUTH_NONE; memset(&ret, 0, sizeof ret); - err = call (conn, priv, - REMOTE_CALL_IN_OPEN | REMOTE_CALL_QUIET_MISSING_RPC, + err = call (conn, priv, 0, REMOTE_PROC_AUTH_LIST, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_remote_auth_list_ret, (char *) &ret); - if (err == -2) /* Missing RPC - old server - ignore */ - return 0; - - if (err < 0) + if (err < 0) { + virErrorPtr verr = virGetLastError(); + if (verr && verr->code == VIR_ERR_NO_SUPPORT) { + /* Missing RPC - old server - ignore */ + virResetLastError(); + return 0; + } return -1; + } if (ret.types.types_len == 0) return 0; @@ -3023,7 +2297,7 @@ remoteAuthenticate (virConnectPtr conn, struct private_data *priv, STRCASEEQLEN(authtype, "sasl.", 5)) mech = authtype + 5; - if (remoteAuthSASL(conn, priv, in_open, auth, mech) < 0) { + if (remoteAuthSASL(conn, priv, auth, mech) < 0) { VIR_FREE(ret.types.types_val); return -1; } @@ -3033,7 +2307,7 @@ remoteAuthenticate (virConnectPtr conn, struct private_data *priv, #if HAVE_POLKIT case REMOTE_AUTH_POLKIT: - if (remoteAuthPolkit(conn, priv, in_open, auth) < 0) { + if (remoteAuthPolkit(conn, priv, auth) < 0) { VIR_FREE(ret.types.types_val); return -1; } @@ -3225,11 +2499,9 @@ static void remoteAuthFillInteract(virConnectCredentialPtr cred, /* Perform the SASL authentication process */ static int -remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, +remoteAuthSASL (virConnectPtr conn, struct private_data *priv, virConnectAuthPtr auth, const char *wantmech) { - sasl_conn_t *saslconn = NULL; - sasl_security_properties_t secprops; remote_auth_sasl_init_ret iret; remote_auth_sasl_start_args sargs; remote_auth_sasl_start_ret sret; @@ -3237,48 +2509,22 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, remote_auth_sasl_step_ret pret; const char *clientout; char *serverin = NULL; - unsigned int clientoutlen, serverinlen; + size_t clientoutlen, serverinlen; const char *mech; int err, complete; - virSocketAddr sa; - char *localAddr = NULL, *remoteAddr = NULL; - const void *val; - sasl_ssf_t ssf; + int ssf; sasl_callback_t *saslcb = NULL; sasl_interact_t *interact = NULL; virConnectCredentialPtr cred = NULL; int ncred = 0; int ret = -1; const char *mechlist; + virNetSASLContextPtr saslCtxt; + virNetSASLSessionPtr sasl; VIR_DEBUG("Client initialize SASL authentication"); - /* Sets up the SASL library as a whole */ - err = sasl_client_init(NULL); - if (err != SASL_OK) { - remoteError(VIR_ERR_AUTH_FAILED, - _("failed to initialize SASL library: %d (%s)"), - err, sasl_errstring(err, NULL, NULL)); - goto cleanup; - } - /* Get local address in form IPADDR:PORT */ - sa.len = sizeof(sa.data.stor); - if (getsockname(priv->sock, &sa.data.sa, &sa.len) < 0) { - virReportSystemError(errno, "%s", - _("failed to get sock address")); - goto cleanup; - } - if ((localAddr = virSocketFormatAddrFull(&sa, true, ";")) == NULL) - goto cleanup; - - /* Get remote address in form IPADDR:PORT */ - sa.len = sizeof(sa.data.stor); - if (getpeername(priv->sock, &sa.data.sa, &sa.len) < 0) { - virReportSystemError(errno, "%s", - _("failed to get peer address")); - goto cleanup; - } - if ((remoteAddr = virSocketFormatAddrFull(&sa, true, ";")) == NULL) + if (!(saslCtxt = virNetSASLContextNewClient())) goto cleanup; if (auth) { @@ -3289,63 +2535,37 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, } /* Setup a handle for being a client */ - err = sasl_client_new("libvirt", - priv->hostname, - localAddr, - remoteAddr, - saslcb, - SASL_SUCCESS_DATA, - &saslconn); - - if (err != SASL_OK) { - remoteError(VIR_ERR_AUTH_FAILED, - _("Failed to create SASL client context: %d (%s)"), - err, sasl_errstring(err, NULL, NULL)); + if (!(sasl = virNetSASLSessionNewClient(saslCtxt, + "libvirt", + priv->hostname, + virNetClientLocalAddrString(priv->client), + virNetClientRemoteAddrString(priv->client), + saslcb))) goto cleanup; - } /* Initialize some connection props we care about */ - if (priv->uses_tls) { - gnutls_cipher_algorithm_t cipher; - - cipher = gnutls_cipher_get(priv->session); - if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) { - remoteError(VIR_ERR_INTERNAL_ERROR, "%s", - _("invalid cipher size for TLS session")); + if (priv->tls) { + if ((ssf = virNetClientGetTLSKeySize(priv->client)) < 0) goto cleanup; - } + ssf *= 8; /* key size is bytes, sasl wants bits */ VIR_DEBUG("Setting external SSF %d", ssf); - err = sasl_setprop(saslconn, SASL_SSF_EXTERNAL, &ssf); - if (err != SASL_OK) { - remoteError(VIR_ERR_INTERNAL_ERROR, - _("cannot set external SSF %d (%s)"), - err, sasl_errstring(err, NULL, NULL)); + if (virNetSASLSessionExtKeySize(sasl, ssf) < 0) goto cleanup; - } } - memset (&secprops, 0, sizeof secprops); /* If we've got a secure channel (TLS or UNIX sock), we don't care about SSF */ - secprops.min_ssf = priv->is_secure ? 0 : 56; /* Equiv to DES supported by all Kerberos */ - secprops.max_ssf = priv->is_secure ? 0 : 100000; /* Very strong ! AES == 256 */ - secprops.maxbufsize = 100000; /* If we're not secure, then forbid any anonymous or trivially crackable auth */ - secprops.security_flags = priv->is_secure ? 0 : - SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; - - err = sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops); - if (err != SASL_OK) { - remoteError(VIR_ERR_INTERNAL_ERROR, - _("cannot set security props %d (%s)"), - err, sasl_errstring(err, NULL, NULL)); + if (virNetSASLSessionSecProps(sasl, + priv->is_secure ? 0 : 56, /* Equiv to DES supported by all Kerberos */ + priv->is_secure ? 0 : 100000, /* Very strong ! AES == 256 */ + priv->is_secure ? true : false) < 0) goto cleanup; - } /* First call is to inquire about supported mechanisms in the server */ memset (&iret, 0, sizeof iret); - if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_INIT, + if (call (conn, priv, 0, REMOTE_PROC_AUTH_SASL_INIT, (xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_remote_auth_sasl_init_ret, (char *) &iret) != 0) goto cleanup; @@ -3365,22 +2585,16 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, restart: /* Start the auth negotiation on the client end first */ VIR_DEBUG("Client start negotiation mechlist '%s'", mechlist); - err = sasl_client_start(saslconn, - mechlist, - &interact, - &clientout, - &clientoutlen, - &mech); - if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) { - remoteError(VIR_ERR_AUTH_FAILED, - _("Failed to start SASL negotiation: %d (%s)"), - err, sasl_errdetail(saslconn)); - VIR_FREE(iret.mechlist); + if ((err = virNetSASLSessionClientStart(sasl, + mechlist, + &interact, + &clientout, + &clientoutlen, + &mech)) < 0) goto cleanup; - } /* Need to gather some credentials from the client */ - if (err == SASL_INTERACT) { + if (err == VIR_NET_SASL_INTERACT) { const char *msg; if (cred) { remoteAuthFreeCredentials(cred, ncred); @@ -3409,7 +2623,7 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, if (clientoutlen > REMOTE_AUTH_SASL_DATA_MAX) { remoteError(VIR_ERR_AUTH_FAILED, - _("SASL negotiation data too long: %d bytes"), + _("SASL negotiation data too long: %zu bytes"), clientoutlen); goto cleanup; } @@ -3419,11 +2633,12 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, sargs.data.data_val = (char*)clientout; sargs.data.data_len = clientoutlen; sargs.mech = (char*)mech; - VIR_DEBUG("Server start negotiation with mech %s. Data %d bytes %p", mech, clientoutlen, clientout); + VIR_DEBUG("Server start negotiation with mech %s. Data %zu bytes %p", + mech, clientoutlen, clientout); /* Now send the initial auth data to the server */ memset (&sret, 0, sizeof sret); - if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_START, + if (call (conn, priv, 0, REMOTE_PROC_AUTH_SASL_START, (xdrproc_t) xdr_remote_auth_sasl_start_args, (char *) &sargs, (xdrproc_t) xdr_remote_auth_sasl_start_ret, (char *) &sret) != 0) goto cleanup; @@ -3432,28 +2647,24 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, /* NB, distinction of NULL vs "" is *critical* in SASL */ serverin = sret.nil ? NULL : sret.data.data_val; serverinlen = sret.data.data_len; - VIR_DEBUG("Client step result complete: %d. Data %d bytes %p", - complete, serverinlen, serverin); + VIR_DEBUG("Client step result complete: %d. Data %zu bytes %p", + complete, serverinlen, serverin); /* Loop-the-loop... * Even if the server has completed, the client must *always* do at least one step * in this loop to verify the server isn't lying about something. Mutual auth */ for (;;) { restep: - err = sasl_client_step(saslconn, - serverin, - serverinlen, - &interact, - &clientout, - &clientoutlen); - if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) { - remoteError(VIR_ERR_AUTH_FAILED, - _("Failed SASL step: %d (%s)"), - err, sasl_errdetail(saslconn)); + if ((err = virNetSASLSessionClientStep(sasl, + serverin, + serverinlen, + &interact, + &clientout, + &clientoutlen)) < 0) goto cleanup; - } + /* Need to gather some credentials from the client */ - if (err == SASL_INTERACT) { + if (err == VIR_NET_SASL_INTERACT) { const char *msg; if (cred) { remoteAuthFreeCredentials(cred, ncred); @@ -3479,10 +2690,11 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, } VIR_FREE(serverin); - VIR_DEBUG("Client step result %d. Data %d bytes %p", err, clientoutlen, clientout); + VIR_DEBUG("Client step result %d. Data %zu bytes %p", + err, clientoutlen, clientout); /* Previous server call showed completion & we're now locally complete too */ - if (complete && err == SASL_OK) + if (complete && err == VIR_NET_SASL_COMPLETE) break; /* Not done, prepare to talk with the server for another iteration */ @@ -3491,10 +2703,11 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, pargs.nil = clientout ? 0 : 1; pargs.data.data_val = (char*)clientout; pargs.data.data_len = clientoutlen; - VIR_DEBUG("Server step with %d bytes %p", clientoutlen, clientout); + VIR_DEBUG("Server step with %zu bytes %p", + clientoutlen, clientout); memset (&pret, 0, sizeof pret); - if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_STEP, + if (call (conn, priv, 0, REMOTE_PROC_AUTH_SASL_STEP, (xdrproc_t) xdr_remote_auth_sasl_step_args, (char *) &pargs, (xdrproc_t) xdr_remote_auth_sasl_step_ret, (char *) &pret) != 0) goto cleanup; @@ -3504,11 +2717,11 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, serverin = pret.nil ? NULL : pret.data.data_val; serverinlen = pret.data.data_len; - VIR_DEBUG("Client step result complete: %d. Data %d bytes %p", - complete, serverinlen, serverin); + VIR_DEBUG("Client step result complete: %d. Data %zu bytes %p", + complete, serverinlen, serverin); /* This server call shows complete, and earlier client step was OK */ - if (complete && err == SASL_OK) { + if (complete && err == VIR_NET_SASL_COMPLETE) { VIR_FREE(serverin); break; } @@ -3516,14 +2729,9 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, /* Check for suitable SSF if not already secure (TLS or UNIX sock) */ if (!priv->is_secure) { - err = sasl_getprop(saslconn, SASL_SSF, &val); - if (err != SASL_OK) { - remoteError(VIR_ERR_AUTH_FAILED, - _("cannot query SASL ssf on connection %d (%s)"), - err, sasl_errstring(err, NULL, NULL)); + if ((ssf = virNetSASLSessionGetKeySize(sasl)) < 0) goto cleanup; - } - ssf = *(const int *)val; + VIR_DEBUG("SASL SSF value %d", ssf); if (ssf < 56) { /* 56 == DES level, good for Kerberos */ remoteError(VIR_ERR_AUTH_FAILED, @@ -3534,18 +2742,16 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, } VIR_DEBUG("SASL authentication complete"); - priv->saslconn = saslconn; + virNetClientSetSASLSession(priv->client, sasl); ret = 0; cleanup: - VIR_FREE(localAddr); - VIR_FREE(remoteAddr); VIR_FREE(serverin); VIR_FREE(saslcb); remoteAuthFreeCredentials(cred, ncred); - if (ret != 0 && saslconn) - sasl_dispose(&saslconn); + virNetSASLSessionFree(sasl); + virNetSASLContextFree(saslCtxt); return ret; } @@ -3555,14 +2761,14 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, #if HAVE_POLKIT # if HAVE_POLKIT1 static int -remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open, +remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, virConnectAuthPtr auth ATTRIBUTE_UNUSED) { remote_auth_polkit_ret ret; VIR_DEBUG("Client initialize PolicyKit-1 authentication"); memset (&ret, 0, sizeof ret); - if (call (conn, priv, in_open, REMOTE_PROC_AUTH_POLKIT, + if (call (conn, priv, 0, REMOTE_PROC_AUTH_POLKIT, (xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_remote_auth_polkit_ret, (char *) &ret) != 0) { return -1; /* virError already set by call */ @@ -3575,7 +2781,7 @@ remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open, /* Perform the PolicyKit authentication process */ static int -remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open, +remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, virConnectAuthPtr auth) { remote_auth_polkit_ret ret; @@ -3613,7 +2819,7 @@ remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open, } memset (&ret, 0, sizeof ret); - if (call (conn, priv, in_open, REMOTE_PROC_AUTH_POLKIT, + if (call (conn, priv, 0, REMOTE_PROC_AUTH_POLKIT, (xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_remote_auth_polkit_ret, (char *) &ret) != 0) { return -1; /* virError already set by call */ @@ -3694,184 +2900,155 @@ done: return rv; } -/** - * remoteDomainReadEventLifecycle - * - * Read the domain lifecycle event data off the wire - */ -static virDomainEventPtr -remoteDomainReadEventLifecycle(virConnectPtr conn, XDR *xdr) + +static void +remoteDomainBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) { - remote_domain_event_lifecycle_msg msg; + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_lifecycle_msg *msg = evdata; virDomainPtr dom; virDomainEventPtr event = NULL; - memset (&msg, 0, sizeof msg); - - /* unmarshal parameters, and process it*/ - if (! xdr_remote_domain_event_lifecycle_msg(xdr, &msg) ) { - remoteError(VIR_ERR_RPC, "%s", - _("Unable to demarshal lifecycle event")); - return NULL; - } - dom = get_nonnull_domain(conn,msg.dom); + dom = get_nonnull_domain(conn, msg->dom); if (!dom) - return NULL; - - event = virDomainEventNewFromDom(dom, msg.event, msg.detail); - xdr_free ((xdrproc_t) &xdr_remote_domain_event_lifecycle_msg, (char *) &msg); + return; + event = virDomainEventNewFromDom(dom, msg->event, msg->detail); virDomainFree(dom); - return event; + + remoteDomainEventQueue(priv, event); } -static virDomainEventPtr -remoteDomainReadEventReboot(virConnectPtr conn, XDR *xdr) +static void +remoteDomainBuildEventReboot(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) { - remote_domain_event_reboot_msg msg; + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_reboot_msg *msg = evdata; virDomainPtr dom; virDomainEventPtr event = NULL; - memset (&msg, 0, sizeof msg); - - /* unmarshal parameters, and process it*/ - if (! xdr_remote_domain_event_reboot_msg(xdr, &msg) ) { - remoteError(VIR_ERR_RPC, "%s", - _("Unable to demarshal reboot event")); - return NULL; - } - dom = get_nonnull_domain(conn,msg.dom); + dom = get_nonnull_domain(conn, msg->dom); if (!dom) - return NULL; + return; event = virDomainEventRebootNewFromDom(dom); - xdr_free ((xdrproc_t) &xdr_remote_domain_event_reboot_msg, (char *) &msg); - virDomainFree(dom); - return event; + + remoteDomainEventQueue(priv, event); } -static virDomainEventPtr -remoteDomainReadEventRTCChange(virConnectPtr conn, XDR *xdr) +static void +remoteDomainBuildEventRTCChange(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) { - remote_domain_event_rtc_change_msg msg; + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_rtc_change_msg *msg = evdata; virDomainPtr dom; virDomainEventPtr event = NULL; - memset (&msg, 0, sizeof msg); - - /* unmarshal parameters, and process it*/ - if (! xdr_remote_domain_event_rtc_change_msg(xdr, &msg) ) { - remoteError(VIR_ERR_RPC, "%s", - _("Unable to demarshal RTC change event")); - return NULL; - } - dom = get_nonnull_domain(conn,msg.dom); + dom = get_nonnull_domain(conn, msg->dom); if (!dom) - return NULL; - - event = virDomainEventRTCChangeNewFromDom(dom, msg.offset); - xdr_free ((xdrproc_t) &xdr_remote_domain_event_rtc_change_msg, (char *) &msg); + return; + event = virDomainEventRTCChangeNewFromDom(dom, msg->offset); virDomainFree(dom); - return event; + + remoteDomainEventQueue(priv, event); } -static virDomainEventPtr -remoteDomainReadEventWatchdog(virConnectPtr conn, XDR *xdr) +static void +remoteDomainBuildEventWatchdog(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) { - remote_domain_event_watchdog_msg msg; + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_watchdog_msg *msg = evdata; virDomainPtr dom; virDomainEventPtr event = NULL; - memset (&msg, 0, sizeof msg); - - /* unmarshal parameters, and process it*/ - if (! xdr_remote_domain_event_watchdog_msg(xdr, &msg) ) { - remoteError(VIR_ERR_RPC, "%s", - _("Unable to demarshal watchdog event")); - return NULL; - } - dom = get_nonnull_domain(conn,msg.dom); + dom = get_nonnull_domain(conn, msg->dom); if (!dom) - return NULL; - - event = virDomainEventWatchdogNewFromDom(dom, msg.action); - xdr_free ((xdrproc_t) &xdr_remote_domain_event_watchdog_msg, (char *) &msg); + return; + event = virDomainEventWatchdogNewFromDom(dom, msg->action); virDomainFree(dom); - return event; + + remoteDomainEventQueue(priv, event); } -static virDomainEventPtr -remoteDomainReadEventIOError(virConnectPtr conn, XDR *xdr) +static void +remoteDomainBuildEventIOError(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) { - remote_domain_event_io_error_msg msg; + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_io_error_msg *msg = evdata; virDomainPtr dom; virDomainEventPtr event = NULL; - memset (&msg, 0, sizeof msg); - - /* unmarshal parameters, and process it*/ - if (! xdr_remote_domain_event_io_error_msg(xdr, &msg) ) { - remoteError(VIR_ERR_RPC, "%s", - _("Unable to demarshal IO error event")); - return NULL; - } - dom = get_nonnull_domain(conn,msg.dom); + dom = get_nonnull_domain(conn, msg->dom); if (!dom) - return NULL; + return; event = virDomainEventIOErrorNewFromDom(dom, - msg.srcPath, - msg.devAlias, - msg.action); - xdr_free ((xdrproc_t) &xdr_remote_domain_event_io_error_msg, (char *) &msg); - + msg->srcPath, + msg->devAlias, + msg->action); virDomainFree(dom); - return event; + + remoteDomainEventQueue(priv, event); } -static virDomainEventPtr -remoteDomainReadEventIOErrorReason(virConnectPtr conn, XDR *xdr) +static void +remoteDomainBuildEventIOErrorReason(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) { - remote_domain_event_io_error_reason_msg msg; + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_io_error_reason_msg *msg = evdata; virDomainPtr dom; virDomainEventPtr event = NULL; - memset (&msg, 0, sizeof msg); - - /* unmarshal parameters, and process it*/ - if (! xdr_remote_domain_event_io_error_reason_msg(xdr, &msg) ) { - remoteError(VIR_ERR_RPC, "%s", - _("Unable to demarshal IO error reason event")); - return NULL; - } - dom = get_nonnull_domain(conn,msg.dom); + dom = get_nonnull_domain(conn,msg->dom); if (!dom) - return NULL; + return; event = virDomainEventIOErrorReasonNewFromDom(dom, - msg.srcPath, - msg.devAlias, - msg.action, - msg.reason); - xdr_free ((xdrproc_t) &xdr_remote_domain_event_io_error_reason_msg, (char *) &msg); + msg->srcPath, + msg->devAlias, + msg->action, + msg->reason); virDomainFree(dom); - return event; + + remoteDomainEventQueue(priv, event); } -static virDomainEventPtr -remoteDomainReadEventGraphics(virConnectPtr conn, XDR *xdr) +static void +remoteDomainBuildEventGraphics(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) { - remote_domain_event_graphics_msg msg; + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_graphics_msg *msg = evdata; virDomainPtr dom; virDomainEventPtr event = NULL; virDomainEventGraphicsAddressPtr localAddr = NULL; @@ -3879,58 +3056,48 @@ remoteDomainReadEventGraphics(virConnectPtr conn, XDR *xdr) virDomainEventGraphicsSubjectPtr subject = NULL; int i; - memset (&msg, 0, sizeof msg); - - /* unmarshal parameters, and process it*/ - if (! xdr_remote_domain_event_graphics_msg(xdr, &msg) ) { - remoteError(VIR_ERR_RPC, "%s", - _("Unable to demarshal graphics event")); - return NULL; - } - - dom = get_nonnull_domain(conn,msg.dom); + dom = get_nonnull_domain(conn, msg->dom); if (!dom) - return NULL; + return; if (VIR_ALLOC(localAddr) < 0) goto no_memory; - localAddr->family = msg.local.family; - if (!(localAddr->service = strdup(msg.local.service)) || - !(localAddr->node = strdup(msg.local.node))) + localAddr->family = msg->local.family; + if (!(localAddr->service = strdup(msg->local.service)) || + !(localAddr->node = strdup(msg->local.node))) goto no_memory; if (VIR_ALLOC(remoteAddr) < 0) goto no_memory; - remoteAddr->family = msg.remote.family; - if (!(remoteAddr->service = strdup(msg.remote.service)) || - !(remoteAddr->node = strdup(msg.remote.node))) + remoteAddr->family = msg->remote.family; + if (!(remoteAddr->service = strdup(msg->remote.service)) || + !(remoteAddr->node = strdup(msg->remote.node))) goto no_memory; if (VIR_ALLOC(subject) < 0) goto no_memory; - if (VIR_ALLOC_N(subject->identities, msg.subject.subject_len) < 0) + if (VIR_ALLOC_N(subject->identities, msg->subject.subject_len) < 0) goto no_memory; - subject->nidentity = msg.subject.subject_len; + subject->nidentity = msg->subject.subject_len; for (i = 0 ; i < subject->nidentity ; i++) { - if (!(subject->identities[i].type = strdup(msg.subject.subject_val[i].type)) || - !(subject->identities[i].name = strdup(msg.subject.subject_val[i].name))) + if (!(subject->identities[i].type = strdup(msg->subject.subject_val[i].type)) || + !(subject->identities[i].name = strdup(msg->subject.subject_val[i].name))) goto no_memory; } event = virDomainEventGraphicsNewFromDom(dom, - msg.phase, + msg->phase, localAddr, remoteAddr, - msg.authScheme, + msg->authScheme, subject); - xdr_free ((xdrproc_t) &xdr_remote_domain_event_graphics_msg, (char *) &msg); virDomainFree(dom); - return event; -no_memory: - xdr_free ((xdrproc_t) &xdr_remote_domain_event_graphics_msg, (char *) &msg); + remoteDomainEventQueue(priv, event); + return; +no_memory: if (localAddr) { VIR_FREE(localAddr->service); VIR_FREE(localAddr->node); @@ -3949,34 +3116,31 @@ no_memory: VIR_FREE(subject->identities); VIR_FREE(subject); } - return NULL; + return; } -static virDomainEventPtr -remoteDomainReadEventControlError(virConnectPtr conn, XDR *xdr) +static void +remoteDomainBuildEventControlError(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) { - remote_domain_event_control_error_msg msg; + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_control_error_msg *msg = evdata; virDomainPtr dom; virDomainEventPtr event = NULL; - memset (&msg, 0, sizeof msg); - - /* unmarshall parameters, and process it*/ - if (! xdr_remote_domain_event_control_error_msg(xdr, &msg) ) { - remoteError(VIR_ERR_RPC, "%s", - _("unable to demarshall reboot event")); - return NULL; - } - dom = get_nonnull_domain(conn,msg.dom); + dom = get_nonnull_domain(conn, msg->dom); if (!dom) - return NULL; + return; event = virDomainEventControlErrorNewFromDom(dom); xdr_free ((xdrproc_t) &xdr_remote_domain_event_control_error_msg, (char *) &msg); virDomainFree(dom); - return event; + + remoteDomainEventQueue(priv, event); } @@ -4020,195 +3184,6 @@ done: return rv; } -static struct private_stream_data * -remoteStreamOpen(virStreamPtr st, - unsigned int proc_nr, - unsigned int serial) -{ - struct private_data *priv = st->conn->privateData; - struct private_stream_data *stpriv; - - if (VIR_ALLOC(stpriv) < 0) { - virReportOOMError(); - return NULL; - } - - /* Initialize call object used to receive replies */ - stpriv->proc_nr = proc_nr; - stpriv->serial = serial; - - stpriv->next = priv->streams; - priv->streams = stpriv; - - return stpriv; -} - - -static void -remoteStreamEventTimerUpdate(struct private_stream_data *privst) -{ - if (!privst->cb) - return; - - VIR_DEBUG("Check timer offset=%d %d", privst->incomingOffset, privst->cbEvents); - if ((privst->incomingOffset && - (privst->cbEvents & VIR_STREAM_EVENT_READABLE)) || - (privst->cbEvents & VIR_STREAM_EVENT_WRITABLE)) { - VIR_DEBUG("Enabling event timer"); - virEventUpdateTimeout(privst->cbTimer, 0); - } else { - VIR_DEBUG("Disabling event timer"); - virEventUpdateTimeout(privst->cbTimer, -1); - } -} - - -static int -remoteStreamPacket(virStreamPtr st, - int status, - const char *data, - size_t nbytes) -{ - VIR_DEBUG("st=%p status=%d data=%p nbytes=%zu", st, status, data, nbytes); - struct private_data *priv = st->conn->privateData; - struct private_stream_data *privst = st->privateData; - XDR xdr; - struct remote_thread_call *thiscall; - remote_message_header hdr; - int ret; - - memset(&hdr, 0, sizeof hdr); - - if (VIR_ALLOC(thiscall) < 0) { - virReportOOMError(); - return -1; - } - - thiscall->mode = REMOTE_MODE_WAIT_TX; - thiscall->serial = privst->serial; - thiscall->proc_nr = privst->proc_nr; - if (status == REMOTE_OK || - status == REMOTE_ERROR) - thiscall->want_reply = 1; - - if (virCondInit(&thiscall->cond) < 0) { - VIR_FREE(thiscall); - remoteError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot initialize mutex")); - return -1; - } - - /* Don't fill in any other fields in 'thiscall' since - * we're not expecting a reply for this */ - - hdr.prog = REMOTE_PROGRAM; - hdr.vers = REMOTE_PROTOCOL_VERSION; - hdr.proc = privst->proc_nr; - hdr.type = REMOTE_STREAM; - hdr.serial = privst->serial; - hdr.status = status; - - - /* Length must include the length word itself (always encoded in - * 4 bytes as per RFC 4506), so offset start length. We write this - * later. - */ - thiscall->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; - - /* Serialise header followed by args. */ - xdrmem_create (&xdr, thiscall->buffer + thiscall->bufferLength, - REMOTE_MESSAGE_MAX, XDR_ENCODE); - if (!xdr_remote_message_header (&xdr, &hdr)) { - remoteError(VIR_ERR_RPC, "%s", _("xdr_remote_message_header failed")); - goto error; - } - - thiscall->bufferLength += xdr_getpos (&xdr); - xdr_destroy (&xdr); - - if (status == REMOTE_CONTINUE) { - if (((4 + REMOTE_MESSAGE_MAX) - thiscall->bufferLength) < nbytes) { - remoteError(VIR_ERR_RPC, _("data size %zu too large for payload %d"), - nbytes, ((4 + REMOTE_MESSAGE_MAX) - thiscall->bufferLength)); - goto error; - } - - memcpy(thiscall->buffer + thiscall->bufferLength, data, nbytes); - thiscall->bufferLength += nbytes; - } - - /* Go back to packet start and encode the length word. */ - xdrmem_create (&xdr, thiscall->buffer, REMOTE_MESSAGE_HEADER_XDR_LEN, XDR_ENCODE); - if (!xdr_u_int (&xdr, &thiscall->bufferLength)) { - remoteError(VIR_ERR_RPC, "%s", _("xdr_u_int (length word)")); - goto error; - } - xdr_destroy (&xdr); - - ret = remoteIO(st->conn, priv, 0, thiscall); - ignore_value(virCondDestroy(&thiscall->cond)); - VIR_FREE(thiscall); - if (ret < 0) - return -1; - - return nbytes; - -error: - xdr_destroy (&xdr); - ignore_value(virCondDestroy(&thiscall->cond)); - VIR_FREE(thiscall); - return -1; -} - -static int -remoteStreamHasError(virStreamPtr st) { - struct private_stream_data *privst = st->privateData; - if (!privst->has_error) { - return 0; - } - - VIR_DEBUG("Raising async error"); - virRaiseErrorFull(__FILE__, __FUNCTION__, __LINE__, - privst->err.domain, - privst->err.code, - privst->err.level, - privst->err.str1 ? *privst->err.str1 : NULL, - privst->err.str2 ? *privst->err.str2 : NULL, - privst->err.str3 ? *privst->err.str3 : NULL, - privst->err.int1, - privst->err.int2, - "%s", privst->err.message ? *privst->err.message : NULL); - - return 1; -} - -static void -remoteStreamRelease(virStreamPtr st) -{ - struct private_data *priv = st->conn->privateData; - struct private_stream_data *privst = st->privateData; - - if (priv->streams == privst) - priv->streams = privst->next; - else { - struct private_stream_data *tmp = priv->streams; - while (tmp && tmp->next) { - if (tmp->next == privst) { - tmp->next = privst->next; - break; - } - } - } - - if (privst->has_error) - xdr_free((xdrproc_t)xdr_remote_error, (char *)&privst->err); - - VIR_FREE(privst); - - st->driver = NULL; - st->privateData = NULL; -} - static int remoteStreamSend(virStreamPtr st, @@ -4217,22 +3192,21 @@ remoteStreamSend(virStreamPtr st, { VIR_DEBUG("st=%p data=%p nbytes=%zu", st, data, nbytes); struct private_data *priv = st->conn->privateData; + virNetClientStreamPtr privst = st->privateData; int rv = -1; remoteDriverLock(priv); - if (remoteStreamHasError(st)) + if (virNetClientStreamRaiseError(privst)) goto cleanup; - rv = remoteStreamPacket(st, - REMOTE_CONTINUE, - data, - nbytes); + rv = virNetClientStreamSendPacket(privst, + priv->client, + VIR_NET_CONTINUE, + data, + nbytes); cleanup: - if (rv == -1) - remoteStreamRelease(st); - remoteDriverUnlock(priv); return rv; @@ -4246,123 +3220,57 @@ remoteStreamRecv(virStreamPtr st, { VIR_DEBUG("st=%p data=%p nbytes=%zu", st, data, nbytes); struct private_data *priv = st->conn->privateData; - struct private_stream_data *privst = st->privateData; + virNetClientStreamPtr privst = st->privateData; int rv = -1; remoteDriverLock(priv); - if (remoteStreamHasError(st)) + if (virNetClientStreamRaiseError(privst)) goto cleanup; - if (!privst->incomingOffset) { - struct remote_thread_call *thiscall; - int ret; - - if (st->flags & VIR_STREAM_NONBLOCK) { - VIR_DEBUG("Non-blocking mode and no data available"); - rv = -2; - goto cleanup; - } - - if (VIR_ALLOC(thiscall) < 0) { - virReportOOMError(); - goto cleanup; - } - - /* We're not really doing an RPC calls, so we're - * skipping straight to RX part */ - thiscall->mode = REMOTE_MODE_WAIT_RX; - thiscall->serial = privst->serial; - thiscall->proc_nr = privst->proc_nr; - thiscall->want_reply = 1; - - if (virCondInit(&thiscall->cond) < 0) { - VIR_FREE(thiscall); - remoteError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot initialize mutex")); - goto cleanup; - } - - ret = remoteIO(st->conn, priv, 0, thiscall); - ignore_value(virCondDestroy(&thiscall->cond)); - VIR_FREE(thiscall); - if (ret < 0) - goto cleanup; - } - - VIR_DEBUG("After IO %d", privst->incomingOffset); - if (privst->incomingOffset) { - int want = privst->incomingOffset; - if (want > nbytes) - want = nbytes; - memcpy(data, privst->incoming, want); - if (want < privst->incomingOffset) { - memmove(privst->incoming, privst->incoming + want, privst->incomingOffset - want); - privst->incomingOffset -= want; - } else { - VIR_FREE(privst->incoming); - privst->incomingOffset = privst->incomingLength = 0; - } - rv = want; - } else { - rv = 0; - } - - remoteStreamEventTimerUpdate(privst); + rv = virNetClientStreamRecvPacket(privst, + priv->client, + data, + nbytes, + (st->flags & VIR_STREAM_NONBLOCK)); VIR_DEBUG("Done %d", rv); cleanup: - if (rv == -1) - remoteStreamRelease(st); remoteDriverUnlock(priv); return rv; } +struct remoteStreamCallbackData { + virStreamPtr st; + virStreamEventCallback cb; + void *opaque; + virFreeCallback ff; +}; -static void -remoteStreamEventTimer(int timer ATTRIBUTE_UNUSED, void *opaque) +static void remoteStreamEventCallback(virNetClientStreamPtr stream ATTRIBUTE_UNUSED, + int events, + void *opaque) { - virStreamPtr st = opaque; - struct private_data *priv = st->conn->privateData; - struct private_stream_data *privst = st->privateData; - int events = 0; - - remoteDriverLock(priv); - - if (privst->cb && - (privst->cbEvents & VIR_STREAM_EVENT_READABLE) && - privst->incomingOffset) - events |= VIR_STREAM_EVENT_READABLE; - if (privst->cb && - (privst->cbEvents & VIR_STREAM_EVENT_WRITABLE)) - events |= VIR_STREAM_EVENT_WRITABLE; - VIR_DEBUG("Got Timer dispatch %d %d offset=%d", events, privst->cbEvents, privst->incomingOffset); - if (events) { - virStreamEventCallback cb = privst->cb; - void *cbOpaque = privst->cbOpaque; - virFreeCallback cbFree = privst->cbFree; - - privst->cbDispatch = 1; - remoteDriverUnlock(priv); - (cb)(st, events, cbOpaque); - remoteDriverLock(priv); - privst->cbDispatch = 0; - - if (!privst->cb && cbFree) - (cbFree)(cbOpaque); - } + struct remoteStreamCallbackData *cbdata = opaque; + struct private_data *priv = cbdata->st->conn->privateData; remoteDriverUnlock(priv); + (cbdata->cb)(cbdata->st, events, cbdata->opaque); + remoteDriverLock(priv); } -static void -remoteStreamEventTimerFree(void *opaque) +static void remoteStreamCallbackFree(void *opaque) { - virStreamPtr st = opaque; - virUnrefStream(st); + struct remoteStreamCallbackData *cbdata = opaque; + + if (!cbdata->cb && cbdata->ff) + (cbdata->ff)(cbdata->opaque); + + virStreamFree(cbdata->st); + VIR_FREE(opaque); } @@ -4374,148 +3282,128 @@ remoteStreamEventAddCallback(virStreamPtr st, virFreeCallback ff) { struct private_data *priv = st->conn->privateData; - struct private_stream_data *privst = st->privateData; + virNetClientStreamPtr privst = st->privateData; int ret = -1; + struct remoteStreamCallbackData *cbdata; - remoteDriverLock(priv); - - if (privst->cb) { - remoteError(VIR_ERR_INTERNAL_ERROR, - "%s", _("multiple stream callbacks not supported")); - goto cleanup; + if (VIR_ALLOC(cbdata) < 0) { + virReportOOMError(); + return -1; } - + cbdata->cb = cb; + cbdata->opaque = opaque; + cbdata->ff = ff; + cbdata->st = st; virStreamRef(st); - if ((privst->cbTimer = - virEventAddTimeout(-1, - remoteStreamEventTimer, - st, - remoteStreamEventTimerFree)) < 0) { - virUnrefStream(st); - goto cleanup; - } - - privst->cb = cb; - privst->cbOpaque = opaque; - privst->cbFree = ff; - privst->cbEvents = events; - remoteStreamEventTimerUpdate(privst); + remoteDriverLock(priv); - ret = 0; + if ((ret = virNetClientStreamEventAddCallback(privst, + events, + remoteStreamEventCallback, + cbdata, + remoteStreamCallbackFree)) < 0) { + VIR_FREE(cbdata); + goto cleanup; + } cleanup: remoteDriverUnlock(priv); return ret; } + static int remoteStreamEventUpdateCallback(virStreamPtr st, int events) { struct private_data *priv = st->conn->privateData; - struct private_stream_data *privst = st->privateData; + virNetClientStreamPtr privst = st->privateData; int ret = -1; remoteDriverLock(priv); - if (!privst->cb) { - remoteError(VIR_ERR_INTERNAL_ERROR, - "%s", _("no stream callback registered")); - goto cleanup; - } + ret = virNetClientStreamEventUpdateCallback(privst, events); - privst->cbEvents = events; - - remoteStreamEventTimerUpdate(privst); - - ret = 0; - -cleanup: - remoteDriverUnlock(priv); - return ret; -} + remoteDriverUnlock(priv); + return ret; +} static int remoteStreamEventRemoveCallback(virStreamPtr st) { struct private_data *priv = st->conn->privateData; - struct private_stream_data *privst = st->privateData; + virNetClientStreamPtr privst = st->privateData; int ret = -1; remoteDriverLock(priv); - if (!privst->cb) { - remoteError(VIR_ERR_INTERNAL_ERROR, - "%s", _("no stream callback registered")); - goto cleanup; - } + ret = virNetClientStreamEventRemoveCallback(privst); - if (!privst->cbDispatch && - privst->cbFree) - (privst->cbFree)(privst->cbOpaque); - privst->cb = NULL; - privst->cbOpaque = NULL; - privst->cbFree = NULL; - privst->cbEvents = 0; - virEventRemoveTimeout(privst->cbTimer); - - ret = 0; - -cleanup: remoteDriverUnlock(priv); return ret; } + static int remoteStreamFinish(virStreamPtr st) { struct private_data *priv = st->conn->privateData; + virNetClientStreamPtr privst = st->privateData; int ret = -1; remoteDriverLock(priv); - if (remoteStreamHasError(st)) + if (virNetClientStreamRaiseError(privst)) goto cleanup; - ret = remoteStreamPacket(st, - REMOTE_OK, - NULL, - 0); + ret = virNetClientStreamSendPacket(privst, + priv->client, + VIR_NET_OK, + NULL, + 0); cleanup: - remoteStreamRelease(st); + virNetClientRemoveStream(priv->client, privst); + virNetClientStreamFree(privst); + st->privateData = NULL; + st->driver = NULL; remoteDriverUnlock(priv); return ret; } + static int remoteStreamAbort(virStreamPtr st) { struct private_data *priv = st->conn->privateData; + virNetClientStreamPtr privst = st->privateData; int ret = -1; remoteDriverLock(priv); - if (remoteStreamHasError(st)) + if (virNetClientStreamRaiseError(privst)) goto cleanup; - ret = remoteStreamPacket(st, - REMOTE_ERROR, - NULL, - 0); + ret = virNetClientStreamSendPacket(privst, + priv->client, + VIR_NET_ERROR, + NULL, + 0); cleanup: - remoteStreamRelease(st); + virNetClientRemoveStream(priv->client, privst); + virNetClientStreamFree(privst); + st->privateData = NULL; + st->driver = NULL; remoteDriverUnlock(priv); return ret; } - static virStreamDriver remoteStreamDrv = { .streamRecv = remoteStreamRecv, .streamSend = remoteStreamSend, @@ -4526,6 +3414,7 @@ static virStreamDriver remoteStreamDrv = { .streamRemoveCallback = remoteStreamEventRemoveCallback, }; + static int remoteDomainEventRegisterAny(virConnectPtr conn, virDomainPtr dom, int eventID, @@ -4620,6 +3509,7 @@ done: return rv; } + /*----------------------------------------------------------------------*/ static int @@ -4793,23 +3683,28 @@ remoteDomainMigratePrepareTunnel3(virConnectPtr dconn, const char *dom_xml) { struct private_data *priv = dconn->privateData; - struct private_stream_data *privst = NULL; int rv = -1; remote_domain_migrate_prepare_tunnel3_args args; remote_domain_migrate_prepare_tunnel3_ret ret; + virNetClientStreamPtr netst; remoteDriverLock(priv); memset(&args, 0, sizeof(args)); memset(&ret, 0, sizeof(ret)); - if (!(privst = remoteStreamOpen(st, - REMOTE_PROC_DOMAIN_MIGRATE_PREPARE_TUNNEL3, - priv->counter))) + if (!(netst = virNetClientStreamNew(priv->remoteProgram, + REMOTE_PROC_DOMAIN_MIGRATE_PREPARE_TUNNEL, + priv->counter))) goto done; + if (virNetClientAddStream(priv->client, netst) < 0) { + virNetClientStreamFree(netst); + goto done; + } + st->driver = &remoteStreamDrv; - st->privateData = privst; + st->privateData = netst; args.cookie_in.cookie_in_val = (char *)cookiein; args.cookie_in.cookie_in_len = cookieinlen; @@ -4821,7 +3716,8 @@ remoteDomainMigratePrepareTunnel3(virConnectPtr dconn, if (call(dconn, priv, 0, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE_TUNNEL3, (xdrproc_t) xdr_remote_domain_migrate_prepare_tunnel3_args, (char *) &args, (xdrproc_t) xdr_remote_domain_migrate_prepare_tunnel3_ret, (char *) &ret) == -1) { - remoteStreamRelease(st); + virNetClientRemoveStream(priv->client, netst); + virNetClientStreamFree(netst); goto done; } @@ -5006,1251 +3902,42 @@ done: #include "remote_client_bodies.h" #include "qemu_client_bodies.h" - -/*----------------------------------------------------------------------*/ - -static struct remote_thread_call * -prepareCall(struct private_data *priv, - int flags, - int proc_nr, - xdrproc_t args_filter, char *args, - xdrproc_t ret_filter, char *ret) -{ - XDR xdr; - struct remote_message_header hdr; - struct remote_thread_call *rv; - - if (VIR_ALLOC(rv) < 0) { - virReportOOMError(); - return NULL; - } - - if (virCondInit(&rv->cond) < 0) { - VIR_FREE(rv); - remoteError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot initialize mutex")); - return NULL; - } - - /* Get a unique serial number for this message. */ - rv->serial = priv->counter++; - rv->proc_nr = proc_nr; - rv->ret_filter = ret_filter; - rv->ret = ret; - rv->want_reply = 1; - - if (flags & REMOTE_CALL_QEMU) { - hdr.prog = QEMU_PROGRAM; - hdr.vers = QEMU_PROTOCOL_VERSION; - } - else { - hdr.prog = REMOTE_PROGRAM; - hdr.vers = REMOTE_PROTOCOL_VERSION; - } - hdr.proc = proc_nr; - hdr.type = REMOTE_CALL; - hdr.serial = rv->serial; - hdr.status = REMOTE_OK; - - /* Serialise header followed by args. */ - xdrmem_create (&xdr, rv->buffer+4, REMOTE_MESSAGE_MAX, XDR_ENCODE); - if (!xdr_remote_message_header (&xdr, &hdr)) { - remoteError(VIR_ERR_RPC, "%s", _("xdr_remote_message_header failed")); - goto error; - } - - if (!(*args_filter) (&xdr, args)) { - remoteError(VIR_ERR_RPC, - _("Unable to marshal arguments for program %d version %d procedure %d type %d status %d"), - hdr.prog, hdr.vers, hdr.proc, hdr.type, hdr.status); - goto error; - } - - /* Get the length stored in buffer. */ - rv->bufferLength = xdr_getpos (&xdr); - xdr_destroy (&xdr); - - /* Length must include the length word itself (always encoded in - * 4 bytes as per RFC 4506). - */ - rv->bufferLength += REMOTE_MESSAGE_HEADER_XDR_LEN; - - /* Encode the length word. */ - xdrmem_create (&xdr, rv->buffer, REMOTE_MESSAGE_HEADER_XDR_LEN, XDR_ENCODE); - if (!xdr_u_int (&xdr, &rv->bufferLength)) { - remoteError(VIR_ERR_RPC, "%s", _("xdr_u_int (length word)")); - goto error; - } - xdr_destroy (&xdr); - - return rv; - -error: - xdr_destroy (&xdr); - ignore_value(virCondDestroy(&rv->cond)); - VIR_FREE(rv); - return NULL; -} - - - -static int -remoteIOWriteBuffer(struct private_data *priv, - const char *bytes, int len) -{ - int ret; - - if (priv->uses_tls) { - tls_resend: - ret = gnutls_record_send (priv->session, bytes, len); - if (ret < 0) { - if (ret == GNUTLS_E_INTERRUPTED) - goto tls_resend; - if (ret == GNUTLS_E_AGAIN) - return 0; - - remoteError(VIR_ERR_GNUTLS_ERROR, "%s", gnutls_strerror (ret)); - return -1; - } - } else { - resend: - ret = send (priv->sock, bytes, len, 0); - if (ret == -1) { - if (errno == EINTR) - goto resend; - if (errno == EWOULDBLOCK) - return 0; - - virReportSystemError(errno, "%s", _("cannot send data")); - return -1; - - } - } - - return ret; -} - - -static int -remoteIOReadBuffer(struct private_data *priv, - char *bytes, int len) -{ - int ret; - - if (priv->uses_tls) { - tls_resend: - ret = gnutls_record_recv (priv->session, bytes, len); - if (ret == GNUTLS_E_INTERRUPTED) - goto tls_resend; - if (ret == GNUTLS_E_AGAIN) - return 0; - - /* Treat 0 == EOF as an error */ - if (ret <= 0) { - if (ret < 0) - remoteError(VIR_ERR_GNUTLS_ERROR, - _("failed to read from TLS socket %s"), - gnutls_strerror (ret)); - else - remoteError(VIR_ERR_SYSTEM_ERROR, "%s", - _("server closed connection")); - return -1; - } - } else { - resend: - ret = recv (priv->sock, bytes, len, 0); - if (ret <= 0) { - if (ret == -1) { - if (errno == EINTR) - goto resend; - if (errno == EWOULDBLOCK) - return 0; - - char errout[1024] = "\0"; - if (priv->errfd != -1) { - if (saferead(priv->errfd, errout, sizeof(errout)) < 0) { - virReportSystemError(errno, "%s", - _("cannot recv data")); - return -1; - } - } - - virReportSystemError(errno, - _("cannot recv data: %s"), errout); - - } else { - char errout[1024] = "\0"; - if (priv->errfd != -1) { - if (saferead(priv->errfd, errout, sizeof(errout)) < 0) { - remoteError(VIR_ERR_SYSTEM_ERROR, - _("server closed connection: %s"), - virStrerror(errno, errout, sizeof errout)); - return -1; - } - } - - remoteError(VIR_ERR_SYSTEM_ERROR, - _("server closed connection: %s"), errout); - } - return -1; - } - } - - return ret; -} - - -static int -remoteIOWriteMessage(struct private_data *priv, - struct remote_thread_call *thecall) -{ -#if HAVE_SASL - if (priv->saslconn) { - const char *output; - unsigned int outputlen; - int err, ret; - - if (!priv->saslEncoded) { - err = sasl_encode(priv->saslconn, - thecall->buffer + thecall->bufferOffset, - thecall->bufferLength - thecall->bufferOffset, - &output, &outputlen); - if (err != SASL_OK) { - remoteError(VIR_ERR_INTERNAL_ERROR, - _("failed to encode SASL data: %s"), - sasl_errstring(err, NULL, NULL)); - return -1; - } - priv->saslEncoded = output; - priv->saslEncodedLength = outputlen; - priv->saslEncodedOffset = 0; - - thecall->bufferOffset = thecall->bufferLength; - } - - ret = remoteIOWriteBuffer(priv, - priv->saslEncoded + priv->saslEncodedOffset, - priv->saslEncodedLength - priv->saslEncodedOffset); - if (ret < 0) - return ret; - priv->saslEncodedOffset += ret; - - if (priv->saslEncodedOffset == priv->saslEncodedLength) { - priv->saslEncoded = NULL; - priv->saslEncodedOffset = priv->saslEncodedLength = 0; - if (thecall->want_reply) - thecall->mode = REMOTE_MODE_WAIT_RX; - else - thecall->mode = REMOTE_MODE_COMPLETE; - } - } else { -#endif - int ret; - ret = remoteIOWriteBuffer(priv, - thecall->buffer + thecall->bufferOffset, - thecall->bufferLength - thecall->bufferOffset); - if (ret < 0) - return ret; - thecall->bufferOffset += ret; - - if (thecall->bufferOffset == thecall->bufferLength) { - thecall->bufferOffset = thecall->bufferLength = 0; - if (thecall->want_reply) - thecall->mode = REMOTE_MODE_WAIT_RX; - else - thecall->mode = REMOTE_MODE_COMPLETE; - } -#if HAVE_SASL - } -#endif - return 0; -} - - -static int -remoteIOHandleOutput(struct private_data *priv) { - struct remote_thread_call *thecall = priv->waitDispatch; - - while (thecall && - thecall->mode != REMOTE_MODE_WAIT_TX) - thecall = thecall->next; - - if (!thecall) - return -1; /* Shouldn't happen, but you never know... */ - - while (thecall) { - int ret = remoteIOWriteMessage(priv, thecall); - if (ret < 0) - return ret; - - if (thecall->mode == REMOTE_MODE_WAIT_TX) - return 0; /* Blocking write, to back to event loop */ - - thecall = thecall->next; - } - - return 0; /* No more calls to send, all done */ -} - -static int -remoteIOReadMessage(struct private_data *priv) { - unsigned int wantData; - - /* Start by reading length word */ - if (priv->bufferLength == 0) - priv->bufferLength = 4; - - wantData = priv->bufferLength - priv->bufferOffset; - -#if HAVE_SASL - if (priv->saslconn) { - if (priv->saslDecoded == NULL) { - int ret, err; - ret = remoteIOReadBuffer(priv, priv->saslTemporary, - sizeof(priv->saslTemporary)); - if (ret < 0) - return -1; - if (ret == 0) - return 0; - - err = sasl_decode(priv->saslconn, priv->saslTemporary, ret, - &priv->saslDecoded, &priv->saslDecodedLength); - if (err != SASL_OK) { - remoteError(VIR_ERR_INTERNAL_ERROR, - _("failed to decode SASL data: %s"), - sasl_errstring(err, NULL, NULL)); - return -1; - } - priv->saslDecodedOffset = 0; - } - - if ((priv->saslDecodedLength - priv->saslDecodedOffset) < wantData) - wantData = (priv->saslDecodedLength - priv->saslDecodedOffset); - - memcpy(priv->buffer + priv->bufferOffset, - priv->saslDecoded + priv->saslDecodedOffset, - wantData); - priv->saslDecodedOffset += wantData; - priv->bufferOffset += wantData; - if (priv->saslDecodedOffset == priv->saslDecodedLength) { - priv->saslDecodedOffset = priv->saslDecodedLength = 0; - priv->saslDecoded = NULL; - } - - return wantData; - } else { -#endif - int ret; - - ret = remoteIOReadBuffer(priv, - priv->buffer + priv->bufferOffset, - wantData); - if (ret < 0) - return -1; - if (ret == 0) - return 0; - - priv->bufferOffset += ret; - - return ret; -#if HAVE_SASL - } -#endif -} - - -static int -remoteIODecodeMessageLength(struct private_data *priv) { - XDR xdr; - unsigned int len; - - xdrmem_create (&xdr, priv->buffer, priv->bufferLength, XDR_DECODE); - if (!xdr_u_int (&xdr, &len)) { - remoteError(VIR_ERR_RPC, "%s", _("xdr_u_int (length word, reply)")); - return -1; - } - xdr_destroy (&xdr); - - if (len < REMOTE_MESSAGE_HEADER_XDR_LEN) { - remoteError(VIR_ERR_RPC, "%s", - _("packet received from server too small")); - return -1; - } - - /* Length includes length word - adjust to real length to read. */ - len -= REMOTE_MESSAGE_HEADER_XDR_LEN; - - if (len > REMOTE_MESSAGE_MAX) { - remoteError(VIR_ERR_RPC, "%s", - _("packet received from server too large")); - return -1; - } - - /* Extend our declared buffer length and carry - on reading the header + payload */ - priv->bufferLength += len; - VIR_DEBUG("Got length, now need %d total (%d more)", priv->bufferLength, len); - return 0; -} - - -static int -processCallDispatchReply(virConnectPtr conn, struct private_data *priv, - remote_message_header *hdr, - XDR *xdr); - -static int -processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, - int in_open, - remote_message_header *hdr, - XDR *xdr); - -static int -processCallDispatchStream(virConnectPtr conn, struct private_data *priv, - remote_message_header *hdr, - XDR *xdr); - - -static int -processCallDispatch(virConnectPtr conn, struct private_data *priv, - int flags) { - XDR xdr; - struct remote_message_header hdr; - int len = priv->bufferLength - 4; - int rv = -1; - int expectedprog; - int expectedvers; - - /* Length word has already been read */ - priv->bufferOffset = 4; - - /* Deserialise reply header. */ - xdrmem_create (&xdr, priv->buffer + priv->bufferOffset, len, XDR_DECODE); - if (!xdr_remote_message_header (&xdr, &hdr)) { - remoteError(VIR_ERR_RPC, "%s", _("invalid header in reply")); - return -1; - } - - priv->bufferOffset += xdr_getpos(&xdr); - - expectedprog = REMOTE_PROGRAM; - expectedvers = REMOTE_PROTOCOL_VERSION; - if (flags & REMOTE_CALL_QEMU) { - expectedprog = QEMU_PROGRAM; - expectedvers = QEMU_PROTOCOL_VERSION; - } - - /* Check program, version, etc. are what we expect. */ - if (hdr.prog != expectedprog) { - remoteError(VIR_ERR_RPC, - _("unknown program (received %x, expected %x)"), - hdr.prog, expectedprog); - return -1; - } - if (hdr.vers != expectedvers) { - remoteError(VIR_ERR_RPC, - _("unknown protocol version (received %x, expected %x)"), - hdr.vers, expectedvers); - return -1; - } - - - switch (hdr.type) { - case REMOTE_REPLY: /* Normal RPC replies */ - rv = processCallDispatchReply(conn, priv, &hdr, &xdr); - break; - - case REMOTE_MESSAGE: /* Async notifications */ - VIR_DEBUG("Dispatch event %d %d", hdr.proc, priv->bufferLength); - rv = processCallDispatchMessage(conn, priv, flags & REMOTE_CALL_IN_OPEN, - &hdr, &xdr); - break; - - case REMOTE_STREAM: /* Stream protocol */ - rv = processCallDispatchStream(conn, priv, &hdr, &xdr); - break; - - default: - remoteError(VIR_ERR_RPC, - _("got unexpected RPC call %d from server"), - hdr.proc); - rv = -1; - break; - } - - xdr_destroy(&xdr); - return rv; -} - - -static int -processCallDispatchReply(virConnectPtr conn ATTRIBUTE_UNUSED, - struct private_data *priv, - remote_message_header *hdr, - XDR *xdr) { - struct remote_thread_call *thecall; - - /* Ok, definitely got an RPC reply now find - out who's been waiting for it */ - thecall = priv->waitDispatch; - while (thecall && - thecall->serial != hdr->serial) - thecall = thecall->next; - - if (!thecall) { - remoteError(VIR_ERR_RPC, - _("no call waiting for reply with serial %d"), - hdr->serial); - return -1; - } - - if (hdr->proc != thecall->proc_nr) { - remoteError(VIR_ERR_RPC, - _("unknown procedure (received %x, expected %x)"), - hdr->proc, thecall->proc_nr); - return -1; - } - - /* Status is either REMOTE_OK (meaning that what follows is a ret - * structure), or REMOTE_ERROR (and what follows is a remote_error - * structure). - */ - switch (hdr->status) { - case REMOTE_OK: - if (!(*thecall->ret_filter) (xdr, thecall->ret)) { - remoteError(VIR_ERR_RPC, - _("Unable to marshal reply for program %d version %d procedure %d type %d status %d"), - hdr->prog, hdr->vers, hdr->proc, hdr->type, hdr->status); - return -1; - } - thecall->mode = REMOTE_MODE_COMPLETE; - return 0; - - case REMOTE_ERROR: - memset (&thecall->err, 0, sizeof thecall->err); - if (!xdr_remote_error (xdr, &thecall->err)) { - remoteError(VIR_ERR_RPC, - _("Unable to marshal error for program %d version %d procedure %d type %d status %d"), - hdr->prog, hdr->vers, hdr->proc, hdr->type, hdr->status); - return -1; - } - thecall->mode = REMOTE_MODE_ERROR; - return 0; - - default: - remoteError(VIR_ERR_RPC, _("unknown status (received %x)"), hdr->status); - return -1; - } -} - -static int -processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, - int in_open, - remote_message_header *hdr, - XDR *xdr) { - virDomainEventPtr event = NULL; - /* An async message has come in while we were waiting for the - * response. Process it to pull it off the wire, and try again - */ - - if (in_open) { - VIR_DEBUG("Ignoring bogus event %d received while in open", hdr->proc); - return -1; - } - - switch (hdr->proc) { - case REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE: - event = remoteDomainReadEventLifecycle(conn, xdr); - break; - - case REMOTE_PROC_DOMAIN_EVENT_REBOOT: - event = remoteDomainReadEventReboot(conn, xdr); - break; - - case REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE: - event = remoteDomainReadEventRTCChange(conn, xdr); - break; - - case REMOTE_PROC_DOMAIN_EVENT_WATCHDOG: - event = remoteDomainReadEventWatchdog(conn, xdr); - break; - - case REMOTE_PROC_DOMAIN_EVENT_IO_ERROR: - event = remoteDomainReadEventIOError(conn, xdr); - break; - - case REMOTE_PROC_DOMAIN_EVENT_IO_ERROR_REASON: - event = remoteDomainReadEventIOErrorReason(conn, xdr); - break; - - case REMOTE_PROC_DOMAIN_EVENT_GRAPHICS: - event = remoteDomainReadEventGraphics(conn, xdr); - break; - - case REMOTE_PROC_DOMAIN_EVENT_CONTROL_ERROR: - event = remoteDomainReadEventControlError(conn, xdr); - break; - - default: - VIR_DEBUG("Unexpected event proc %d", hdr->proc); - break; - } - VIR_DEBUG("Event ready for queue %p %p", event, conn); - - if (!event) - return -1; - - remoteDomainEventQueue(priv, event); - return 0; -} - -static int -processCallDispatchStream(virConnectPtr conn ATTRIBUTE_UNUSED, - struct private_data *priv, - remote_message_header *hdr, - XDR *xdr) { - struct private_stream_data *privst; - struct remote_thread_call *thecall; - - /* Try and find a matching stream */ - privst = priv->streams; - while (privst && - privst->serial != hdr->serial && - privst->proc_nr != hdr->proc) - privst = privst->next; - - if (!privst) { - VIR_DEBUG("No registered stream matching serial=%d, proc=%d", - hdr->serial, hdr->proc); - return -1; - } - - /* See if there's also a (optional) call waiting for this reply */ - thecall = priv->waitDispatch; - while (thecall && - thecall->serial != hdr->serial) - thecall = thecall->next; - - - /* Status is either REMOTE_OK (meaning that what follows is a ret - * structure), or REMOTE_ERROR (and what follows is a remote_error - * structure). - */ - switch (hdr->status) { - case REMOTE_CONTINUE: { - int avail = privst->incomingLength - privst->incomingOffset; - int need = priv->bufferLength - priv->bufferOffset; - VIR_DEBUG("Got a stream data packet"); - - /* XXX flag stream as complete somwhere if need==0 */ - - if (need > avail) { - int extra = need - avail; - if (VIR_REALLOC_N(privst->incoming, - privst->incomingLength + extra) < 0) { - VIR_DEBUG("Out of memory handling stream data"); - return -1; - } - privst->incomingLength += extra; - } - - memcpy(privst->incoming + privst->incomingOffset, - priv->buffer + priv->bufferOffset, - priv->bufferLength - priv->bufferOffset); - privst->incomingOffset += (priv->bufferLength - priv->bufferOffset); - - if (thecall && thecall->want_reply) { - VIR_DEBUG("Got sync data packet offset=%d", privst->incomingOffset); - thecall->mode = REMOTE_MODE_COMPLETE; - } else { - VIR_DEBUG("Got aysnc data packet offset=%d", privst->incomingOffset); - remoteStreamEventTimerUpdate(privst); - } - return 0; - } - - case REMOTE_OK: - VIR_DEBUG("Got a synchronous confirm"); - if (!thecall) { - VIR_DEBUG("Got unexpected stream finish confirmation"); - return -1; - } - thecall->mode = REMOTE_MODE_COMPLETE; - return 0; - - case REMOTE_ERROR: - if (thecall && thecall->want_reply) { - VIR_DEBUG("Got a synchronous error"); - /* Give the error straight to this call */ - memset (&thecall->err, 0, sizeof thecall->err); - if (!xdr_remote_error (xdr, &thecall->err)) { - remoteError(VIR_ERR_RPC, "%s", _("unmarshaling remote_error")); - return -1; - } - thecall->mode = REMOTE_MODE_ERROR; - } else { - VIR_DEBUG("Got a asynchronous error"); - /* No call, so queue the error against the stream */ - if (privst->has_error) { - VIR_DEBUG("Got unexpected duplicate stream error"); - return -1; - } - privst->has_error = 1; - memset (&privst->err, 0, sizeof privst->err); - if (!xdr_remote_error (xdr, &privst->err)) { - VIR_DEBUG("Failed to unmarshal error"); - return -1; - } - } - return 0; - - default: - VIR_WARN("Stream with unexpected serial=%d, proc=%d, status=%d", - hdr->serial, hdr->proc, hdr->status); - return -1; - } -} - -static int -remoteIOHandleInput(virConnectPtr conn, struct private_data *priv, - int flags) -{ - /* Read as much data as is available, until we get - * EAGAIN - */ - for (;;) { - int ret = remoteIOReadMessage(priv); - - if (ret < 0) - return -1; - if (ret == 0) - return 0; /* Blocking on read */ - - /* Check for completion of our goal */ - if (priv->bufferOffset == priv->bufferLength) { - if (priv->bufferOffset == 4) { - ret = remoteIODecodeMessageLength(priv); - if (ret < 0) - return -1; - - /* - * We'll carry on around the loop to immediately - * process the message body, because it has probably - * already arrived. Worst case, we'll get EAGAIN on - * next iteration. - */ - } else { - ret = processCallDispatch(conn, priv, flags); - priv->bufferOffset = priv->bufferLength = 0; - /* - * We've completed one call, but we don't want to - * spin around the loop forever if there are many - * incoming async events, or replies for other - * thread's RPC calls. We want to get out & let - * any other thread take over as soon as we've - * got our reply. When SASL is active though, we - * may have read more data off the wire than we - * initially wanted & cached it in memory. In this - * case, poll() would not detect that there is more - * ready todo. - * - * So if SASL is active *and* some SASL data is - * already cached, then we'll process that now, - * before returning. - */ -#if HAVE_SASL - if (ret == 0 && - priv->saslconn && - priv->saslDecoded) - continue; -#endif - return ret; - } - } - } -} - -/* - * Process all calls pending dispatch/receive until we - * get a reply to our own call. Then quit and pass the buck - * to someone else. - */ -static int -remoteIOEventLoop(virConnectPtr conn, - struct private_data *priv, - int flags, - struct remote_thread_call *thiscall) -{ - struct pollfd fds[2]; - int ret; - - fds[0].fd = priv->sock; - fds[1].fd = priv->wakeupReadFD; - - for (;;) { - struct remote_thread_call *tmp = priv->waitDispatch; - struct remote_thread_call *prev; - char ignore; -#ifdef HAVE_PTHREAD_SIGMASK - sigset_t oldmask, blockedsigs; -#endif - int timeout = -1; - - /* If we have existing SASL decoded data we - * don't want to sleep in the poll(), just - * check if any other FDs are also ready - */ -#if HAVE_SASL - if (priv->saslDecoded) - timeout = 0; -#endif - - fds[0].events = fds[0].revents = 0; - fds[1].events = fds[1].revents = 0; - - fds[1].events = POLLIN; - while (tmp) { - if (tmp->mode == REMOTE_MODE_WAIT_RX) - fds[0].events |= POLLIN; - if (tmp->mode == REMOTE_MODE_WAIT_TX) - fds[0].events |= POLLOUT; - - tmp = tmp->next; - } - - if (priv->streams) - fds[0].events |= POLLIN; - - /* Release lock while poll'ing so other threads - * can stuff themselves on the queue */ - remoteDriverUnlock(priv); - - /* Block SIGWINCH from interrupting poll in curses programs, - * then restore the original signal mask again immediately - * after the call (RHBZ#567931). Same for SIGCHLD and SIGPIPE - * at the suggestion of Paolo Bonzini and Daniel Berrange. - */ -#ifdef HAVE_PTHREAD_SIGMASK - sigemptyset (&blockedsigs); - sigaddset (&blockedsigs, SIGWINCH); - sigaddset (&blockedsigs, SIGCHLD); - sigaddset (&blockedsigs, SIGPIPE); - ignore_value(pthread_sigmask(SIG_BLOCK, &blockedsigs, &oldmask)); -#endif - - repoll: - ret = poll(fds, ARRAY_CARDINALITY(fds), timeout); - if (ret < 0 && errno == EAGAIN) - goto repoll; - -#ifdef HAVE_PTHREAD_SIGMASK - ignore_value(pthread_sigmask(SIG_SETMASK, &oldmask, NULL)); -#endif - - remoteDriverLock(priv); - - /* If we have existing SASL decoded data, pretend - * the socket became readable so we consume it - */ -#if HAVE_SASL - if (priv->saslDecoded) - fds[0].revents |= POLLIN; -#endif - - if (fds[1].revents) { - ssize_t s; - VIR_DEBUG("Woken up from poll by other thread"); - s = saferead(priv->wakeupReadFD, &ignore, sizeof(ignore)); - if (s < 0) { - virReportSystemError(errno, "%s", - _("read on wakeup fd failed")); - goto error; - } else if (s != sizeof(ignore)) { - remoteError(VIR_ERR_INTERNAL_ERROR, "%s", - _("read on wakeup fd failed")); - goto error; - } - } - - if (ret < 0) { - if (errno == EWOULDBLOCK) - continue; - virReportSystemError(errno, - "%s", _("poll on socket failed")); - goto error; - } - - if (fds[0].revents & POLLOUT) { - if (remoteIOHandleOutput(priv) < 0) - goto error; - } - - if (fds[0].revents & POLLIN) { - if (remoteIOHandleInput(conn, priv, flags) < 0) - goto error; - } - - /* Iterate through waiting threads and if - * any are complete then tell 'em to wakeup - */ - tmp = priv->waitDispatch; - prev = NULL; - while (tmp) { - if (tmp != thiscall && - (tmp->mode == REMOTE_MODE_COMPLETE || - tmp->mode == REMOTE_MODE_ERROR)) { - /* Take them out of the list */ - if (prev) - prev->next = tmp->next; - else - priv->waitDispatch = tmp->next; - - /* And wake them up.... - * ...they won't actually wakeup until - * we release our mutex a short while - * later... - */ - VIR_DEBUG("Waking up sleep %d %p %p", tmp->proc_nr, tmp, priv->waitDispatch); - virCondSignal(&tmp->cond); - } else { - prev = tmp; - } - tmp = tmp->next; - } - - /* Now see if *we* are done */ - if (thiscall->mode == REMOTE_MODE_COMPLETE || - thiscall->mode == REMOTE_MODE_ERROR) { - /* We're at head of the list already, so - * remove us - */ - priv->waitDispatch = thiscall->next; - VIR_DEBUG("Giving up the buck %d %p %p", thiscall->proc_nr, thiscall, priv->waitDispatch); - /* See if someone else is still waiting - * and if so, then pass the buck ! */ - if (priv->waitDispatch) { - VIR_DEBUG("Passing the buck to %d %p", priv->waitDispatch->proc_nr, priv->waitDispatch); - virCondSignal(&priv->waitDispatch->cond); - } - return 0; - } - - - if (fds[0].revents & (POLLHUP | POLLERR)) { - remoteError(VIR_ERR_INTERNAL_ERROR, "%s", - _("received hangup / error event on socket")); - goto error; - } - } - - -error: - priv->waitDispatch = thiscall->next; - VIR_DEBUG("Giving up the buck due to I/O error %d %p %p", thiscall->proc_nr, thiscall, priv->waitDispatch); - /* See if someone else is still waiting - * and if so, then pass the buck ! */ - if (priv->waitDispatch) { - VIR_DEBUG("Passing the buck to %d %p", priv->waitDispatch->proc_nr, priv->waitDispatch); - virCondSignal(&priv->waitDispatch->cond); - } - return -1; -} - -/* - * This function sends a message to remote server and awaits a reply - * - * NB. This does not free the args structure (not desirable, since you - * often want this allocated on the stack or else it contains strings - * which come from the user). It does however free any intermediate - * results, eg. the error structure if there is one. - * - * NB(2). Make sure to memset (&ret, 0, sizeof ret) before calling, - * else Bad Things will happen in the XDR code. - * - * NB(3) You must have the private_data lock before calling this - * - * NB(4) This is very complicated. Due to connection cloning, multiple - * threads can want to use the socket at once. Obviously only one of - * them can. So if someone's using the socket, other threads are put - * to sleep on condition variables. The existing thread may completely - * send & receive their RPC call/reply while they're asleep. Or it - * may only get around to dealing with sending the call. Or it may - * get around to neither. So upon waking up from slumber, the other - * thread may or may not have more work todo. - * - * We call this dance 'passing the buck' - * - * http://en.wikipedia.org/wiki/Passing_the_buck - * - * "Buck passing or passing the buck is the action of transferring - * responsibility or blame unto another person. It is also used as - * a strategy in power politics when the actions of one country/ - * nation are blamed on another, providing an opportunity for war." - * - * NB(5) Don't Panic! - */ -static int -remoteIO(virConnectPtr conn, - struct private_data *priv, - int flags, - struct remote_thread_call *thiscall) -{ - int rv; - - VIR_DEBUG("Do proc=%d serial=%d length=%d wait=%p", - thiscall->proc_nr, thiscall->serial, - thiscall->bufferLength, priv->waitDispatch); - - /* Check to see if another thread is dispatching */ - if (priv->waitDispatch) { - /* Stick ourselves on the end of the wait queue */ - struct remote_thread_call *tmp = priv->waitDispatch; - char ignore = 1; - ssize_t s; - while (tmp && tmp->next) - tmp = tmp->next; - if (tmp) - tmp->next = thiscall; - else - priv->waitDispatch = thiscall; - - /* Force other thread to wakeup from poll */ - s = safewrite(priv->wakeupSendFD, &ignore, sizeof(ignore)); - if (s < 0) { - char errout[1024]; - remoteError(VIR_ERR_INTERNAL_ERROR, - _("failed to wake up polling thread: %s"), - virStrerror(errno, errout, sizeof errout)); - return -1; - } else if (s != sizeof(ignore)) { - remoteError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to wake up polling thread")); - return -1; - } - - VIR_DEBUG("Going to sleep %d %p %p", thiscall->proc_nr, priv->waitDispatch, thiscall); - /* Go to sleep while other thread is working... */ - if (virCondWait(&thiscall->cond, &priv->lock) < 0) { - if (priv->waitDispatch == thiscall) { - priv->waitDispatch = thiscall->next; - } else { - tmp = priv->waitDispatch; - while (tmp && tmp->next && - tmp->next != thiscall) { - tmp = tmp->next; - } - if (tmp && tmp->next == thiscall) - tmp->next = thiscall->next; - } - remoteError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to wait on condition")); - return -1; - } - - VIR_DEBUG("Wokeup from sleep %d %p %p", thiscall->proc_nr, priv->waitDispatch, thiscall); - /* Two reasons we can be woken up - * 1. Other thread has got our reply ready for us - * 2. Other thread is all done, and it is our turn to - * be the dispatcher to finish waiting for - * our reply - */ - if (thiscall->mode == REMOTE_MODE_COMPLETE || - thiscall->mode == REMOTE_MODE_ERROR) { - /* - * We avoided catching the buck and our reply is ready ! - * We've already had 'thiscall' removed from the list - * so just need to (maybe) handle errors & free it - */ - goto cleanup; - } - - /* Grr, someone passed the buck onto us ... */ - - } else { - /* We're first to catch the buck */ - priv->waitDispatch = thiscall; - } - - VIR_DEBUG("We have the buck %d %p %p", thiscall->proc_nr, priv->waitDispatch, thiscall); - /* - * The buck stops here! - * - * At this point we're about to own the dispatch - * process... - */ - - /* - * Avoid needless wake-ups of the event loop in the - * case where this call is being made from a different - * thread than the event loop. These wake-ups would - * cause the event loop thread to be blocked on the - * mutex for the duration of the call - */ - if (priv->watch >= 0) - virEventUpdateHandle(priv->watch, 0); - - rv = remoteIOEventLoop(conn, priv, flags, thiscall); - - if (priv->watch >= 0) - virEventUpdateHandle(priv->watch, VIR_EVENT_HANDLE_READABLE); - - if (rv < 0) - return -1; - -cleanup: - VIR_DEBUG("All done with our call %d %p %p", thiscall->proc_nr, - priv->waitDispatch, thiscall); - if (thiscall->mode == REMOTE_MODE_ERROR) { - /* Interop for virErrorNumber glitch in 0.8.0, if server is - * 0.7.1 through 0.7.7; see comments in virterror.h. */ - switch (thiscall->err.code) { - case VIR_WAR_NO_NWFILTER: - /* no way to tell old VIR_WAR_NO_SECRET apart from - * VIR_WAR_NO_NWFILTER, but both are very similar - * warnings, so ignore the difference */ - break; - case VIR_ERR_INVALID_NWFILTER: - case VIR_ERR_NO_NWFILTER: - case VIR_ERR_BUILD_FIREWALL: - /* server was trying to pass VIR_ERR_INVALID_SECRET, - * VIR_ERR_NO_SECRET, or VIR_ERR_CONFIG_UNSUPPORTED */ - if (thiscall->err.domain != VIR_FROM_NWFILTER) - thiscall->err.code += 4; - break; - case VIR_WAR_NO_SECRET: - if (thiscall->err.domain == VIR_FROM_QEMU) - thiscall->err.code = VIR_ERR_OPERATION_TIMEOUT; - break; - case VIR_ERR_INVALID_SECRET: - if (thiscall->err.domain == VIR_FROM_XEN) - thiscall->err.code = VIR_ERR_MIGRATE_PERSIST_FAILED; - break; - default: - /* Nothing to alter. */ - break; - } - - /* See if caller asked us to keep quiet about missing RPCs - * eg for interop with older servers */ - if (flags & REMOTE_CALL_QUIET_MISSING_RPC && - thiscall->err.domain == VIR_FROM_REMOTE && - thiscall->err.code == VIR_ERR_RPC && - thiscall->err.level == VIR_ERR_ERROR && - thiscall->err.message && - STRPREFIX(*thiscall->err.message, "unknown procedure")) { - rv = -2; - } else if (thiscall->err.domain == VIR_FROM_REMOTE && - thiscall->err.code == VIR_ERR_RPC && - thiscall->err.level == VIR_ERR_ERROR && - thiscall->err.message && - STRPREFIX(*thiscall->err.message, "unknown procedure")) { - /* - * convert missing remote entry points into the unsupported - * feature error - */ - virRaiseErrorFull(__FILE__, __FUNCTION__, __LINE__, - thiscall->err.domain, - VIR_ERR_NO_SUPPORT, - thiscall->err.level, - thiscall->err.str1 ? *thiscall->err.str1 : NULL, - thiscall->err.str2 ? *thiscall->err.str2 : NULL, - thiscall->err.str3 ? *thiscall->err.str3 : NULL, - thiscall->err.int1, - thiscall->err.int2, - "%s", *thiscall->err.message); - rv = -1; - } else { - virRaiseErrorFull(__FILE__, __FUNCTION__, __LINE__, - thiscall->err.domain, - thiscall->err.code, - thiscall->err.level, - thiscall->err.str1 ? *thiscall->err.str1 : NULL, - thiscall->err.str2 ? *thiscall->err.str2 : NULL, - thiscall->err.str3 ? *thiscall->err.str3 : NULL, - thiscall->err.int1, - thiscall->err.int2, - "%s", thiscall->err.message ? *thiscall->err.message : "unknown"); - rv = -1; - } - xdr_free((xdrproc_t)xdr_remote_error, (char *)&thiscall->err); - } else { - rv = 0; - } - return rv; -} - - /* * Serial a set of arguments into a method call message, * send that to the server and wait for reply */ static int -call (virConnectPtr conn, struct private_data *priv, +call (virConnectPtr conn ATTRIBUTE_UNUSED, + struct private_data *priv, int flags, int proc_nr, xdrproc_t args_filter, char *args, xdrproc_t ret_filter, char *ret) { - struct remote_thread_call *thiscall; int rv; + virNetClientProgramPtr prog = flags & REMOTE_CALL_QEMU ? priv->qemuProgram : priv->remoteProgram; + int counter = priv->counter++; + virNetClientPtr client = priv->client; + priv->localUses++; + + /* Unlock, so that if we get any async events/stream data + * while processing the RPC, we don't deadlock when our + * callbacks for those are invoked + */ + remoteDriverUnlock(priv); + rv = virNetClientProgramCall(prog, + client, + counter, + proc_nr, + args_filter, args, + ret_filter, ret); + remoteDriverLock(priv); + priv->localUses--; - thiscall = prepareCall(priv, flags, 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; } -/** remoteDomainEventFired: - * - * The callback for monitoring the remote socket - * for event data - */ -void -remoteDomainEventFired(int watch, - int fd, - int event, - void *opaque) -{ - virConnectPtr conn = opaque; - struct private_data *priv = conn->privateData; - - remoteDriverLock(priv); - - /* This should be impossible, but it doesn't hurt to check */ - if (priv->waitDispatch) - goto done; - - VIR_DEBUG("Event fired %d %d %d %X", watch, fd, event, event); - - if (event & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) { - VIR_DEBUG("%s : VIR_EVENT_HANDLE_HANGUP or " - "VIR_EVENT_HANDLE_ERROR encountered", __FUNCTION__); - virEventRemoveHandle(watch); - priv->watch = -1; - goto done; - } - - if (fd != priv->sock) { - virEventRemoveHandle(watch); - priv->watch = -1; - goto done; - } - - if (remoteIOHandleInput(conn, priv, 0) < 0) - VIR_DEBUG("Something went wrong during async message processing"); - -done: - remoteDriverUnlock(priv); -} - static void remoteDomainEventDispatchFunc(virConnectPtr conn, virDomainEventPtr event, virConnectDomainEventGenericCallback cb, @@ -6266,7 +3953,7 @@ static void remoteDomainEventDispatchFunc(virConnectPtr conn, remoteDriverLock(priv); } -void +static void remoteDomainEventQueueFlush(int timer ATTRIBUTE_UNUSED, void *opaque) { virConnectPtr conn = opaque; @@ -6282,7 +3969,7 @@ remoteDomainEventQueueFlush(int timer ATTRIBUTE_UNUSED, void *opaque) remoteDriverUnlock(priv); } -void +static void remoteDomainEventQueue(struct private_data *priv, virDomainEventPtr event) { virDomainEventStateQueue(priv->domainEventState, event); diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index 71085d9283..d6264b9d90 100755 --- a/src/rpc/gendispatch.pl +++ b/src/rpc/gendispatch.pl @@ -1326,7 +1326,7 @@ elsif ($opt_k) { } if ($call->{streamflag} ne "none") { - print " struct private_stream_data *privst = NULL;\n"; + print " virNetClientStreamPtr netst = NULL;\n"; } print "\n"; @@ -1334,11 +1334,16 @@ elsif ($opt_k) { if ($call->{streamflag} ne "none") { print "\n"; - print " if (!(privst = remoteStreamOpen(st, REMOTE_PROC_$call->{UC_NAME}, priv->counter)))\n"; + print " if (!(netst = virNetClientStreamNew(priv->remoteProgram, REMOTE_PROC_$call->{UC_NAME}, priv->counter)))\n"; print " goto done;\n"; print "\n"; + print " if (virNetClientAddStream(priv->client, netst) < 0) {"; + print " virNetClientStreamFree(netst);\n"; + print " goto done;\n"; + print " }"; + print "\n"; print " st->driver = &remoteStreamDrv;\n"; - print " st->privateData = privst;\n"; + print " st->privateData = netst;\n"; } if ($call->{ProcName} eq "SupportsFeature") { @@ -1403,7 +1408,8 @@ elsif ($opt_k) { print " (xdrproc_t)xdr_$call->{ret}, (char *)$call_ret) == -1) {\n"; if ($call->{streamflag} ne "none") { - print " remoteStreamRelease(st);\n"; + print " virNetClientRemoveStream(priv->client, netst);\n"; + print " virNetClientStreamFree(netst);\n"; } print " goto done;\n"; -- GitLab