diff --git a/src/Makefile.am b/src/Makefile.am index 4dbf7b1de51df4e64c31c31dfadd3c34645bcd31..83d267fc90f0df86080a6361ed7c8ae68c7eb440 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 f0d42a3bcc8bd78ea3a85f458a8a1d5b36861087..2ac87c8de054e07fd8ebca0fa6903aeccf90097d 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 71085d9283db6642c76e4b0a3f23c8bb2a735501..d6264b9d90b2a563c020065f77db48d98a79f3ab 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";