This guts the current remote driver, removing all its networking handling code. Instead it calls out to the new virClientPtr and virClientProgramPtr APIs for all RPC & networking work. --- src/Makefile.am | 3 +- src/remote/remote_driver.c | 2530 ++++++++------------------------------------ 2 files changed, 419 insertions(+), 2114 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 4c6efa8..8986f22 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -475,9 +475,10 @@ libvirt_driver_remote_la_CFLAGS = \ $(GNUTLS_CFLAGS) \ $(SASL_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) $(SASL_LIBS) libvirt-net-client.la libvirt-net-rpc.la if WITH_DRIVER_MODULES libvirt_driver_remote_la_LDFLAGS += -module -avoid-version endif diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index e6eb9b5..8fd7949 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -23,51 +23,13 @@ #include <config.h> -#include <stdio.h> -#include <stdlib.h> #include <unistd.h> -#include <string.h> #include <assert.h> -#include <signal.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <arpa/inet.h> -#include <sys/wait.h> - -/* Windows socket compatibility functions. */ -#include <errno.h> -#include <sys/socket.h> - -#ifndef HAVE_WINSOCK2_H /* Unix & Cygwin. */ -# include <sys/un.h> -# include <net/if.h> -# include <netinet/in.h> -# include <netinet/tcp.h> -#endif - -#ifdef HAVE_PWD_H -# include <pwd.h> -#endif - -#ifdef HAVE_PATHS_H -# include <paths.h> -#endif -#include <rpc/types.h> -#include <rpc/xdr.h> -#include <gnutls/gnutls.h> -#include <gnutls/x509.h> -#include "gnutls_1_0_compat.h" -#if HAVE_SASL -# include <sasl/sasl.h> -#endif #include <libxml/uri.h> -#include <netdb.h> - -#include <poll.h> - +#include "virnetclient.h" +#include "virnetclientprogram.h" #include "virterror_internal.h" #include "logging.h" #include "datatypes.h" @@ -86,106 +48,24 @@ #define VIR_FROM_THIS VIR_FROM_REMOTE -#ifdef WIN32 -# define pipe(fds) _pipe(fds,4096, _O_BINARY) -#endif - - 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_data { + virMutex lock; -struct private_stream_data { - unsigned int has_error : 1; - remote_error err; - - unsigned int serial; - unsigned int proc_nr; - - 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; -#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; /* The list of domain event callbacks */ virDomainEventCallbackListPtr callbackList; @@ -196,15 +76,6 @@ struct private_data { int eventFlushTimer; /* Flag if we're in process of dispatching */ int domainEventDispatching; - - /* 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 { @@ -225,10 +96,6 @@ 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, @@ -272,10 +139,6 @@ void remoteDomainEventQueueFlush(int timer, void *opaque); /* Helper functions for remoteOpen. */ static char *get_transport_from_scheme (char *scheme); -/* GnuTLS functions used by remoteOpen. */ -static int initialize_gnutls(void); -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) @@ -290,7 +153,7 @@ remoteStartup(int privileged ATTRIBUTE_UNUSED) #ifndef WIN32 /** - * remoteFindServerPath: + * remoteFindDaemonPath: * * Tries to find the path to the libvirtd binary. * @@ -317,36 +180,68 @@ 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(); - const char *const daemonargs[] = { daemonPath, "--timeout=30", NULL }; - pid_t pid; - - if (!daemonPath) { - remoteError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to find libvirtd binary")); - return -1; - } - - if (virExecDaemonize(daemonargs, NULL, NULL, - &pid, -1, NULL, NULL, - VIR_EXEC_CLEAR_CAPS, - NULL, NULL, NULL) < 0) - return -1; - return 0; -} -#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 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_address }, +}; enum virDrvOpenRemoteFlags { VIR_DRV_OPEN_REMOTE_RO = (1 << 0), @@ -379,7 +274,6 @@ doRemoteOpen (virConnectPtr conn, int flags) { struct qparam_set *vars = NULL; - int wakeupFD[2] = { -1, -1 }; char *transport_str = NULL; enum { trans_tls, @@ -508,15 +402,10 @@ 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 + } else { DEBUG("passing through variable '%s' ('%s') to remote end", var->name, var->value); + } } /* Construct the original name. */ @@ -579,89 +468,35 @@ 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() == -1) goto failed; - priv->uses_tls = 1; + priv->tls = virNetTLSContextNewClient(LIBVIRT_CACERT, + LIBVIRT_CLIENTCERT, + LIBVIRT_CLIENTKEY, + 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_DEBUG0("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()); @@ -676,153 +511,57 @@ 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); - 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); + if (!(priv->client = virNetClientNewUNIX(sockname, + flags & VIR_DRV_OPEN_REMOTE_AUTOSTART, + remoteFindDaemonPath()))) goto failed; - } break; - } - - case trans_ssh: { - int j, nr_args = 6; - - if (username) nr_args += 2; /* For -l username */ - if (no_tty) nr_args += 5; /* For -T -o BatchMode=yes -e none */ - if (port) nr_args += 2; /* For -p port */ + case trans_ssh: command = command ? command : strdup ("ssh"); if (command == NULL) goto out_of_memory; - // Generate the final command argv[] array. - // ssh [-p $port] [-l $username] $hostname $netcat -U $sockname [NULL] - if (VIR_ALLOC_N(cmd_argv, nr_args) < 0) - goto out_of_memory; - - j = 0; - cmd_argv[j++] = strdup (command); - if (port) { - cmd_argv[j++] = strdup ("-p"); - cmd_argv[j++] = strdup (port); - } - if (username) { - cmd_argv[j++] = strdup ("-l"); - cmd_argv[j++] = strdup (username); - } - if (no_tty) { - cmd_argv[j++] = strdup ("-T"); - cmd_argv[j++] = strdup ("-o"); - cmd_argv[j++] = strdup ("BatchMode=yes"); - cmd_argv[j++] = strdup ("-e"); - cmd_argv[j++] = strdup ("none"); - } - cmd_argv[j++] = strdup (priv->hostname); - cmd_argv[j++] = strdup (netcat ? netcat : "nc"); - cmd_argv[j++] = strdup ("-U"); - cmd_argv[j++] = strdup (sockname ? sockname : - (flags & VIR_CONNECT_RO - ? LIBVIRTD_PRIV_UNIX_SOCKET_RO - : LIBVIRTD_PRIV_UNIX_SOCKET)); - cmd_argv[j++] = 0; - assert (j == nr_args); - for (j = 0; j < (nr_args-1); j++) - if (cmd_argv[j] == 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; - - 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")); - goto failed; } - if (pipe(errfd) == -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 (virExec((const char**)cmd_argv, NULL, NULL, - &pid, sv[1], &(sv[1]), &(errfd[1]), - VIR_EXEC_CLEAR_CAPS) < 0) - goto failed; + priv->is_secure = 1; + break; - /* Parent continues here. */ - VIR_FORCE_CLOSE(sv[1]); - VIR_FORCE_CLOSE(errfd[1]); - priv->sock = sv[0]; - priv->errfd = errfd[0]; - priv->pid = pid; + case trans_ext: + if (!(priv->client = virNetClientNewCommand((const char **)cmd_argv, NULL))) + goto failed; /* 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,36 +573,32 @@ doRemoteOpen (virConnectPtr conn, goto failed; #endif /* WIN32 */ - } /* switch (transport) */ - if (virSetNonBlock(priv->sock) < 0) { - virReportSystemError(errno, "%s", - _("unable to make socket non-blocking")); - goto failed; - } - - if ((priv->errfd != -1) && virSetNonBlock(priv->errfd) < 0) { - virReportSystemError(errno, "%s", - _("unable to make socket non-blocking")); + if (!(priv->remoteProgram = virNetClientProgramNew(REMOTE_PROGRAM, + REMOTE_PROTOCOL_VERSION, + remoteDomainEvents, + ARRAY_CARDINALITY(remoteDomainEvents), + conn, + NULL))) goto failed; - } - - if (pipe(wakeupFD) < 0) { - virReportSystemError(errno, "%s", - _("unable to make pipe")); + if (!(priv->qemuProgram = virNetClientProgramNew(QEMU_PROGRAM, + QEMU_PROTOCOL_VERSION, + NULL, + 0, + NULL, + NULL))) goto failed; - } - priv->wakeupReadFD = wakeupFD[0]; - priv->wakeupSendFD = wakeupFD[1]; /* Try and authenticate with server */ + VIR_DEBUG0("Trying authentication"); if (remoteAuthenticate(conn, priv, 1, auth, authtype) == -1) goto failed; /* Finally we can call the remote side's open function. */ remote_open_args args = { &name, flags }; + VIR_DEBUG("Trying to open URI %s", name); if (call (conn, priv, REMOTE_CALL_IN_OPEN, REMOTE_PROC_OPEN, (xdrproc_t) xdr_remote_open_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) @@ -874,6 +609,7 @@ doRemoteOpen (virConnectPtr conn, remote_get_uri_ret uriret; int urierr; + VIR_DEBUG0("Trying to query remote URI"); memset (&uriret, 0, sizeof uriret); urierr = call (conn, priv, REMOTE_CALL_IN_OPEN | REMOTE_CALL_QUIET_MISSING_RPC, @@ -902,36 +638,23 @@ doRemoteOpen (virConnectPtr conn, } } - if(VIR_ALLOC(priv->callbackList)<0) { + if (VIR_ALLOC(priv->callbackList)<0) { virReportOOMError(); goto failed; } - if(VIR_ALLOC(priv->domainEvents)<0) { + if (VIR_ALLOC(priv->domainEvents)<0) { virReportOOMError(); goto failed; } - DEBUG0("Adding Handler for remote events"); - /* Set up a callback to listen on the socket data */ - if ((priv->watch = virEventAddHandle(priv->sock, - VIR_EVENT_HANDLE_READABLE, - remoteDomainEventFired, - conn, NULL)) < 0) { - DEBUG0("virEventAddHandle failed: No addHandleImpl defined." - " continuing without events."); - } else { - - DEBUG0("Adding Timeout for remote event queue flushing"); - if ( (priv->eventFlushTimer = virEventAddTimeout(-1, - remoteDomainEventQueueFlush, - conn, NULL)) < 0) { - DEBUG0("virEventAddTimeout failed: No addTimeoutImpl defined. " - "continuing without events."); - virEventRemoveHandle(priv->watch); - priv->watch = -1; - } + DEBUG0("Adding Timeout for remote event queue flushing"); + if ((priv->eventFlushTimer = virEventAddTimeout(-1, + remoteDomainEventQueueFlush, + conn, NULL)) < 0) { + DEBUG0("Failed to add timer for dispatching events, disabling events"); } + /* Successful. */ retcode = VIR_DRV_OPEN_SUCCESS; @@ -961,30 +684,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; @@ -1008,8 +709,6 @@ remoteAllocPrivateData(void) remoteDriverLock(priv); priv->localUses = 1; priv->watch = -1; - priv->sock = -1; - priv->errfd = -1; return priv; } @@ -1121,435 +820,98 @@ 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) -{ - struct stat sb; - if (stat(file, &sb) < 0) { - virReportSystemError(errno, - _("Cannot access %s '%s'"), - type, file); - return -1; - } - return 0; -} - +/*----------------------------------------------------------------------*/ -static void remote_debug_gnutls_log(int level, const char* str) { - DEBUG("%d %s", level, str); -} static int -initialize_gnutls(void) +doRemoteClose (virConnectPtr conn, struct private_data *priv) { - static int initialized = 0; - int err; - char *gnutlsdebug; - - if (initialized) return 0; - - gnutls_global_init (); - - 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); - } - - /* 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; + if (priv->eventFlushTimer >= 0) { + /* Remove timeout */ + virEventRemoveTimeout(priv->eventFlushTimer); + /* Remove handle for remote events */ + virEventRemoveHandle(priv->watch); + priv->watch = -1; } - - if (check_cert_file("CA certificate", LIBVIRT_CACERT) < 0) - return -1; - if (check_cert_file("client key", LIBVIRT_CLIENTKEY) < 0) - return -1; - if (check_cert_file("client certificate", LIBVIRT_CLIENTCERT) < 0) + if (call (conn, priv, 0, REMOTE_PROC_CLOSE, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_void, (char *) NULL) == -1) return -1; - /* Set the trusted CA cert. */ - 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"), - gnutls_strerror (err)); - return -1; - } + virNetTLSContextFree(priv->tls); + priv->tls = NULL; + virNetClientFree(priv->client); + priv->client = NULL; + virNetClientProgramFree(priv->remoteProgram); + virNetClientProgramFree(priv->qemuProgram); + priv->remoteProgram = priv->qemuProgram = NULL; - /* Set the client certificate and private key. */ - 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/certificate: %s"), - gnutls_strerror (err)); - return -1; - } + /* Free hostname copy */ + VIR_FREE(priv->hostname); - initialized = 1; - return 0; -} + /* See comment for remoteType. */ + VIR_FREE(priv->type); -static int verify_certificate (virConnectPtr conn, struct private_data *priv, gnutls_session_t session); + /* Free callback list */ + virDomainEventCallbackListFree(priv->callbackList); -#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); -} + /* Free queued events */ + virDomainEventQueueFree(priv->domainEvents); -static ssize_t -custom_gnutls_pull(void *s, void *buf, size_t len) -{ - return recv((size_t)s, buf, len, 0); + return 0; } -#endif -static gnutls_session_t -negotiate_gnutls_on_connection (virConnectPtr conn, - struct private_data *priv, - int no_verify) +static int +remoteClose (virConnectPtr conn) { - const int cert_type_priority[3] = { - GNUTLS_CRT_X509, - GNUTLS_CRT_OPENPGP, - 0 - }; - 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; - } + int ret = 0; + struct private_data *priv = conn->privateData; - /* 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)); - return NULL; - } - err = - gnutls_certificate_type_set_priority (session, - cert_type_priority); - if (err) { - remoteError(VIR_ERR_GNUTLS_ERROR, - _("unable to set certificate priority: %s"), - gnutls_strerror (err)); - return NULL; + 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); - /* 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)); - return NULL; - } + return ret; +} - gnutls_transport_set_ptr (session, - (gnutls_transport_ptr_t) (long) priv->sock); +static int +remoteSupportsFeature (virConnectPtr conn, int feature) +{ + int rv = -1; + remote_supports_feature_args args; + remote_supports_feature_ret ret; + struct private_data *priv = conn->privateData; -#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 + remoteDriverLock(priv); - /* 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)); - return NULL; + /* VIR_DRV_FEATURE_REMOTE* features are handled directly. */ + if (feature == VIR_DRV_FEATURE_REMOTE) { + rv = 1; + goto done; } - /* Verify certificate. */ - if (verify_certificate (conn, priv, session) == -1) { - DEBUG0("failed to verify peer's certificate"); - if (!no_verify) return NULL; - } + args.feature = feature; - /* 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)); - return NULL; - } - if (len != 1 || buf[0] != '\1') { - remoteError(VIR_ERR_RPC, "%s", - _("server verification (of our certificate or IP " - "address) failed")); - return NULL; - } + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_SUPPORTS_FEATURE, + (xdrproc_t) xdr_remote_supports_feature_args, (char *) &args, + (xdrproc_t) xdr_remote_supports_feature_ret, (char *) &ret) == -1) + goto done; -#if 0 - /* Print session info. */ - print_info (session); -#endif + rv = ret.supported; - 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) -{ - if (priv->eventFlushTimer >= 0) { - /* Remove timeout */ - virEventRemoveTimeout(priv->eventFlushTimer); - /* Remove handle for remote events */ - virEventRemoveHandle(priv->watch); - priv->watch = -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); - - /* Free callback list */ - virDomainEventCallbackListFree(priv->callbackList); - - /* Free queued events */ - virDomainEventQueueFree(priv->domainEvents); - - 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; -} - -static int -remoteSupportsFeature (virConnectPtr conn, int feature) -{ - int rv = -1; - remote_supports_feature_args args; - remote_supports_feature_ret ret; - struct private_data *priv = conn->privateData; - - remoteDriverLock(priv); - - /* VIR_DRV_FEATURE_REMOTE* features are handled directly. */ - if (feature == VIR_DRV_FEATURE_REMOTE) { - rv = 1; - goto done; - } - - args.feature = feature; - - memset (&ret, 0, sizeof ret); - if (call (conn, priv, 0, REMOTE_PROC_SUPPORTS_FEATURE, - (xdrproc_t) xdr_remote_supports_feature_args, (char *) &args, - (xdrproc_t) xdr_remote_supports_feature_ret, (char *) &ret) == -1) - goto done; - - rv = ret.supported; - -done: - remoteDriverUnlock(priv); - return rv; +done: + remoteDriverUnlock(priv); + return rv; } /* Unfortunately this function is defined to return a static string. @@ -1701,13 +1063,8 @@ static int remoteIsEncrypted(virConnectPtr conn) (xdrproc_t) xdr_remote_is_secure_ret, (char *) &ret) == -1) goto done; - if (priv->uses_tls) + if (virNetClientIsEncrypted(priv->client)) encrypted = 1; -#if HAVE_SASL - else if (priv->saslconn) - encrypted = 1; -#endif - /* We claim to be encrypted, if the remote driver * transport itself is encrypted, and the remote @@ -7103,8 +6460,6 @@ static int remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, 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; @@ -7112,49 +6467,19 @@ 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; + virNetClientSaslContextPtr sasl; DEBUG0("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) - goto cleanup; if (auth) { if ((saslcb = remoteAuthMakeCallbacks(auth->credtype, auth->ncredtype)) == NULL) @@ -7164,59 +6489,32 @@ 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 = virNetClientSaslContextNew("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 */ 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 (virNetClientSaslContextExtKeySize(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 (virNetClientSaslContextSecProps(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); @@ -7240,22 +6538,16 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, restart: /* Start the auth negotiation on the client end first */ 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 = virNetClientSaslContextStart(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_CLIENT_SASL_INTERACT) { const char *msg; if (cred) { remoteAuthFreeCredentials(cred, ncred); @@ -7285,7 +6577,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"), - clientoutlen); + (int)clientoutlen); goto cleanup; } /* NB, distinction of NULL vs "" is *critical* in SASL */ @@ -7294,7 +6586,8 @@ 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; - DEBUG("Server start negotiation with mech %s. Data %d bytes %p", mech, clientoutlen, clientout); + DEBUG("Server start negotiation with mech %s. Data %d bytes %p", + mech, (int)clientoutlen, clientout); /* Now send the initial auth data to the server */ memset (&sret, 0, sizeof sret); @@ -7308,27 +6601,23 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, serverin = sret.nil ? NULL : sret.data.data_val; serverinlen = sret.data.data_len; DEBUG("Client step result complete: %d. Data %d bytes %p", - complete, serverinlen, serverin); + complete, (int)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 = virNetClientSaslContextStep(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_CLIENT_SASL_INTERACT) { const char *msg; if (cred) { remoteAuthFreeCredentials(cred, ncred); @@ -7354,10 +6643,11 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, } VIR_FREE(serverin); - DEBUG("Client step result %d. Data %d bytes %p", err, clientoutlen, clientout); + DEBUG("Client step result %d. Data %d bytes %p", + err, (int)clientoutlen, clientout); /* Previous server call showed completion & we're now locally complete too */ - if (complete && err == SASL_OK) + if (complete && err == VIR_NET_CLIENT_SASL_COMPLETE) break; /* Not done, prepare to talk with the server for another iteration */ @@ -7366,7 +6656,8 @@ 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; - DEBUG("Server step with %d bytes %p", clientoutlen, clientout); + DEBUG("Server step with %d bytes %p", + (int)clientoutlen, clientout); memset (&pret, 0, sizeof pret); if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_STEP, @@ -7380,10 +6671,10 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, serverinlen = pret.data.data_len; DEBUG("Client step result complete: %d. Data %d bytes %p", - complete, serverinlen, serverin); + complete, (int)serverinlen, serverin); /* This server call shows complete, and earlier client step was OK */ - if (complete && err == SASL_OK) { + if (complete && err == VIR_NET_CLIENT_SASL_COMPLETE) { VIR_FREE(serverin); break; } @@ -7391,14 +6682,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 = virNetClientSaslContextGetKeySize(sasl)) < 0) goto cleanup; - } - ssf = *(const int *)val; + DEBUG("SASL SSF value %d", ssf); if (ssf < 56) { /* 56 == DES level, good for Kerberos */ remoteError(VIR_ERR_AUTH_FAILED, @@ -7409,18 +6695,15 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, } DEBUG0("SASL authentication complete"); - priv->saslconn = saslconn; + virNetClientSetSASLContext(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); + virNetClientSaslContextFree(sasl); return ret; } @@ -7573,184 +6856,187 @@ done: return rv; } + +static int remoteDomainEventQueuePush(struct private_data *priv, + virDomainEventPtr event) +{ + int ret = -1; + remoteDriverLock(priv); + + if (virDomainEventQueuePush(priv->domainEvents, + event) < 0) { + DEBUG0("Error adding event to queue"); + goto cleanup; + } + + ret = 0; + virEventUpdateTimeout(priv->eventFlushTimer, 0); + +cleanup: + remoteDriverUnlock(priv); + return ret; +} + + /** * 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); - - /* unmarshall parameters, and process it*/ - if (! xdr_remote_domain_event_lifecycle_msg(xdr, &msg) ) { - remoteError(VIR_ERR_RPC, "%s", - _("unable to demarshall 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; + + if (remoteDomainEventQueuePush(priv, event) < 0) + virDomainEventFree(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); - - /* unmarshall parameters, and process it*/ - if (! xdr_remote_domain_event_reboot_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 = virDomainEventRebootNewFromDom(dom); - xdr_free ((xdrproc_t) &xdr_remote_domain_event_reboot_msg, (char *) &msg); - virDomainFree(dom); - return event; + + if (remoteDomainEventQueuePush(priv, event) < 0) + virDomainEventFree(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); - - /* unmarshall parameters, and process it*/ - if (! xdr_remote_domain_event_rtc_change_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; - - 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; + + if (remoteDomainEventQueuePush(priv, event) < 0) + virDomainEventFree(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); - - /* unmarshall parameters, and process it*/ - if (! xdr_remote_domain_event_watchdog_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; - - 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; + + if (remoteDomainEventQueuePush(priv, event) < 0) + virDomainEventFree(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); - - /* unmarshall parameters, and process it*/ - if (! xdr_remote_domain_event_io_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 = 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; + + if (remoteDomainEventQueuePush(priv, event) < 0) + virDomainEventFree(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; - virDomainPtr dom; + 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); - - /* unmarshall parameters, and process it*/ - if (! xdr_remote_domain_event_io_error_reason_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 = 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; + + if (remoteDomainEventQueuePush(priv, event) < 0) + virDomainEventFree(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; @@ -7758,58 +7044,49 @@ remoteDomainReadEventGraphics(virConnectPtr conn, XDR *xdr) virDomainEventGraphicsSubjectPtr subject = NULL; int i; - memset (&msg, 0, sizeof msg); - - /* unmarshall parameters, and process it*/ - if (! xdr_remote_domain_event_graphics_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; 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); + if (remoteDomainEventQueuePush(priv, event) < 0) + virDomainEventFree(event); + return; +no_memory: if (localAddr) { VIR_FREE(localAddr->service); VIR_FREE(localAddr->node); @@ -7828,7 +7105,7 @@ no_memory: VIR_FREE(subject->identities); VIR_FREE(subject); } - return NULL; + return; } @@ -8165,7 +7442,7 @@ done: return rv; } - +#if 0 static struct private_stream_data * remoteStreamOpen(virStreamPtr st, int output ATTRIBUTE_UNUSED, @@ -8712,7 +7989,7 @@ done: return rv; } - +#endif static int remoteCPUCompare(virConnectPtr conn, const char *xmlDesc, @@ -9244,7 +8521,7 @@ done: return rv; } - +#if 0 static int remoteDomainOpenConsole(virDomainPtr dom, const char *devname, @@ -9283,6 +8560,7 @@ done: return rv; } +#endif /*----------------------------------------------------------------------*/ @@ -9325,534 +8603,7 @@ done: return rv; } -/*----------------------------------------------------------------------*/ - -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, "%s", _("marshalling args")); - 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); - 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) { - char encoded[8192]; - int ret, err; - ret = remoteIOReadBuffer(priv, encoded, sizeof(encoded)); - if (ret < 0) - return -1; - if (ret == 0) - return 0; - - err = sasl_decode(priv->saslconn, encoded, 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; - 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 */ - 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, "%s", _("unmarshalling ret")); - 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, "%s", _("unmarshalling remote_error")); - return -1; - } - thecall->mode = REMOTE_MODE_ERROR; - return 0; - - default: - remoteError(VIR_ERR_RPC, _("unknown status (received %x)"), hdr->status); - return -1; - } -} - +#if 0 static int processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, int in_open, @@ -9871,31 +8622,31 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, switch (hdr->proc) { case REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE: - event = remoteDomainReadEventLifecycle(conn, xdr); + event = remoteDomainBuildEventLifecycle(conn, xdr); break; case REMOTE_PROC_DOMAIN_EVENT_REBOOT: - event = remoteDomainReadEventReboot(conn, xdr); + event = remoteDomainBuildEventReboot(conn, xdr); break; case REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE: - event = remoteDomainReadEventRTCChange(conn, xdr); + event = remoteDomainBuildEventRTCChange(conn, xdr); break; case REMOTE_PROC_DOMAIN_EVENT_WATCHDOG: - event = remoteDomainReadEventWatchdog(conn, xdr); + event = remoteDomainBuildEventWatchdog(conn, xdr); break; case REMOTE_PROC_DOMAIN_EVENT_IO_ERROR: - event = remoteDomainReadEventIOError(conn, xdr); + event = remoteDomainBuildEventIOError(conn, xdr); break; case REMOTE_PROC_DOMAIN_EVENT_IO_ERROR_REASON: - event = remoteDomainReadEventIOErrorReason(conn, xdr); + event = remoteDomainBuildEventIOErrorReason(conn, xdr); break; case REMOTE_PROC_DOMAIN_EVENT_GRAPHICS: - event = remoteDomainReadEventGraphics(conn, xdr); + event = remoteDomainBuildEventGraphics(conn, xdr); break; default: @@ -9906,16 +8657,11 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, if (!event) return -1; - if (virDomainEventQueuePush(priv->domainEvents, - event) < 0) { - DEBUG0("Error adding event to queue"); - virDomainEventFree(event); - } - virEventUpdateTimeout(priv->eventFlushTimer, 0); - return 0; } +#endif +#if 0 static int processCallDispatchStream(virConnectPtr conn ATTRIBUTE_UNUSED, struct private_data *priv, @@ -10022,485 +8768,43 @@ processCallDispatchStream(virConnectPtr conn ATTRIBUTE_UNUSED, 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, so return even - * though there might still be more data on - * the wire. We need to actually let the caller - * deal with this arrived message to keep good - * response, and also to correctly handle EOF. - */ - 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 - - 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), -1); - if (ret < 0 && errno == EAGAIN) - goto repoll; - -#ifdef HAVE_PTHREAD_SIGMASK - ignore_value(pthread_sigmask(SIG_SETMASK, &oldmask, NULL)); -#endif - - remoteDriverLock(priv); - - if (fds[1].revents) { - ssize_t s; - DEBUG0("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... - */ - DEBUG("Waking up sleep %d %p %p", tmp->proc_nr, tmp, priv->waitDispatch); - virCondSignal(&tmp->cond); - } - 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; - 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) { - 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; - 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) { - 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; - - 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; - } - - 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; - } - - 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; - } - - 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: - DEBUG("All done with our call %d %p %p", thiscall->proc_nr, priv->waitDispatch, thiscall); - if (thiscall->mode == REMOTE_MODE_ERROR) { - /* 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(flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - __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(flags & REMOTE_CALL_IN_OPEN ? NULL : conn, - __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++; + priv->localUses++; - thiscall = prepareCall(priv, flags, proc_nr, args_filter, args, - ret_filter, ret); - - if (!thiscall) { - virReportOOMError(); - return -1; - } - - rv = remoteIO(conn, priv, flags, thiscall); - VIR_FREE(thiscall); + /* 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, + priv->client, + counter, + proc_nr, + args_filter, args, + ret_filter, ret); + remoteDriverLock(priv); + priv->localUses--; 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; - - DEBUG("Event fired %d %d %d %X", watch, fd, event, event); - - if (event & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) { - 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) - DEBUG0("Something went wrong during async message processing"); - -done: - remoteDriverUnlock(priv); -} - static void remoteDomainEventDispatchFunc(virConnectPtr conn, virDomainEventPtr event, virConnectDomainEventGenericCallback cb, @@ -10753,7 +9057,7 @@ static virDriver remote_driver = { remoteNodeDeviceDettach, /* nodeDeviceDettach */ remoteNodeDeviceReAttach, /* nodeDeviceReAttach */ remoteNodeDeviceReset, /* nodeDeviceReset */ - remoteDomainMigratePrepareTunnel, /* domainMigratePrepareTunnel */ + NULL, //remoteDomainMigratePrepareTunnel, /* domainMigratePrepareTunnel */ remoteIsEncrypted, /* isEncrypted */ remoteIsSecure, /* isSecure */ remoteDomainIsActive, /* domainIsActive */ @@ -10781,7 +9085,7 @@ static virDriver remote_driver = { remoteQemuDomainMonitorCommand, /* qemuDomainMonitorCommand */ remoteDomainSetMemoryParameters, /* domainSetMemoryParameters */ remoteDomainGetMemoryParameters, /* domainGetMemoryParameters */ - remoteDomainOpenConsole, /* domainOpenConsole */ + NULL, //remoteDomainOpenConsole, /* domainOpenConsole */ }; static virNetworkDriver network_driver = { -- 1.7.2.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list