[libvirt] [PATCH 0/4] Switch over to use the new RPC code

This patch set actually switches the libvirtd daemon and remote driver client code to use the new RPC code. While it has had a fair amount of testing, it has also been rebased many many many times. There are bound to be things in it which break, hopefully not the actual RPC wire data format, but in the client/daemon dispatch code and thread safety, etc ....

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 | 5 +- src/remote/remote_driver.c | 3452 ++++++++------------------------------------ src/rpc/gendispatch.pl | 14 +- 3 files changed, 586 insertions(+), 2885 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 1e7b905..83d267f 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 @@ -1222,6 +1222,7 @@ endif libvirt_net_rpc_la_CFLAGS = \ $(GNUTLS_CFLAGS) \ $(SASL_CFLAGS) \ + $(XDR_CFLAGS) \ $(AM_CFLAGS) libvirt_net_rpc_la_LDFLAGS = \ $(GNUTLS_LIBS) \ diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 84a5eab..21651f3 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -23,51 +23,14 @@ #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 "virnetclientstream.h" #include "virterror_internal.h" #include "logging.h" #include "datatypes.h" @@ -107,119 +70,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 +104,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 +140,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 +161,7 @@ remoteStartup(int privileged ATTRIBUTE_UNUSED) #ifndef WIN32 /** - * remoteFindServerPath: + * remoteFindDaemonPath: * * Tries to find the path to the libvirtd binary. * @@ -326,37 +188,84 @@ 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 +remoteDomainBuildEventBlockPull(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 }, + { REMOTE_PROC_DOMAIN_EVENT_BLOCK_PULL, + remoteDomainBuildEventBlockPull, + sizeof(remote_domain_event_block_pull_msg), + (xdrproc_t)xdr_remote_domain_event_block_pull_msg }, +}; enum virDrvOpenRemoteFlags { VIR_DRV_OPEN_REMOTE_RO = (1 << 0), @@ -389,7 +298,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 +353,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 +431,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 +502,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 +544,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 +608,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 +646,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 +664,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 +682,6 @@ doRemoteOpen (virConnectPtr conn, VIR_FREE(netcat); VIR_FREE(username); VIR_FREE(port); - virCommandFree(cmd); VIR_FREE(pkipath); return retcode; @@ -949,30 +692,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 +716,6 @@ remoteAllocPrivateData(void) } remoteDriverLock(priv); priv->localUses = 1; - priv->watch = -1; - priv->sock = -1; - priv->errfd = -1; return priv; } @@ -1109,577 +827,139 @@ 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); -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 +2247,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 +2254,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 +2305,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 +2315,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 +2507,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 +2517,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 +2543,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 +2593,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); @@ -3410,7 +2632,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 */ @@ -3419,11 +2641,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 %d bytes %p", + mech, (int)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; @@ -3433,27 +2656,23 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, 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); + 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 = 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 +2698,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 %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_SASL_COMPLETE) break; /* Not done, prepare to talk with the server for another iteration */ @@ -3491,10 +2711,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 %d bytes %p", + (int)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; @@ -3505,10 +2726,10 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, serverinlen = pret.data.data_len; VIR_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_SASL_COMPLETE) { VIR_FREE(serverin); break; } @@ -3516,14 +2737,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 +2750,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 +2769,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 +2789,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 +2827,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 +2908,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 +3064,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 +3124,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 +3192,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 +3200,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 +3228,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 +3290,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; - } - - 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; + ret = virNetClientStreamEventRemoveCallback(privst); -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 +3422,7 @@ static virStreamDriver remoteStreamDrv = { .streamRemoveCallback = remoteStreamEventRemoveCallback, }; + static int remoteDomainEventRegisterAny(virConnectPtr conn, virDomainPtr dom, int eventID, @@ -4620,6 +3517,7 @@ done: return rv; } + /*----------------------------------------------------------------------*/ static int @@ -4793,23 +3691,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 +3724,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 +3910,41 @@ 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++; + priv->localUses++; - thiscall = prepareCall(priv, flags, proc_nr, args_filter, args, - ret_filter, ret); - - if (!thiscall) { - return -1; - } + /* 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--; - 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 +3960,7 @@ static void remoteDomainEventDispatchFunc(virConnectPtr conn, remoteDriverLock(priv); } -void +static void remoteDomainEventQueueFlush(int timer ATTRIBUTE_UNUSED, void *opaque) { virConnectPtr conn = opaque; @@ -6282,7 +3976,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 71085d9..d6264b9 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"; -- 1.7.4.4

On 06/27/2011 08:24 AM, Daniel P. Berrange wrote:
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 | 5 +- src/remote/remote_driver.c | 3452 ++++++++------------------------------------ src/rpc/gendispatch.pl | 14 +- 3 files changed, 586 insertions(+), 2885 deletions(-) @@ -1222,6 +1222,7 @@ endif libvirt_net_rpc_la_CFLAGS = \ $(GNUTLS_CFLAGS) \ $(SASL_CFLAGS) \ + $(XDR_CFLAGS) \
Should this hunk be done as a separate patch?
+++ b/src/remote/remote_driver.c @@ -23,51 +23,14 @@
+#include "virnetclient.h" +#include "virnetclientprogram.h" +#include "virnetclientstream.h" #include "virterror_internal.h" #include "logging.h" #include "datatypes.h"
'make syntax-check' is calling you for not removing the now-unused #include "ignore-value.h". Aargh. This needs yet another rebase to pick up the revert of BlockPull patches: remote/remote_driver.c:263:7: error: 'REMOTE_PROC_DOMAIN_EVENT_BLOCK_PULL' undeclared here (not in a function) remote/remote_driver.c:265:14: error: 'remote_domain_event_block_pull_msg' undeclared here (not in a function) remote/remote_driver.c:266:18: error: 'xdr_remote_domain_event_block_pull_msg' undeclared here (not in a function) cc1: warnings being treated as errors remote/remote_driver.c:222:1: error: 'remoteDomainBuildEventBlockPull' used but never defined
@@ -107,119 +70,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, -};
Replaced by virnetclient.c, but that enum only has MODE_WAIT_TX, MODE_WAIT_RX, and MODE_COMPLETE - I'm hoping that dropping the MODE_ERROR works out.
+static void +remoteDomainBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque);
Is it worth rearranging this file to be in topological order, to avoid having to use quite as many forward declarations? But that should be a separate followup patch.
@@ -524,12 +431,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")) {
I'm not sure why this hunk is here.
/*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 +544,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 +608,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 +646,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 +664,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 +682,6 @@ doRemoteOpen (virConnectPtr conn, VIR_FREE(netcat); VIR_FREE(username); VIR_FREE(port); - virCommandFree(cmd); VIR_FREE(pkipath);
return retcode; @@ -949,30 +692,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 +716,6 @@ remoteAllocPrivateData(void) } remoteDriverLock(priv); priv->localUses = 1; - priv->watch = -1; - priv->sock = -1; - priv->errfd = -1;
return priv; } @@ -1109,577 +827,139 @@ 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);
-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 +2247,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 +2254,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 +2305,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 +2315,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 +2507,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 +2517,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 +2543,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 +2593,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); @@ -3410,7 +2632,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 */ @@ -3419,11 +2641,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 %d bytes %p", + mech, (int)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; @@ -3433,27 +2656,23 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, 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); + 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 = 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 +2698,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 %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_SASL_COMPLETE) break;
/* Not done, prepare to talk with the server for another iteration */ @@ -3491,10 +2711,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 %d bytes %p", + (int)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; @@ -3505,10 +2726,10 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, serverinlen = pret.data.data_len;
VIR_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_SASL_COMPLETE) { VIR_FREE(serverin); break; } @@ -3516,14 +2737,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 +2750,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 +2769,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 +2789,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 +2827,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 +2908,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 +3064,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 +3124,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 +3192,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 +3200,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 +3228,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 +3290,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; - } - - 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; + ret = virNetClientStreamEventRemoveCallback(privst);
-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 +3422,7 @@ static virStreamDriver remoteStreamDrv = { .streamRemoveCallback = remoteStreamEventRemoveCallback, };
+ static int remoteDomainEventRegisterAny(virConnectPtr conn, virDomainPtr dom, int eventID, @@ -4620,6 +3517,7 @@ done: return rv; }
+ /*----------------------------------------------------------------------*/
static int @@ -4793,23 +3691,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 +3724,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 +3910,41 @@ 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++; + priv->localUses++;
- thiscall = prepareCall(priv, flags, proc_nr, args_filter, args, - ret_filter, ret); - - if (!thiscall) { - return -1; - } + /* 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--;
- 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 +3960,7 @@ static void remoteDomainEventDispatchFunc(virConnectPtr conn, remoteDriverLock(priv); }
-void +static void remoteDomainEventQueueFlush(int timer ATTRIBUTE_UNUSED, void *opaque) { virConnectPtr conn = opaque; @@ -6282,7 +3976,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 71085d9..d6264b9 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";
-- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 06/27/2011 02:08 PM, Eric Blake wrote: Aargh, I hit send too soon.
+ case trans_ext: { + char const *cmd_argv[] = { command, NULL }; + if (!(priv->client = virNetClientNewExternal(cmd_argv)))
This appears to be the only call to virNetClientNewExternal. And it only passes a single argument, command. Should we simplify the signature to be const char *command instead of const char **cmdargv? But that can be a separate patch (or you can prove me wrong, if later patches also start using this with more than just the command name).
@@ -874,26 +646,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,
Is the loss of REMOTE_CALL_QUIET_MISSING_RPC going to make this new code noisy when talking to an (extremely) old remote client? Or is the assumption that the deleted comment still applies, and the only reason we were using the QUIET_MISSING_RPC call would be to silence a one-time failure upon a libvirtd upgrade, but where we can assume that it is no longer a practical concern.
static int -check_cert_file(const char *type, const char *file) +doRemoteClose (virConnectPtr conn, struct private_data *priv) {
+ 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);
You had several instances of cleanup followed by NULL-ing the argument, to avoid double cleanup; does priv->domainEventState need this same protection? But it does make sense that doRemoteClose is used to sever connections while keeping the object still alive, so that it does part, but not all, of the work of the final cleanup function and must protect against double 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)
Those look like magic numbers, but they were there before the refactoring, so not fatal to this patch.
@@ -3410,7 +2632,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);
Rather than adding a cast to cope with the change of clientoutlen from int to size_t, shouldn't you instead change %d to %zu?
goto cleanup; } /* NB, distinction of NULL vs "" is *critical* in SASL */ @@ -3419,11 +2641,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 %d bytes %p", + mech, (int)clientoutlen, clientout);
Likewise.
/* 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; @@ -3433,27 +2656,23 @@ remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, 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); + complete, (int)serverinlen, serverin);
Likewise.
@@ -3479,10 +2698,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 %d bytes %p", + err, (int)clientoutlen, clientout);
and so on.
+static void remoteStreamEventCallback(virNetClientStreamPtr stream ATTRIBUTE_UNUSED, + int events, + void *opaque) {
+ 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);
Given that remoteStreamEventCallback unlocked the driver for the duration of the callback, should remoteStreamCallbackFree be doing likewise?
/* * 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++;
This shares a single counter among two programs, rather than giving each program its own counter. Is that ever going to cause a problem, where a single program sees a skipped counter because it was interleaved with other programs?
- thiscall = prepareCall(priv, flags, proc_nr, args_filter, args, - ret_filter, ret); - - if (!thiscall) { - return -1; - } + /* 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,
Is it safe to grab priv->client while priv is unlocked, or do you need a local variable cache? Conditional ACK. Most of my comments can either be trivially addressed or rebutted, or were ideas for future cleanups that don't impact this patch. Squash this in to compile: diff --git i/src/remote/remote_driver.c w/src/remote/remote_driver.c index 87879e3..17c27f8 100644 --- i/src/remote/remote_driver.c +++ w/src/remote/remote_driver.c @@ -43,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" @@ -220,10 +219,6 @@ remoteDomainBuildEventGraphics(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); static void -remoteDomainBuildEventBlockPull(virNetClientProgramPtr prog, - virNetClientPtr client, - void *evdata, void *opaque); -static void remoteDomainBuildEventControlError(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); @@ -261,10 +256,6 @@ static virNetClientProgramEvent remoteDomainEvents[] = { remoteDomainBuildEventControlError, sizeof(remote_domain_event_control_error_msg), (xdrproc_t)xdr_remote_domain_event_control_error_msg }, - { REMOTE_PROC_DOMAIN_EVENT_BLOCK_PULL, - remoteDomainBuildEventBlockPull, - sizeof(remote_domain_event_block_pull_msg), - (xdrproc_t)xdr_remote_domain_event_block_pull_msg }, }; enum virDrvOpenRemoteFlags { -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Mon, Jun 27, 2011 at 02:49:37PM -0600, Eric Blake wrote:
On 06/27/2011 02:08 PM, Eric Blake wrote:
Aargh, I hit send too soon.
+ case trans_ext: { + char const *cmd_argv[] = { command, NULL }; + if (!(priv->client = virNetClientNewExternal(cmd_argv)))
This appears to be the only call to virNetClientNewExternal. And it only passes a single argument, command. Should we simplify the signature to be const char *command instead of const char **cmdargv? But that can be a separate patch (or you can prove me wrong, if later patches also start using this with more than just the command name).
This in turns calls to virNetSocketNewExternal which has the same signature, and I thought it was more useful to have it take a full argv, rather than restrict it to just server the needs of our remote driver code.
@@ -874,26 +646,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,
Is the loss of REMOTE_CALL_QUIET_MISSING_RPC going to make this new code noisy when talking to an (extremely) old remote client? Or is the assumption that the deleted comment still applies, and the only reason we were using the QUIET_MISSING_RPC call would be to silence a one-time failure upon a libvirtd upgrade, but where we can assume that it is no longer a practical concern.
Yes, this is a mistake. There were two places using REMOTE_CALL_QUIET_MISSING_RPC, this and the authentication routines. In the latter I added 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 < 0) { virErrorPtr verr = virGetLastError(); if (verr && verr->code == VIR_ERR_NO_SUPPORT) { /* Missing RPC - old server - ignore */ virResetLastError(); return 0; } return -1; } And I should have done the same with the URI call too. I'll add a note to explicitly test compatibility against an old server.
static int -check_cert_file(const char *type, const char *file) +doRemoteClose (virConnectPtr conn, struct private_data *priv) {
+ 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);
You had several instances of cleanup followed by NULL-ing the argument, to avoid double cleanup; does priv->domainEventState need this same protection? But it does make sense that doRemoteClose is used to sever connections while keeping the object still alive, so that it does part, but not all, of the work of the final cleanup function and must protect against double cleanup.
Yes, it is probably worth nulling domainEventState.
- 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)
Those look like magic numbers, but they were there before the refactoring, so not fatal to this patch.
@@ -3410,7 +2632,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);
Rather than adding a cast to cope with the change of clientoutlen from int to size_t, shouldn't you instead change %d to %zu?
Yes, I got most of those converted, but missed a few.
+static void remoteStreamEventCallback(virNetClientStreamPtr stream ATTRIBUTE_UNUSED, + int events, + void *opaque) {
+ 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);
Given that remoteStreamEventCallback unlocked the driver for the duration of the callback, should remoteStreamCallbackFree be doing likewise?
There are a few bugs in this streams callback code which I'll send a followup patch to fix soon.
/* * 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++;
This shares a single counter among two programs, rather than giving each program its own counter. Is that ever going to cause a problem, where a single program sees a skipped counter because it was interleaved with other programs?
Nothing requires that the counter variable be monotonically incrementing. It merely needs to be unique for the duration of the connection, so we can reliably match up calls to returns, and streams to calls. The previous code also had one counter for both programs.
- thiscall = prepareCall(priv, flags, proc_nr, args_filter, args, - ret_filter, ret); - - if (!thiscall) { - return -1; - } + /* 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,
Is it safe to grab priv->client while priv is unlocked, or do you need a local variable cache?
For sanity, I can cache the variable Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Tue, Jun 28, 2011 at 11:35:11AM +0100, Daniel P. Berrange wrote:
On Mon, Jun 27, 2011 at 02:49:37PM -0600, Eric Blake wrote:
On 06/27/2011 02:08 PM, Eric Blake wrote:
@@ -874,26 +646,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,
Is the loss of REMOTE_CALL_QUIET_MISSING_RPC going to make this new code noisy when talking to an (extremely) old remote client? Or is the assumption that the deleted comment still applies, and the only reason we were using the QUIET_MISSING_RPC call would be to silence a one-time failure upon a libvirtd upgrade, but where we can assume that it is no longer a practical concern.
Yes, this is a mistake. There were two places using REMOTE_CALL_QUIET_MISSING_RPC, this and the authentication routines. In the latter I added
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 < 0) { virErrorPtr verr = virGetLastError(); if (verr && verr->code == VIR_ERR_NO_SUPPORT) { /* Missing RPC - old server - ignore */ virResetLastError(); return 0; } return -1; }
And I should have done the same with the URI call too. I'll add a note to explicitly test compatibility against an old server.
Actually this turns out to *not* be a mistake. Even though the original code set QUIET_MISSING_RPC, we still raised a fatal error here. In practice we would never hit this scenario, because this codepath is only taken if the URI for virConnectOpen() is NULL. In such a case the libvirtd daemon must be local, and so will be running the same version as virsh. So the RPC call won't be missing unless the old daemon is left running after an RPM upgrade, but RPM %post does a restart so we're basically safe. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Mon, Jun 27, 2011 at 02:08:07PM -0600, Eric Blake wrote:
On 06/27/2011 08:24 AM, Daniel P. Berrange wrote:
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 | 5 +- src/remote/remote_driver.c | 3452 ++++++++------------------------------------ src/rpc/gendispatch.pl | 14 +- 3 files changed, 586 insertions(+), 2885 deletions(-) @@ -1222,6 +1222,7 @@ endif libvirt_net_rpc_la_CFLAGS = \ $(GNUTLS_CFLAGS) \ $(SASL_CFLAGS) \ + $(XDR_CFLAGS) \
Should this hunk be done as a separate patch?
Yeah, should do really.
+++ b/src/remote/remote_driver.c @@ -23,51 +23,14 @@
+#include "virnetclient.h" +#include "virnetclientprogram.h" +#include "virnetclientstream.h" #include "virterror_internal.h" #include "logging.h" #include "datatypes.h"
'make syntax-check' is calling you for not removing the now-unused #include "ignore-value.h".
Aargh. This needs yet another rebase to pick up the revert of BlockPull patches:
remote/remote_driver.c:263:7: error: 'REMOTE_PROC_DOMAIN_EVENT_BLOCK_PULL' undeclared here (not in a function) remote/remote_driver.c:265:14: error: 'remote_domain_event_block_pull_msg' undeclared here (not in a function) remote/remote_driver.c:266:18: error: 'xdr_remote_domain_event_block_pull_msg' undeclared here (not in a function) cc1: warnings being treated as errors remote/remote_driver.c:222:1: error: 'remoteDomainBuildEventBlockPull' used but never defined
Yeah, I messed that rebase up & have fixed that by removing those unused pieces.
@@ -107,119 +70,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, -};
Replaced by virnetclient.c, but that enum only has MODE_WAIT_TX, MODE_WAIT_RX, and MODE_COMPLETE - I'm hoping that dropping the MODE_ERROR works out.
Yes, I merged MODE_COMPLETE and MODE_ERROR, into the single MODE_COMPLETE and now distinguish error conditons by just looking to see if the virError object has been set.
+static void +remoteDomainBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque);
Is it worth rearranging this file to be in topological order, to avoid having to use quite as many forward declarations? But that should be a separate followup patch.
@@ -524,12 +431,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")) {
I'm not sure why this hunk is here.
The 'priv->debugLog' variable was not used anywhere, so I killed it. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

This guts the libvirtd daemon, removing all its networking and RPC handling code. Instead it calls out to the new virServerPtr APIs for all its RPC & networking work As a fallout all libvirtd daemon error reporting now takes place via the normal internal error reporting APIs. There is no need to call separate error reporting APIs in RPC code, nor should code use VIR_WARN/VIR_ERROR for reporting fatal problems anymore. * daemon/qemu_dispatch_*.h, daemon/remote_dispatch_*.h: Remove old generated dispatcher code * daemon/qemu_dispatch.h, daemon/remote_dispatch.h: New dispatch code * daemon/dispatch.c, daemon/dispatch.h: Remove obsoleted code * daemon/remote.c, daemon/remote.h: Rewrite for new dispatch APIs * daemon/libvirtd.c, daemon/libvirtd.h: Remove all networking code * daemon/stream.c, daemon/stream.h: Update for new APIs * daemon/Makefile.am: Link to libvirt-net-rpc-server.la --- configure.ac | 11 - daemon/Makefile.am | 77 +- daemon/dispatch.c | 693 ----------- daemon/dispatch.h | 68 - daemon/libvirtd.c | 3171 ++++++++++-------------------------------------- daemon/libvirtd.h | 245 +---- daemon/remote.c | 1560 +++++++++++------------- daemon/remote.h | 64 +- daemon/stream.c | 495 +++++---- daemon/stream.h | 28 +- po/POTFILES.in | 1 - src/rpc/gendispatch.pl | 292 +++--- 12 files changed, 1819 insertions(+), 4886 deletions(-) delete mode 100644 daemon/dispatch.c delete mode 100644 daemon/dispatch.h diff --git a/configure.ac b/configure.ac index a59e8e1..cd22afb 100644 --- a/configure.ac +++ b/configure.ac @@ -299,17 +299,6 @@ if test x"$enable_debug" = x"yes"; then fi -AC_MSG_CHECKING([where to write libvirtd PID file]) -AC_ARG_WITH([remote-pid-file], [AC_HELP_STRING([--with-remote-pid-file=@<:@pidfile|none@:>@], [PID file for libvirtd])]) -if test "x$with_remote_pid_file" = "x" ; then - REMOTE_PID_FILE="$localstatedir/run/libvirtd.pid" -elif test "x$with_remote_pid_file" = "xnone" ; then - REMOTE_PID_FILE="" -else - REMOTE_PID_FILE="$with_remote_pid_file" -fi -AC_SUBST([REMOTE_PID_FILE]) -AC_MSG_RESULT($REMOTE_PID_FILE) dnl dnl init script flavor diff --git a/daemon/Makefile.am b/daemon/Makefile.am index ad14c90..57499ac 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -3,33 +3,21 @@ CLEANFILES = DAEMON_GENERATED = \ - $(srcdir)/remote_dispatch_prototypes.h \ - $(srcdir)/remote_dispatch_table.h \ - $(srcdir)/remote_dispatch_args.h \ - $(srcdir)/remote_dispatch_ret.h \ - $(srcdir)/remote_dispatch_bodies.h \ - $(srcdir)/qemu_dispatch_prototypes.h \ - $(srcdir)/qemu_dispatch_table.h \ - $(srcdir)/qemu_dispatch_args.h \ - $(srcdir)/qemu_dispatch_ret.h \ - $(srcdir)/qemu_dispatch_bodies.h + $(srcdir)/remote_dispatch.h \ + $(srcdir)/qemu_dispatch.h DAEMON_SOURCES = \ libvirtd.c libvirtd.h \ remote.c remote.h \ - dispatch.c dispatch.h \ stream.c stream.h \ ../src/remote/remote_protocol.c \ ../src/remote/qemu_protocol.c \ $(DAEMON_GENERATED) -AVAHI_SOURCES = \ - mdns.c mdns.h - DISTCLEANFILES = EXTRA_DIST = \ - remote_dispatch_bodies.h \ - qemu_dispatch_bodies.h \ + remote_dispatch.h \ + qemu_dispatch.h \ libvirtd.conf \ libvirtd.init.in \ libvirtd.upstart \ @@ -47,7 +35,6 @@ EXTRA_DIST = \ libvirtd.pod.in \ libvirtd.8.in \ libvirtd.stp \ - $(AVAHI_SOURCES) \ $(DAEMON_SOURCES) BUILT_SOURCES = @@ -55,52 +42,12 @@ BUILT_SOURCES = REMOTE_PROTOCOL = $(top_srcdir)/src/remote/remote_protocol.x QEMU_PROTOCOL = $(top_srcdir)/src/remote/qemu_protocol.x -$(srcdir)/remote_dispatch_prototypes.h: $(srcdir)/../src/rpc/gendispatch.pl \ - $(REMOTE_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/../src/rpc/gendispatch.pl -c -p remote \ - $(REMOTE_PROTOCOL) > $@ - -$(srcdir)/remote_dispatch_table.h: $(srcdir)/../src/rpc/gendispatch.pl \ - $(REMOTE_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/../src/rpc/gendispatch.pl -c -t remote \ - $(REMOTE_PROTOCOL) > $@ - -$(srcdir)/remote_dispatch_args.h: $(srcdir)/../src/rpc/gendispatch.pl \ - $(REMOTE_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/../src/rpc/gendispatch.pl -c -a remote \ - $(REMOTE_PROTOCOL) > $@ - -$(srcdir)/remote_dispatch_ret.h: $(srcdir)/../src/rpc/gendispatch.pl \ - $(REMOTE_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/../src/rpc/gendispatch.pl -c -r remote \ - $(REMOTE_PROTOCOL) > $@ - -$(srcdir)/remote_dispatch_bodies.h: $(srcdir)/../src/rpc/gendispatch.pl \ +$(srcdir)/remote_dispatch.h: $(srcdir)/../src/rpc/gendispatch.pl \ $(REMOTE_PROTOCOL) $(AM_V_GEN)perl -w $(srcdir)/../src/rpc/gendispatch.pl -c -b remote \ $(REMOTE_PROTOCOL) > $@ -$(srcdir)/qemu_dispatch_prototypes.h: $(srcdir)/../src/rpc/gendispatch.pl \ - $(QEMU_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/../src/rpc/gendispatch.pl -p qemu \ - $(QEMU_PROTOCOL) > $@ - -$(srcdir)/qemu_dispatch_table.h: $(srcdir)/../src/rpc/gendispatch.pl \ - $(QEMU_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/../src/rpc/gendispatch.pl -t qemu \ - $(QEMU_PROTOCOL) > $@ - -$(srcdir)/qemu_dispatch_args.h: $(srcdir)/../src/rpc/gendispatch.pl \ - $(QEMU_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/../src/rpc/gendispatch.pl -a qemu \ - $(QEMU_PROTOCOL) > $@ - -$(srcdir)/qemu_dispatch_ret.h: $(srcdir)/../src/rpc/gendispatch.pl \ - $(QEMU_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/../src/rpc/gendispatch.pl -r qemu \ - $(QEMU_PROTOCOL) > $@ - -$(srcdir)/qemu_dispatch_bodies.h: $(srcdir)/../src/rpc/gendispatch.pl \ +$(srcdir)/qemu_dispatch.h: $(srcdir)/../src/rpc/gendispatch.pl \ $(QEMU_PROTOCOL) $(AM_V_GEN)perl -w $(srcdir)/../src/rpc/gendispatch.pl -b qemu \ $(QEMU_PROTOCOL) > $@ @@ -137,6 +84,7 @@ libvirtd_CFLAGS = \ -I$(top_srcdir)/src \ -I$(top_srcdir)/src/util \ -I$(top_srcdir)/src/conf \ + -I$(top_srcdir)/src/rpc \ -I$(top_srcdir)/src/remote \ $(LIBXML_CFLAGS) $(GNUTLS_CFLAGS) $(SASL_CFLAGS) \ $(XDR_CFLAGS) $(POLKIT_CFLAGS) \ @@ -155,7 +103,10 @@ libvirtd_LDADD = \ $(SASL_LIBS) \ $(POLKIT_LIBS) -libvirtd_LDADD += ../src/libvirt-qemu.la +libvirtd_LDADD += \ + ../src/libvirt-net-rpc-server.la \ + ../src/libvirt-net-rpc.la \ + ../src/libvirt-qemu.la if ! WITH_DRIVER_MODULES if WITH_QEMU @@ -211,11 +162,7 @@ policyfile = libvirtd.policy-1 endif endif -if HAVE_AVAHI -libvirtd_SOURCES += $(AVAHI_SOURCES) -libvirtd_CFLAGS += $(AVAHI_CFLAGS) -libvirtd_LDADD += $(AVAHI_LIBS) -endif +EXTRA_DIST += probes.d libvirtd.stp if WITH_DTRACE libvirtd_LDADD += probes.o diff --git a/daemon/dispatch.c b/daemon/dispatch.c deleted file mode 100644 index 010be1e..0000000 --- a/daemon/dispatch.c +++ /dev/null @@ -1,693 +0,0 @@ -/* - * dispatch.h: RPC message dispatching infrastructure - * - * Copyright (C) 2007, 2008, 2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Richard W.M. Jones <rjones@redhat.com> - * Author: Daniel P. Berrange <berrange@redhat.com> - */ - -#include <config.h> - -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> - -#include "dispatch.h" -#include "remote.h" - -#include "memory.h" - -/* Convert a libvirt virError object into wire format */ -static void -remoteDispatchCopyError (remote_error *rerr, - virErrorPtr verr) -{ - rerr->code = verr->code; - rerr->domain = verr->domain; - rerr->message = verr->message ? malloc(sizeof(char*)) : NULL; - if (rerr->message) *rerr->message = strdup(verr->message); - rerr->level = verr->level; - rerr->str1 = verr->str1 ? malloc(sizeof(char*)) : NULL; - if (rerr->str1) *rerr->str1 = strdup(verr->str1); - rerr->str2 = verr->str2 ? malloc(sizeof(char*)) : NULL; - if (rerr->str2) *rerr->str2 = strdup(verr->str2); - rerr->str3 = verr->str3 ? malloc(sizeof(char*)) : NULL; - if (rerr->str3) *rerr->str3 = strdup(verr->str3); - rerr->int1 = verr->int1; - rerr->int2 = verr->int2; -} - - -/* A set of helpers for sending back errors to client - in various ways .... */ - -static void -remoteDispatchStringError (remote_error *rerr, - int code, const char *msg) -{ - virError verr; - - memset(&verr, 0, sizeof verr); - - /* Construct the dummy libvirt virError. */ - verr.code = code; - verr.domain = VIR_FROM_REMOTE; - verr.message = (char *)msg; - verr.level = VIR_ERR_ERROR; - verr.str1 = (char *)msg; - - remoteDispatchCopyError(rerr, &verr); -} - - -void remoteDispatchAuthError (remote_error *rerr) -{ - remoteDispatchStringError (rerr, VIR_ERR_AUTH_FAILED, "authentication failed"); -} - - -void remoteDispatchFormatError (remote_error *rerr, - const char *fmt, ...) -{ - va_list args; - char msgbuf[1024]; - char *msg = msgbuf; - - va_start (args, fmt); - vsnprintf (msgbuf, sizeof msgbuf, fmt, args); - va_end (args); - - remoteDispatchStringError (rerr, VIR_ERR_RPC, msg); -} - - -void remoteDispatchGenericError (remote_error *rerr) -{ - remoteDispatchStringError(rerr, - VIR_ERR_INTERNAL_ERROR, - "library function returned error but did not set virterror"); -} - - -void remoteDispatchError(remote_error *rerr) -{ - virErrorPtr verr = virGetLastError(); - - if (verr) - remoteDispatchCopyError(rerr, verr); - else - remoteDispatchGenericError(rerr); -} - -static int -remoteSerializeError(struct qemud_client *client, - remote_error *rerr, - int program, - int version, - int procedure, - int type, - int serial) -{ - XDR xdr; - unsigned int len; - struct qemud_client_message *msg = NULL; - - VIR_DEBUG("prog=%d ver=%d proc=%d type=%d serial=%d, msg=%s", - program, version, procedure, type, serial, - rerr->message ? *rerr->message : "(none)"); - - if (VIR_ALLOC(msg) < 0) - goto fatal_error; - - /* Return header. */ - msg->hdr.prog = program; - msg->hdr.vers = version; - msg->hdr.proc = procedure; - msg->hdr.type = type; - msg->hdr.serial = serial; - msg->hdr.status = REMOTE_ERROR; - - msg->bufferLength = sizeof(msg->buffer); - - /* Serialise the return header. */ - xdrmem_create (&xdr, - msg->buffer, - msg->bufferLength, - XDR_ENCODE); - - len = 0; /* We'll come back and write this later. */ - if (!xdr_u_int (&xdr, &len)) - goto xdr_error; - - if (!xdr_remote_message_header (&xdr, &msg->hdr)) - goto xdr_error; - - /* Error was not set, so synthesize a generic error message. */ - if (rerr->code == 0) - remoteDispatchGenericError(rerr); - - if (!xdr_remote_error (&xdr, rerr)) - goto xdr_error; - - /* Write the length word. */ - len = xdr_getpos (&xdr); - if (xdr_setpos (&xdr, 0) == 0) - goto xdr_error; - - if (!xdr_u_int (&xdr, &len)) - goto xdr_error; - - xdr_destroy (&xdr); - - msg->bufferLength = len; - msg->bufferOffset = 0; - - /* Put reply on end of tx queue to send out */ - qemudClientMessageQueuePush(&client->tx, msg); - qemudUpdateClientEvent(client); - xdr_free((xdrproc_t)xdr_remote_error, (char *)rerr); - - return 0; - -xdr_error: - VIR_WARN("Failed to serialize remote error '%s' as XDR", - rerr->message ? *rerr->message : "<unknown>"); - xdr_destroy(&xdr); - VIR_FREE(msg); -fatal_error: - xdr_free((xdrproc_t)xdr_remote_error, (char *)rerr); - return -1; -} - - -/* - * @client: the client to send the error to - * @rerr: the error object to send - * @req: the message this error is in reply to - * - * Send an error message to the client - * - * Returns 0 if the error was sent, -1 upon fatal error - */ -int -remoteSerializeReplyError(struct qemud_client *client, - remote_error *rerr, - remote_message_header *req) { - /* - * For data streams, errors are sent back as data streams - * For method calls, errors are sent back as method replies - */ - return remoteSerializeError(client, - rerr, - req->prog, - req->vers, - req->proc, - req->type == REMOTE_STREAM ? REMOTE_STREAM : REMOTE_REPLY, - req->serial); -} - -int -remoteSerializeStreamError(struct qemud_client *client, - remote_error *rerr, - int proc, - int serial) -{ - return remoteSerializeError(client, - rerr, - REMOTE_PROGRAM, - REMOTE_PROTOCOL_VERSION, - proc, - REMOTE_STREAM, - serial); -} - -/* - * @msg: the complete incoming message, whose header to decode - * - * Decodes the header part of the client message, but does not - * validate the decoded fields in the header. It expects - * bufferLength to refer to length of the data packet. Upon - * return bufferOffset will refer to the amount of the packet - * consumed by decoding of the header. - * - * returns 0 if successfully decoded, -1 upon fatal error - */ -int -remoteDecodeClientMessageHeader (struct qemud_client_message *msg) -{ - XDR xdr; - int ret = -1; - - msg->bufferOffset = REMOTE_MESSAGE_HEADER_XDR_LEN; - - /* Parse the header. */ - xdrmem_create (&xdr, - msg->buffer + msg->bufferOffset, - msg->bufferLength - msg->bufferOffset, - XDR_DECODE); - - if (!xdr_remote_message_header (&xdr, &msg->hdr)) - goto cleanup; - - msg->bufferOffset += xdr_getpos(&xdr); - - ret = 0; - -cleanup: - xdr_destroy(&xdr); - return ret; -} - - -/* - * @msg: the outgoing message, whose header to encode - * - * Encodes the header part of the client message, setting the - * message offset ready to encode the payload. Leaves space - * for the length field later. Upon return bufferLength will - * refer to the total available space for message, while - * bufferOffset will refer to current space used by header - * - * returns 0 if successfully encoded, -1 upon fatal error - */ -int -remoteEncodeClientMessageHeader (struct qemud_client_message *msg) -{ - XDR xdr; - int ret = -1; - unsigned int len = 0; - - msg->bufferLength = sizeof(msg->buffer); - msg->bufferOffset = 0; - - /* Format the header. */ - xdrmem_create (&xdr, - msg->buffer, - msg->bufferLength, - XDR_ENCODE); - - /* The real value is filled in shortly */ - if (!xdr_u_int (&xdr, &len)) { - goto cleanup; - } - - if (!xdr_remote_message_header (&xdr, &msg->hdr)) - goto cleanup; - - len = xdr_getpos(&xdr); - xdr_setpos(&xdr, 0); - - /* Fill in current length - may be re-written later - * if a payload is added - */ - if (!xdr_u_int (&xdr, &len)) { - goto cleanup; - } - - msg->bufferOffset += len; - - ret = 0; - -cleanup: - xdr_destroy(&xdr); - return ret; -} - - -static int -remoteDispatchClientCall (struct qemud_server *server, - struct qemud_client *client, - struct qemud_client_message *msg, - bool qemu_protocol); - - -/* - * @server: the unlocked server object - * @client: the locked client object - * @msg: the complete incoming message packet, with header already decoded - * - * This function gets called from qemud when it pulls a incoming - * remote protocol message off the dispatch queue for processing. - * - * The @msg parameter must have had its header decoded already by - * calling remoteDecodeClientMessageHeader - * - * Returns 0 if the message was dispatched, -1 upon fatal error - */ -int -remoteDispatchClientRequest(struct qemud_server *server, - struct qemud_client *client, - struct qemud_client_message *msg) -{ - int ret; - remote_error rerr; - bool qemu_call; - - VIR_DEBUG("prog=%d ver=%d type=%d status=%d serial=%d proc=%d", - msg->hdr.prog, msg->hdr.vers, msg->hdr.type, - msg->hdr.status, msg->hdr.serial, msg->hdr.proc); - - memset(&rerr, 0, sizeof rerr); - - /* Check version, etc. */ - if (msg->hdr.prog == REMOTE_PROGRAM) - qemu_call = false; - else if (msg->hdr.prog == QEMU_PROGRAM) - qemu_call = true; - else { - remoteDispatchFormatError (&rerr, - _("program mismatch (actual %x, expected %x or %x)"), - msg->hdr.prog, REMOTE_PROGRAM, QEMU_PROGRAM); - goto error; - } - - if (!qemu_call && msg->hdr.vers != REMOTE_PROTOCOL_VERSION) { - remoteDispatchFormatError (&rerr, - _("version mismatch (actual %x, expected %x)"), - msg->hdr.vers, REMOTE_PROTOCOL_VERSION); - goto error; - } - else if (qemu_call && msg->hdr.vers != QEMU_PROTOCOL_VERSION) { - remoteDispatchFormatError (&rerr, - _("version mismatch (actual %x, expected %x)"), - msg->hdr.vers, QEMU_PROTOCOL_VERSION); - goto error; - } - - switch (msg->hdr.type) { - case REMOTE_CALL: - return remoteDispatchClientCall(server, client, msg, qemu_call); - - case REMOTE_STREAM: - /* Since stream data is non-acked, async, we may continue to received - * stream packets after we closed down a stream. Just drop & ignore - * these. - */ - VIR_INFO("Ignoring unexpected stream data serial=%d proc=%d status=%d", - msg->hdr.serial, msg->hdr.proc, msg->hdr.status); - qemudClientMessageRelease(client, msg); - break; - - default: - remoteDispatchFormatError (&rerr, _("type (%d) != REMOTE_CALL"), - (int) msg->hdr.type); - goto error; - } - - return 0; - -error: - ret = remoteSerializeReplyError(client, &rerr, &msg->hdr); - - if (ret >= 0) - VIR_FREE(msg); - - return ret; -} - - -/* - * @server: the unlocked server object - * @client: the locked client object - * @msg: the complete incoming method call, with header already decoded - * - * This method is used to dispatch an message representing an - * incoming method call from a client. It decodes the payload - * to obtain method call arguments, invokves the method and - * then sends a reply packet with the return values - * - * Returns 0 if the reply was sent, or -1 upon fatal error - */ -static int -remoteDispatchClientCall (struct qemud_server *server, - struct qemud_client *client, - struct qemud_client_message *msg, - bool qemu_protocol) -{ - XDR xdr; - remote_error rerr; - dispatch_args args; - dispatch_ret ret; - const dispatch_data *data = NULL; - int rv = -1; - unsigned int len; - virConnectPtr conn = NULL; - - memset(&args, 0, sizeof args); - memset(&ret, 0, sizeof ret); - memset(&rerr, 0, sizeof rerr); - - if (msg->hdr.status != REMOTE_OK) { - remoteDispatchFormatError (&rerr, _("status (%d) != REMOTE_OK"), - (int) msg->hdr.status); - goto rpc_error; - } - - /* If client is marked as needing auth, don't allow any RPC ops, - * except for authentication ones - */ - if (client->auth) { - if (msg->hdr.proc != REMOTE_PROC_AUTH_LIST && - msg->hdr.proc != REMOTE_PROC_AUTH_SASL_INIT && - msg->hdr.proc != REMOTE_PROC_AUTH_SASL_START && - msg->hdr.proc != REMOTE_PROC_AUTH_SASL_STEP && - msg->hdr.proc != REMOTE_PROC_AUTH_POLKIT - ) { - /* Explicitly *NOT* calling remoteDispatchAuthError() because - we want back-compatability with libvirt clients which don't - support the VIR_ERR_AUTH_FAILED error code */ - remoteDispatchFormatError (&rerr, "%s", _("authentication required")); - goto rpc_error; - } - } - - if (qemu_protocol) - data = qemuGetDispatchData(msg->hdr.proc); - else - data = remoteGetDispatchData(msg->hdr.proc); - - if (!data) { - remoteDispatchFormatError (&rerr, _("unknown procedure: %d"), - msg->hdr.proc); - goto rpc_error; - } - - /* De-serialize payload with args from the wire message */ - xdrmem_create (&xdr, - msg->buffer + msg->bufferOffset, - msg->bufferLength - msg->bufferOffset, - XDR_DECODE); - if (!((data->args_filter)(&xdr, &args))) { - xdr_destroy (&xdr); - remoteDispatchFormatError (&rerr, "%s", _("parse args failed")); - goto rpc_error; - } - xdr_destroy (&xdr); - - /* Call function. */ - conn = client->conn; - virMutexUnlock(&client->lock); - - /* - * When the RPC handler is called: - * - * - Server object is unlocked - * - Client object is unlocked - * - * Without locking, it is safe to use: - * - * 'conn', 'rerr', 'args and 'ret' - */ - rv = (data->fn)(server, client, conn, &msg->hdr, &rerr, &args, &ret); - - virMutexLock(&server->lock); - virMutexLock(&client->lock); - virMutexUnlock(&server->lock); - - xdr_free (data->args_filter, (char*)&args); - - if (rv < 0) - goto rpc_error; - - /* Return header. We're re-using same message object, so - * only need to tweak type/status fields */ - /*msg->hdr.prog = msg->hdr.prog;*/ - /*msg->hdr.vers = msg->hdr.vers;*/ - /*msg->hdr.proc = msg->hdr.proc;*/ - msg->hdr.type = REMOTE_REPLY; - /*msg->hdr.serial = msg->hdr.serial;*/ - msg->hdr.status = REMOTE_OK; - - if (remoteEncodeClientMessageHeader(msg) < 0) { - xdr_free (data->ret_filter, (char*)&ret); - remoteDispatchFormatError(&rerr, "%s", _("failed to serialize reply header")); - goto xdr_hdr_error; - } - - - /* Now for the payload */ - xdrmem_create (&xdr, - msg->buffer, - msg->bufferLength, - XDR_ENCODE); - - if (xdr_setpos(&xdr, msg->bufferOffset) == 0) { - remoteDispatchFormatError(&rerr, "%s", _("failed to change XDR reply offset")); - goto xdr_error; - } - - /* If OK, serialise return structure, if error serialise error. */ - /* Serialise reply data */ - if (!((data->ret_filter) (&xdr, &ret))) { - remoteDispatchFormatError(&rerr, "%s", _("failed to serialize reply payload (probable message size limit)")); - goto xdr_error; - } - - /* Update the length word. */ - msg->bufferOffset += xdr_getpos (&xdr); - len = msg->bufferOffset; - if (xdr_setpos (&xdr, 0) == 0) { - remoteDispatchFormatError(&rerr, "%s", _("failed to change XDR reply offset")); - goto xdr_error; - } - - if (!xdr_u_int (&xdr, &len)) { - remoteDispatchFormatError(&rerr, "%s", _("failed to update reply length header")); - goto xdr_error; - } - - xdr_destroy (&xdr); - xdr_free (data->ret_filter, (char*)&ret); - - /* Reset ready for I/O */ - msg->bufferLength = len; - msg->bufferOffset = 0; - - /* Put reply on end of tx queue to send out */ - qemudClientMessageQueuePush(&client->tx, msg); - qemudUpdateClientEvent(client); - - return 0; - -xdr_error: - /* Bad stuff serializing reply. Try to send a little info - * back to client to assist in bug reporting/diagnosis */ - xdr_free (data->ret_filter, (char*)&ret); - xdr_destroy (&xdr); - /* fallthrough */ - -xdr_hdr_error: - VIR_WARN("Failed to serialize reply for program '%d' proc '%d' as XDR", - msg->hdr.prog, msg->hdr.proc); - /* fallthrough */ - -rpc_error: - /* Bad stuff (de-)serializing message, but we have an - * RPC error message we can send back to the client */ - rv = remoteSerializeReplyError(client, &rerr, &msg->hdr); - - if (rv >= 0) - VIR_FREE(msg); - - return rv; -} - - -int -remoteSendStreamData(struct qemud_client *client, - struct qemud_client_stream *stream, - const char *data, - unsigned int len) -{ - struct qemud_client_message *msg; - XDR xdr; - - VIR_DEBUG("client=%p stream=%p data=%p len=%d", client, stream, data, len); - - if (VIR_ALLOC(msg) < 0) { - return -1; - } - - /* Return header. We're re-using same message object, so - * only need to tweak type/status fields */ - msg->hdr.prog = REMOTE_PROGRAM; - msg->hdr.vers = REMOTE_PROTOCOL_VERSION; - msg->hdr.proc = stream->procedure; - msg->hdr.type = REMOTE_STREAM; - msg->hdr.serial = stream->serial; - /* - * NB - * data != NULL + len > 0 => REMOTE_CONTINUE (Sending back data) - * data != NULL + len == 0 => REMOTE_CONTINUE (Sending read EOF) - * data == NULL => REMOTE_OK (Sending finish handshake confirmation) - */ - msg->hdr.status = data ? REMOTE_CONTINUE : REMOTE_OK; - - if (remoteEncodeClientMessageHeader(msg) < 0) - goto fatal_error; - - if (data && len) { - if ((msg->bufferLength - msg->bufferOffset) < len) - goto fatal_error; - - /* Now for the payload */ - xdrmem_create (&xdr, - msg->buffer, - msg->bufferLength, - XDR_ENCODE); - - /* Skip over existing header already written */ - if (xdr_setpos(&xdr, msg->bufferOffset) == 0) - goto xdr_error; - - memcpy(msg->buffer + msg->bufferOffset, data, len); - msg->bufferOffset += len; - - /* Update the length word. */ - len = msg->bufferOffset; - if (xdr_setpos (&xdr, 0) == 0) - goto xdr_error; - - if (!xdr_u_int (&xdr, &len)) - goto xdr_error; - - xdr_destroy (&xdr); - - VIR_DEBUG("Total %d", msg->bufferOffset); - } - if (data) - msg->streamTX = 1; - - /* Reset ready for I/O */ - msg->bufferLength = msg->bufferOffset; - msg->bufferOffset = 0; - - /* Put reply on end of tx queue to send out */ - qemudClientMessageQueuePush(&client->tx, msg); - qemudUpdateClientEvent(client); - - return 0; - -xdr_error: - xdr_destroy (&xdr); -fatal_error: - VIR_FREE(msg); - VIR_WARN("Failed to serialize stream data for proc %d as XDR", - stream->procedure); - return -1; -} diff --git a/daemon/dispatch.h b/daemon/dispatch.h deleted file mode 100644 index f24f494..0000000 --- a/daemon/dispatch.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * dispatch.h: RPC message dispatching infrastructure - * - * Copyright (C) 2007, 2008, 2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Richard W.M. Jones <rjones@redhat.com> - * Author: Daniel P. Berrange <berrange@redhat.com> - */ - -#ifndef __LIBVIRTD_DISPATCH_H__ -# define __LIBVIRTD_DISPATCH_H__ - - -# include "libvirtd.h" - - -int -remoteDecodeClientMessageHeader (struct qemud_client_message *req); -int -remoteEncodeClientMessageHeader (struct qemud_client_message *req); - -int -remoteDispatchClientRequest (struct qemud_server *server, - struct qemud_client *client, - struct qemud_client_message *req); - - -void remoteDispatchFormatError (remote_error *rerr, - const char *fmt, ...) - ATTRIBUTE_FMT_PRINTF(2, 3); - -void remoteDispatchAuthError (remote_error *rerr); -void remoteDispatchGenericError (remote_error *rerr); -void remoteDispatchError(remote_error *rerr); - - -int -remoteSerializeReplyError(struct qemud_client *client, - remote_error *rerr, - remote_message_header *req); -int -remoteSerializeStreamError(struct qemud_client *client, - remote_error *rerr, - int proc, - int serial); - - -int -remoteSendStreamData(struct qemud_client *client, - struct qemud_client_stream *stream, - const char *data, - unsigned int len); - -#endif /* __LIBVIRTD_DISPATCH_H__ */ diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 5f291ec..214199b 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -23,31 +23,13 @@ #include <config.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/stat.h> #include <unistd.h> #include <fcntl.h> -#include <limits.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <sys/poll.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <netdb.h> -#include <stdlib.h> -#include <pwd.h> -#include <stdio.h> -#include <stdarg.h> -#include <syslog.h> -#include <string.h> -#include <errno.h> +#include <sys/wait.h> +#include <sys/stat.h> #include <getopt.h> -#include <fnmatch.h> +#include <stdlib.h> #include <grp.h> -#include <signal.h> -#include <netdb.h> -#include <locale.h> #include "libvirt_internal.h" #include "virterror_internal.h" @@ -56,20 +38,21 @@ #define VIR_FROM_THIS VIR_FROM_QEMU #include "libvirtd.h" -#include "dispatch.h" #include "util.h" #include "uuid.h" #include "remote_driver.h" #include "conf.h" -#include "event_poll.h" #include "memory.h" -#include "stream.h" +#include "conf.h" +#include "virnetserver.h" +#include "threads.h" +#include "remote.h" +#include "remote_driver.h" +//#include "stream.h" #include "hooks.h" +#include "uuid.h" #include "virtaudit.h" -#ifdef HAVE_AVAHI -# include "mdns.h" -#endif #ifdef WITH_DRIVER_MODULES # include "driver.h" @@ -106,100 +89,58 @@ # endif #endif - -#ifdef __sun -# include <ucred.h> -# include <priv.h> - -# ifndef PRIV_VIRT_MANAGE -# define PRIV_VIRT_MANAGE ((const char *)"virt_manage") -# endif - -# ifndef PRIV_XVM_CONTROL -# define PRIV_XVM_CONTROL ((const char *)"xvm_control") -# endif - -# define PU_RESETGROUPS 0x0001 /* Remove supplemental groups */ -# define PU_CLEARLIMITSET 0x0008 /* L=0 */ - -extern int __init_daemon_priv(int, uid_t, gid_t, ...); - -# define SYSTEM_UID 60 - -static gid_t unix_sock_gid = 60; /* Not used */ -static int unix_sock_rw_mask = 0666; -static int unix_sock_ro_mask = 0666; - -#else - -static gid_t unix_sock_gid = 0; /* Only root by default */ -static int unix_sock_rw_mask = 0700; /* Allow user only */ -static int unix_sock_ro_mask = 0777; /* Allow world */ - -#endif /* __sun */ - #include "configmake.h" -static int godaemon = 0; /* -d: Be a daemon */ -static int verbose = 0; /* -v: Verbose mode */ -static int timeout = -1; /* -t: Shutdown timeout */ -static int sigwrite = -1; /* Signal handler pipe */ -static int ipsock = 0; /* -l Listen for TCP/IP */ - -/* Defaults for configuration file elements */ -static int listen_tls = 1; -static int listen_tcp = 0; -static char *listen_addr = (char *) LIBVIRTD_LISTEN_ADDR; -static char *tls_port = (char *) LIBVIRTD_TLS_PORT; -static char *tcp_port = (char *) LIBVIRTD_TCP_PORT; +virNetSASLContextPtr saslCtxt = NULL; +virNetServerProgramPtr remoteProgram = NULL; +virNetServerProgramPtr qemuProgram = NULL; -static char *unix_sock_dir = NULL; +struct daemonConfig { + char *host_uuid; -#if HAVE_POLKIT -static int auth_unix_rw = REMOTE_AUTH_POLKIT; -static int auth_unix_ro = REMOTE_AUTH_POLKIT; -#else -static int auth_unix_rw = REMOTE_AUTH_NONE; -static int auth_unix_ro = REMOTE_AUTH_NONE; -#endif /* HAVE_POLKIT */ -#if HAVE_SASL -static int auth_tcp = REMOTE_AUTH_SASL; -#else -static int auth_tcp = REMOTE_AUTH_NONE; -#endif -static int auth_tls = REMOTE_AUTH_NONE; + int listen_tls; + int listen_tcp; + char *listen_addr; + char *tls_port; + char *tcp_port; -static int mdns_adv = 1; -static char *mdns_name = NULL; + char *unix_sock_ro_perms; + char *unix_sock_rw_perms; + char *unix_sock_group; + char *unix_sock_dir; -static int tls_no_verify_certificate = 0; -static char **tls_allowed_dn_list = NULL; + int auth_unix_rw; + int auth_unix_ro; + int auth_tcp; + int auth_tls; -static char *key_file = (char *) LIBVIRT_SERVERKEY; -static char *cert_file = (char *) LIBVIRT_SERVERCERT; -static char *ca_file = (char *) LIBVIRT_CACERT; -static char *crl_file = (char *) ""; + int mdns_adv; + char *mdns_name; -static gnutls_certificate_credentials_t x509_cred; -static gnutls_dh_params_t dh_params; + int tls_no_verify_certificate; + char **tls_allowed_dn_list; + char **sasl_allowed_username_list; -static int min_workers = 5; -static int max_workers = 20; -static int max_clients = 20; + char *key_file; + char *cert_file; + char *ca_file; + char *crl_file; -/* Total number of 'in-process' RPC calls allowed across all clients */ -static int max_requests = 20; -/* Total number of 'in-process' RPC calls allowed by a single client*/ -static int max_client_requests = 5; + int min_workers; + int max_workers; + int max_clients; -static int audit_level = 1; -static int audit_logging = 0; + int max_requests; + int max_client_requests; -#define DH_BITS 1024 + int log_level; + char *log_filters; + char *log_outputs; + int log_buffer_size; -static sig_atomic_t sig_errors = 0; -static int sig_lasterrno = 0; -static const char *argv0; + int audit_level; + int audit_logging; +}; enum { VIR_DAEMON_ERR_NONE = 0, @@ -229,211 +170,8 @@ VIR_ENUM_IMPL(virDaemonErr, VIR_DAEMON_ERR_LAST, "Unable to look for hook scripts", "Unable to initialize audit system") -static void sig_handler(int sig, siginfo_t * siginfo, - void* context ATTRIBUTE_UNUSED) { - int origerrno; - int r; - - /* set the sig num in the struct */ - siginfo->si_signo = sig; - - origerrno = errno; - r = safewrite(sigwrite, siginfo, sizeof(*siginfo)); - if (r == -1) { - sig_errors++; - sig_lasterrno = errno; - } - errno = origerrno; -} - -static void sig_fatal(int sig, siginfo_t * siginfo ATTRIBUTE_UNUSED, - void* context ATTRIBUTE_UNUSED) { - struct sigaction sig_action; - int origerrno; - - origerrno = errno; - virLogEmergencyDumpAll(sig); - - /* - * If the signal is fatal, avoid looping over this handler - * by desactivating it - */ - if (sig != SIGUSR2) { - sig_action.sa_flags = SA_SIGINFO; - sig_action.sa_handler = SIG_IGN; - sigaction(sig, &sig_action, NULL); - } - errno = origerrno; -} - -static void qemudDispatchClientEvent(int watch, int fd, int events, void *opaque); -static void qemudDispatchServerEvent(int watch, int fd, int events, void *opaque); -static int qemudStartWorker(struct qemud_server *server, struct qemud_worker *worker); - -void -qemudClientMessageQueuePush(struct qemud_client_message **queue, - struct qemud_client_message *msg) -{ - struct qemud_client_message *tmp = *queue; - - if (tmp) { - while (tmp->next) - tmp = tmp->next; - tmp->next = msg; - } else { - *queue = msg; - } -} - -struct qemud_client_message * -qemudClientMessageQueueServe(struct qemud_client_message **queue) -{ - struct qemud_client_message *tmp = *queue; - - if (tmp) { - *queue = tmp->next; - tmp->next = NULL; - } - - return tmp; -} - -static int -remoteCheckCertFile(const char *type, const char *file) -{ - struct stat sb; - if (stat(file, &sb) < 0) { - char ebuf[1024]; - VIR_ERROR(_("Cannot access %s '%s': %s"), - type, file, virStrerror(errno, ebuf, sizeof ebuf)); - return -1; - } - return 0; -} - -static int -remoteInitializeGnuTLS (void) +static int daemonForkIntoBackground(const char *argv0) { - int err; - - err = gnutls_certificate_allocate_credentials (&x509_cred); - if (err) { - VIR_ERROR(_("gnutls_certificate_allocate_credentials: %s"), - gnutls_strerror (err)); - return -1; - } - - if (ca_file && ca_file[0] != '\0') { - if (remoteCheckCertFile("CA certificate", ca_file) < 0) - return -1; - - VIR_DEBUG("loading CA cert from %s", ca_file); - err = gnutls_certificate_set_x509_trust_file (x509_cred, ca_file, - GNUTLS_X509_FMT_PEM); - if (err < 0) { - VIR_ERROR(_("gnutls_certificate_set_x509_trust_file: %s"), - gnutls_strerror (err)); - return -1; - } - } - - if (crl_file && crl_file[0] != '\0') { - if (remoteCheckCertFile("CA revocation list", crl_file) < 0) - return -1; - - VIR_DEBUG("loading CRL from %s", crl_file); - err = gnutls_certificate_set_x509_crl_file (x509_cred, crl_file, - GNUTLS_X509_FMT_PEM); - if (err < 0) { - VIR_ERROR(_("gnutls_certificate_set_x509_crl_file: %s"), - gnutls_strerror (err)); - return -1; - } - } - - if (cert_file && cert_file[0] != '\0' && key_file && key_file[0] != '\0') { - if (remoteCheckCertFile("server certificate", cert_file) < 0) - return -1; - if (remoteCheckCertFile("server key", key_file) < 0) - return -1; - VIR_DEBUG("loading cert and key from %s and %s", cert_file, key_file); - err = - gnutls_certificate_set_x509_key_file (x509_cred, - cert_file, key_file, - GNUTLS_X509_FMT_PEM); - if (err < 0) { - VIR_ERROR(_("gnutls_certificate_set_x509_key_file: %s"), - gnutls_strerror (err)); - return -1; - } - } - - /* Generate Diffie Hellman parameters - for use with DHE - * kx algorithms. These should be discarded and regenerated - * once a day, once a week or once a month. Depending on the - * security requirements. - */ - err = gnutls_dh_params_init (&dh_params); - if (err < 0) { - VIR_ERROR(_("gnutls_dh_params_init: %s"), gnutls_strerror (err)); - return -1; - } - err = gnutls_dh_params_generate2 (dh_params, DH_BITS); - if (err < 0) { - VIR_ERROR(_("gnutls_dh_params_generate2: %s"), gnutls_strerror (err)); - return -1; - } - - gnutls_certificate_set_dh_params (x509_cred, dh_params); - - return 0; -} - -static void -qemudDispatchSignalEvent(int watch ATTRIBUTE_UNUSED, - int fd ATTRIBUTE_UNUSED, - int events ATTRIBUTE_UNUSED, - void *opaque) { - struct qemud_server *server = (struct qemud_server *)opaque; - siginfo_t siginfo; - - virMutexLock(&server->lock); - - if (saferead(server->sigread, &siginfo, sizeof(siginfo)) != sizeof(siginfo)) { - char ebuf[1024]; - VIR_ERROR(_("Failed to read from signal pipe: %s"), - virStrerror(errno, ebuf, sizeof ebuf)); - virMutexUnlock(&server->lock); - return; - } - - switch (siginfo.si_signo) { - case SIGHUP: - VIR_INFO("Reloading configuration on SIGHUP"); - virHookCall(VIR_HOOK_DRIVER_DAEMON, "-", - VIR_HOOK_DAEMON_OP_RELOAD, SIGHUP, "SIGHUP", NULL); - if (virStateReload() < 0) - VIR_WARN("Error while reloading drivers"); - - break; - - case SIGINT: - case SIGQUIT: - case SIGTERM: - VIR_WARN("Shutting down on signal %d", siginfo.si_signo); - server->quitEventThread = 1; - break; - - default: - VIR_INFO("Received unexpected signal %d", siginfo.si_signo); - break; - } - - virMutexUnlock(&server->lock); -} - - -static int daemonForkIntoBackground(void) { int statuspipe[2]; if (pipe(statuspipe) < 0) return -1; @@ -518,7 +256,8 @@ static int daemonForkIntoBackground(void) { } } -static int qemudWritePidFile(const char *pidFile) { +static int daemonWritePidFile(const char *pidFile, const char *argv0) +{ int fd; FILE *fh; char ebuf[1024]; @@ -555,289 +294,77 @@ static int qemudWritePidFile(const char *pidFile) { return 0; } -static int qemudListenUnix(struct qemud_server *server, - char *path, int readonly, int auth) { - struct qemud_socket *sock; - mode_t oldmask; - char ebuf[1024]; - - if (VIR_ALLOC(sock) < 0) { - VIR_ERROR(_("Failed to allocate memory for struct qemud_socket")); - return -1; - } - - sock->readonly = readonly; - sock->type = QEMUD_SOCK_TYPE_UNIX; - sock->auth = auth; - sock->path = path; - sock->addr.len = sizeof(sock->addr.data.un); - if (!(sock->addrstr = strdup(path))) { - VIR_ERROR(_("Failed to copy socket address: %s"), - virStrerror(errno, ebuf, sizeof ebuf)); - goto cleanup; - } - - if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { - VIR_ERROR(_("Failed to create socket: %s"), - virStrerror(errno, ebuf, sizeof ebuf)); - goto cleanup; - } - - if (virSetCloseExec(sock->fd) < 0 || - virSetNonBlock(sock->fd) < 0) - goto cleanup; - - sock->addr.data.un.sun_family = AF_UNIX; - if (virStrcpyStatic(sock->addr.data.un.sun_path, path) == NULL) { - VIR_ERROR(_("Path %s too long for unix socket"), path); - goto cleanup; - } - if (sock->addr.data.un.sun_path[0] == '@') - sock->addr.data.un.sun_path[0] = '\0'; - - oldmask = umask(readonly ? ~unix_sock_ro_mask : ~unix_sock_rw_mask); - if (bind(sock->fd, &sock->addr.data.sa, sock->addr.len) < 0) { - VIR_ERROR(_("Failed to bind socket to '%s': %s"), - path, virStrerror(errno, ebuf, sizeof ebuf)); - umask(oldmask); - goto cleanup; - } - umask(oldmask); - - /* chown() doesn't work for abstract sockets but we use them only - * if libvirtd runs unprivileged - */ - if (server->privileged && chown(path, -1, unix_sock_gid)) { - VIR_ERROR(_("Failed to change group ID of '%s' to %d: %s"), - path, unix_sock_gid, - virStrerror(errno, ebuf, sizeof ebuf)); - goto cleanup; - } - - if (listen(sock->fd, 30) < 0) { - VIR_ERROR(_("Failed to listen for connections on '%s': %s"), - path, virStrerror(errno, ebuf, sizeof ebuf)); - goto cleanup; - } - - sock->next = server->sockets; - server->sockets = sock; - server->nsockets++; - - return 0; - - cleanup: - VIR_FORCE_CLOSE(sock->fd); - VIR_FREE(sock); - return -1; -} - -/* See: http://people.redhat.com/drepper/userapi-ipv6.html */ -static int -remoteMakeSockets (int *fds, int max_fds, int *nfds_r, const char *node, const char *service) -{ - struct addrinfo *ai; - struct addrinfo hints; - memset (&hints, 0, sizeof hints); - hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; - hints.ai_socktype = SOCK_STREAM; - - int e = getaddrinfo (node, service, &hints, &ai); - if (e != 0) { - VIR_ERROR(_("getaddrinfo: %s"), gai_strerror (e)); - return -1; - } - - struct addrinfo *runp = ai; - while (runp && *nfds_r < max_fds) { - char ebuf[1024]; - fds[*nfds_r] = socket (runp->ai_family, runp->ai_socktype, - runp->ai_protocol); - if (fds[*nfds_r] == -1) { - VIR_ERROR(_("socket: %s"), virStrerror (errno, ebuf, sizeof ebuf)); - return -1; - } - - int opt = 1; - setsockopt (fds[*nfds_r], SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt); - -#ifdef IPV6_V6ONLY - if (runp->ai_family == PF_INET6) { - int on = 1; - /* - * Normally on Linux an INET6 socket will bind to the INET4 - * address too. If getaddrinfo returns results with INET4 - * first though, this will result in INET6 binding failing. - * We can trivially cope with multiple server sockets, so - * we force it to only listen on IPv6 - */ - setsockopt(fds[*nfds_r], IPPROTO_IPV6,IPV6_V6ONLY, - (void*)&on, sizeof on); - } -#endif - - if (bind (fds[*nfds_r], runp->ai_addr, runp->ai_addrlen) == -1) { - if (errno != EADDRINUSE) { - VIR_ERROR(_("bind: %s"), virStrerror (errno, ebuf, sizeof ebuf)); - return -1; - } - VIR_FORCE_CLOSE(fds[*nfds_r]); - } else { - ++*nfds_r; - } - runp = runp->ai_next; - } - - freeaddrinfo (ai); - return 0; -} -/* Listen on the named/numbered TCP port. On a machine with IPv4 and - * IPv6 interfaces this may generate several sockets. - */ static int -remoteListenTCP (struct qemud_server *server, - const char *addr, - const char *port, - int type, - int auth) +daemonPidFilePath(bool privileged, + char **pidfile) { - int fds[2]; - int nfds = 0; - int i; - struct qemud_socket *sock; - - if (remoteMakeSockets (fds, 2, &nfds, addr, port) == -1) - return -1; - - for (i = 0; i < nfds; ++i) { - char ebuf[1024]; - - if (VIR_ALLOC(sock) < 0) { - VIR_ERROR(_("remoteListenTCP: calloc: %s"), - virStrerror (errno, ebuf, sizeof ebuf)); - goto cleanup; - } - - sock->addr.len = sizeof(sock->addr.data.stor); - sock->readonly = 0; - sock->next = server->sockets; - server->sockets = sock; - server->nsockets++; - - sock->fd = fds[i]; - sock->type = type; - sock->auth = auth; - - if (getsockname(sock->fd, &sock->addr.data.sa, &sock->addr.len) < 0) - goto cleanup; + if (privileged) { + if (!(*pidfile = strdup(LOCALSTATEDIR "/run/libvirtd.pid"))) + goto no_memory; + } else { + char *userdir = NULL; - if (!(sock->addrstr = virSocketFormatAddrFull(&sock->addr, true, ";"))) - goto cleanup; + if (!(userdir = virGetUserDirectory(geteuid()))) + goto error; - if (virSetCloseExec(sock->fd) < 0 || - virSetNonBlock(sock->fd) < 0) - goto cleanup; + if (virAsprintf(pidfile, "%s/.libvirt/libvirtd.pid", userdir) < 0) + goto no_memory; - if (listen (sock->fd, 30) < 0) { - VIR_ERROR(_("remoteListenTCP: listen: %s"), - virStrerror (errno, ebuf, sizeof ebuf)); - goto cleanup; - } + VIR_FREE(userdir); } return 0; -cleanup: - for (i = 0; i < nfds; ++i) - VIR_FORCE_CLOSE(fds[i]); +no_memory: + virReportOOMError(); +error: return -1; } -static int qemudInitPaths(struct qemud_server *server, - char **sockname, - char **roSockname) +static int +daemonUnixSocketPaths(struct daemonConfig *config, + bool privileged, + char **sockfile, + char **rosockfile) { - char *base_dir_prefix = NULL; - char *sock_dir_prefix = NULL; - int ret = -1; - - /* The base_dir_prefix is the base under which all libvirtd - * files live */ - if (server->privileged) { - if (!(base_dir_prefix = strdup (LOCALSTATEDIR))) + if (config->unix_sock_dir) { + if (virAsprintf(sockfile, "%s/libvirt-sock", config->unix_sock_dir) < 0) goto no_memory; - } else { - uid_t uid = geteuid(); - if (!(base_dir_prefix = virGetUserDirectory(uid))) - goto cleanup; - } - - /* The unix_sock_dir is the location under which all - * unix domain sockets live */ - if (unix_sock_dir) { - if (!(sock_dir_prefix = strdup(unix_sock_dir))) + if (privileged && + virAsprintf(rosockfile, "%s/libvirt-sock-ro", config->unix_sock_dir) < 0) goto no_memory; - - /* Change the group ownership of /var/run/libvirt to unix_sock_gid */ - if (server->privileged) { - if (chown(unix_sock_dir, -1, unix_sock_gid) < 0) - VIR_ERROR(_("Failed to change group ownership of %s"), - unix_sock_dir); - } } else { - if (server->privileged) { - if (virAsprintf(&sock_dir_prefix, "%s/run/libvirt", - base_dir_prefix) < 0) + if (privileged) { + if (!(*sockfile = strdup(LOCALSTATEDIR "/run/libvirt/libvirt-sock"))) goto no_memory; - } else { - if (virAsprintf(&sock_dir_prefix, "%s/.libvirt", - base_dir_prefix) < 0) + if (!(*rosockfile = strdup(LOCALSTATEDIR "/run/libvirt/libvirt-sock-ro"))) goto no_memory; - } - } + } else { + char *userdir = NULL; - if (server->privileged) { - if (virAsprintf(sockname, "%s/libvirt-sock", - sock_dir_prefix) < 0) - goto no_memory; - if (virAsprintf(roSockname, "%s/libvirt-sock-ro", - sock_dir_prefix) < 0) - goto no_memory; - unlink(*sockname); - unlink(*roSockname); - } else { - if (virAsprintf(sockname, "@%s/libvirt-sock", - sock_dir_prefix) < 0) - goto no_memory; - /* There is no RO socket in unprivileged mode, - * since the user always has full RW access - * to their private instance */ - } + if (!(userdir = virGetUserDirectory(geteuid()))) + goto error; - if (server->privileged) { - if (virAsprintf(&server->logDir, "%s/log/libvirt", - base_dir_prefix) < 0) - goto no_memory; - } else { - if (virAsprintf(&server->logDir, "%s/.libvirt/log", - base_dir_prefix) < 0) - goto no_memory; - } + if (virAsprintf(sockfile, "@%s/.libvirt/libvirt-sock", userdir) < 0) { + VIR_FREE(userdir); + goto no_memory; + } - ret = 0; + VIR_FREE(userdir); + } + } + return 0; no_memory: - if (ret != 0) - virReportOOMError(); - - cleanup: - VIR_FREE(base_dir_prefix); - VIR_FREE(sock_dir_prefix); - return ret; + virReportOOMError(); +error: + return -1; } -static void virshErrorHandler(void *opaque ATTRIBUTE_UNUSED, virErrorPtr err ATTRIBUTE_UNUSED) + +static void daemonErrorHandler(void *opaque ATTRIBUTE_UNUSED, + virErrorPtr err ATTRIBUTE_UNUSED) { /* Don't do anything, since logging infrastructure already * took care of reporting the error */ @@ -866,38 +393,8 @@ static int daemonErrorLogFilter(virErrorPtr err, int priority) return priority; } - -static struct qemud_server *qemudInitialize(void) { - struct qemud_server *server; - - if (VIR_ALLOC(server) < 0) { - VIR_ERROR(_("Failed to allocate struct qemud_server")); - return NULL; - } - - server->privileged = geteuid() == 0 ? 1 : 0; - server->sigread = server->sigwrite = -1; - - if (virMutexInit(&server->lock) < 0) { - VIR_ERROR(_("cannot initialize mutex")); - VIR_FREE(server); - return NULL; - } - if (virCondInit(&server->job) < 0) { - VIR_ERROR(_("cannot initialize condition variable")); - virMutexDestroy(&server->lock); - VIR_FREE(server); - return NULL; - } - - if (virEventRegisterDefaultImpl() < 0) { - virMutexDestroy(&server->lock); - if (virCondDestroy(&server->job) < 0) - {} - VIR_FREE(server); - return NULL; - } - +static void daemonInitialize(void) +{ /* * Note that the order is important: the first ones have a higher * priority when calling virStateInitialize. We must register @@ -951,1589 +448,169 @@ static struct qemud_server *qemudInitialize(void) { umlRegister(); # endif #endif - - return server; } -static int qemudNetworkInit(struct qemud_server *server) { - char *sockname = NULL; - char *roSockname = NULL; -#if HAVE_SASL - int err; -#endif /* HAVE_SASL */ - if (qemudInitPaths(server, &sockname, &roSockname) < 0) - goto cleanup; +static int daemonSetupNetworking(virNetServerPtr srv, + struct daemonConfig *config, + const char *sock_path, + const char *sock_path_ro, + bool ipsock, + bool privileged) +{ + virNetServerServicePtr svc = NULL; + virNetServerServicePtr svcRO = NULL; + virNetServerServicePtr svcTCP = NULL; + virNetServerServicePtr svcTLS = NULL; + gid_t unix_sock_gid = 0; + int unix_sock_ro_mask = 0; + int unix_sock_rw_mask = 0; + + if (config->unix_sock_group) { + if (!virNetServerIsPrivileged(srv)) { + VIR_WARN("Cannot set group when not running as root"); + return -1; + } + if (virGetGroupID(config->unix_sock_group, &unix_sock_gid) < 0) + return -1; + } - if (qemudListenUnix(server, sockname, 0, auth_unix_rw) < 0) - goto cleanup; - sockname = NULL; + if (virStrToLong_i(config->unix_sock_ro_perms, NULL, 8, &unix_sock_ro_mask) != 0) { + VIR_ERROR(_("Failed to parse mode '%s'"), config->unix_sock_ro_perms); + goto error; + } - if (roSockname != NULL && qemudListenUnix(server, roSockname, 1, auth_unix_ro) < 0) - goto cleanup; - roSockname = NULL; - -#if HAVE_SASL - if (auth_unix_rw == REMOTE_AUTH_SASL || - auth_unix_ro == REMOTE_AUTH_SASL || - auth_tcp == REMOTE_AUTH_SASL || - auth_tls == REMOTE_AUTH_SASL) { - if ((err = sasl_server_init(NULL, "libvirt")) != SASL_OK) { - VIR_ERROR(_("Failed to initialize SASL authentication %s"), - sasl_errstring(err, NULL, NULL)); - goto cleanup; - } + if (virStrToLong_i(config->unix_sock_rw_perms, NULL, 8, &unix_sock_rw_mask) != 0) { + VIR_ERROR(_("Failed to parse mode '%s'"), config->unix_sock_rw_perms); + goto error; } -#endif - -#if HAVE_POLKIT0 - if (auth_unix_rw == REMOTE_AUTH_POLKIT || - auth_unix_ro == REMOTE_AUTH_POLKIT) { - DBusError derr; - dbus_connection_set_change_sigpipe(FALSE); - dbus_threads_init_default(); + if (!(svc = virNetServerServiceNewUNIX(sock_path, + unix_sock_rw_mask, + unix_sock_gid, + config->auth_unix_rw, + false, + NULL))) + goto error; + if (sock_path_ro && + !(svcRO = virNetServerServiceNewUNIX(sock_path_ro, + unix_sock_ro_mask, + unix_sock_gid, + config->auth_unix_ro, + true, + NULL))) + goto error; - dbus_error_init(&derr); - server->sysbus = dbus_bus_get(DBUS_BUS_SYSTEM, &derr); - if (!(server->sysbus)) { - VIR_ERROR(_("Failed to connect to system bus for PolicyKit auth: %s"), - derr.message); - dbus_error_free(&derr); - goto cleanup; - } - dbus_connection_set_exit_on_disconnect(server->sysbus, FALSE); - } -#endif + if (virNetServerAddService(srv, svc, NULL) < 0) + goto error; + if (svcRO && + virNetServerAddService(srv, svcRO, NULL) < 0) + goto error; if (ipsock) { - if (listen_tcp && remoteListenTCP (server, listen_addr, tcp_port, QEMUD_SOCK_TYPE_TCP, auth_tcp) < 0) - goto cleanup; - - if (listen_tls) { - if (remoteInitializeGnuTLS () < 0) - goto cleanup; - - if (remoteListenTCP (server, listen_addr, tls_port, QEMUD_SOCK_TYPE_TLS, auth_tls) < 0) - goto cleanup; - } - } - -#ifdef HAVE_AVAHI - if (server->privileged && mdns_adv) { - struct libvirtd_mdns_group *group; - struct qemud_socket *sock; - int port = 0; - int ret; - - server->mdns = libvirtd_mdns_new(); - - if (!mdns_name) { - char *groupname, *localhost, *tmp; - - localhost = virGetHostname(NULL); - if (localhost == NULL) - /* we couldn't resolve the hostname; assume that we are - * running in disconnected operation, and report a less - * useful Avahi string - */ - ret = virAsprintf(&groupname, "Virtualization Host"); - else { - /* Extract the host part of the potentially FQDN */ - if ((tmp = strchr(localhost, '.'))) - *tmp = '\0'; - ret = virAsprintf(&groupname, "Virtualization Host %s", - localhost); - } - VIR_FREE(localhost); - if (ret < 0) { - virReportOOMError(); - goto cleanup; - } - group = libvirtd_mdns_add_group(server->mdns, groupname); - VIR_FREE(groupname); - } else { - group = libvirtd_mdns_add_group(server->mdns, mdns_name); - } - - /* - * See if there's a TLS enabled port we can advertise. Cowardly - * don't bother to advertise TCP since we don't want people using - * them for real world apps - */ - sock = server->sockets; - while (sock) { - if (virSocketGetPort(&sock->addr) != -1 && - sock->type == QEMUD_SOCK_TYPE_TLS) { - port = virSocketGetPort(&sock->addr); - break; - } - sock = sock->next; - } - - /* - * Add the primary entry - we choose SSH because its most likely to always - * be available - */ - libvirtd_mdns_add_entry(group, "_libvirt._tcp", port); - libvirtd_mdns_start(server->mdns); - } -#endif - - return 0; - - cleanup: - VIR_FREE(sockname); - VIR_FREE(roSockname); - return -1; -} - -static int qemudNetworkEnable(struct qemud_server *server) { - struct qemud_socket *sock; - - sock = server->sockets; - while (sock) { - if ((sock->watch = virEventAddHandle(sock->fd, - VIR_EVENT_HANDLE_READABLE | - VIR_EVENT_HANDLE_ERROR | - VIR_EVENT_HANDLE_HANGUP, - qemudDispatchServerEvent, - server, NULL)) < 0) { - VIR_ERROR(_("Failed to add server event callback")); - return -1; - } - - sock = sock->next; - } - return 0; -} - - -static gnutls_session_t -remoteInitializeTLSSession (void) -{ - gnutls_session_t session; - int err; - - err = gnutls_init (&session, GNUTLS_SERVER); - if (err != 0) goto failed; - - /* avoid calling all the priority functions, since the defaults - * are adequate. - */ - err = gnutls_set_default_priority (session); - if (err != 0) goto failed; - - err = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred); - if (err != 0) goto failed; - - /* request client certificate if any. - */ - gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST); - - gnutls_dh_set_prime_bits (session, DH_BITS); - - return session; - -failed: - VIR_ERROR(_("remoteInitializeTLSSession: %s"), - gnutls_strerror (err)); - return NULL; -} - -/* Check DN is on tls_allowed_dn_list. */ -static int -remoteCheckDN (const char *dname) -{ - char **wildcards; - - /* If the list is not set, allow any DN. */ - wildcards = tls_allowed_dn_list; - if (!wildcards) - return 1; - - while (*wildcards) { - if (fnmatch (*wildcards, dname, 0) == 0) - return 1; - wildcards++; - } - - /* Print the client's DN. */ - VIR_DEBUG("remoteCheckDN: failed: client DN is %s", dname); - - return 0; /* Not found. */ -} - -static int -remoteCheckCertificate(struct qemud_client *client) -{ - int ret; - unsigned int status; - const gnutls_datum_t *certs; - unsigned int nCerts, i; - time_t now; - char name[256]; - size_t namesize = sizeof name; - - memset(name, 0, namesize); - - if ((ret = gnutls_certificate_verify_peers2 (client->tlssession, &status)) < 0){ - VIR_ERROR(_("Failed to verify certificate peers: %s"), - gnutls_strerror (ret)); - goto authdeny; - } - - if (status != 0) { - if (status & GNUTLS_CERT_INVALID) - VIR_ERROR(_("The client certificate is not trusted.")); - - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) - VIR_ERROR(_("The client certificate has unknown issuer.")); - - if (status & GNUTLS_CERT_REVOKED) - VIR_ERROR(_("The client certificate has been revoked.")); - -#ifndef GNUTLS_1_0_COMPAT - if (status & GNUTLS_CERT_INSECURE_ALGORITHM) - VIR_ERROR(_("The client certificate uses an insecure algorithm.")); -#endif - - goto authdeny; - } - - if (gnutls_certificate_type_get(client->tlssession) != GNUTLS_CRT_X509) { - VIR_ERROR(_("Only x509 certificates are supported")); - goto authdeny; - } - - if (!(certs = gnutls_certificate_get_peers(client->tlssession, &nCerts))) { - VIR_ERROR(_("The certificate has no peers")); - goto authdeny; - } - - now = time (NULL); - - for (i = 0; i < nCerts; i++) { - gnutls_x509_crt_t cert; + if (config->listen_tcp) { + if (!(svcTCP = virNetServerServiceNewTCP(config->listen_addr, + config->tcp_port, + config->auth_tcp, + false, + NULL))) + goto error; - if (gnutls_x509_crt_init (&cert) < 0) { - VIR_ERROR(_("Unable to initialize certificate")); - goto authfail; + if (virNetServerAddService(srv, svcTCP, + config->mdns_adv ? "_libvirt._tcp" : NULL) < 0) + goto error; } - if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { - VIR_ERROR(_("Unable to load certificate")); - gnutls_x509_crt_deinit (cert); - goto authfail; - } + if (config->listen_tls) { + virNetTLSContextPtr ctxt = NULL; - if (i == 0) { - ret = gnutls_x509_crt_get_dn (cert, name, &namesize); - if (ret != 0) { - VIR_ERROR(_("Failed to get certificate distinguished name: %s"), - gnutls_strerror(ret)); - gnutls_x509_crt_deinit (cert); - goto authfail; + if (config->ca_file || + config->cert_file || + config->key_file) { + if (!(ctxt = virNetTLSContextNewServer(config->ca_file, + config->crl_file, + config->cert_file, + config->key_file, + (const char *const*)config->tls_allowed_dn_list, + config->tls_no_verify_certificate ? false : true))) + goto error; + } else { + if (!(ctxt = virNetTLSContextNewServerPath(NULL, + !privileged, + (const char *const*)config->tls_allowed_dn_list, + config->tls_no_verify_certificate ? false : true))) + goto error; } - if (!remoteCheckDN (name)) { - /* This is the most common error: make it informative. */ - VIR_ERROR(_("Client's Distinguished Name is not on the list " - "of allowed clients (tls_allowed_dn_list). Use " - "'certtool -i --infile clientcert.pem' to view the" - "Distinguished Name field in the client certificate," - "or run this daemon with --verbose option.")); - gnutls_x509_crt_deinit (cert); - goto authdeny; + if (!(svcTLS = + virNetServerServiceNewTCP(config->listen_addr, + config->tls_port, + config->auth_tls, + false, + ctxt))) { + virNetTLSContextFree(ctxt); + goto error; } - } - - if (gnutls_x509_crt_get_expiration_time (cert) < now) { - VIR_ERROR(_("The client certificate has expired")); - gnutls_x509_crt_deinit (cert); - goto authdeny; - } + if (virNetServerAddService(srv, svcTLS, + config->mdns_adv && + !config->listen_tcp ? "_libvirt._tcp" : NULL) < 0) + goto error; - if (gnutls_x509_crt_get_activation_time (cert) > now) { - VIR_ERROR(_("The client certificate is not yet active")); - gnutls_x509_crt_deinit (cert); - goto authdeny; + virNetTLSContextFree(ctxt); } } - PROBE(CLIENT_TLS_ALLOW, "fd=%d, name=%s", client->fd, (char *)name); - return 0; - -authdeny: - PROBE(CLIENT_TLS_DENY, "fd=%d, name=%s", client->fd, (char *)name); - return -1; - -authfail: - PROBE(CLIENT_TLS_FAIL, "fd=%d", client->fd); - return -1; -} - -/* Check the client's access. */ -static int -remoteCheckAccess (struct qemud_client *client) -{ - struct qemud_client_message *confirm; - - /* Verify client certificate. */ - if (remoteCheckCertificate (client) == -1) { - VIR_ERROR(_("remoteCheckCertificate: " - "failed to verify client's certificate")); - if (!tls_no_verify_certificate) return -1; - else VIR_INFO("remoteCheckCertificate: tls_no_verify_certificate " - "is set so the bad certificate is ignored"); - } - - if (client->tx) { - VIR_INFO("%s", - _("client had unexpected data pending tx after access check")); - return -1; - } - - if (VIR_ALLOC(confirm) < 0) - return -1; - - /* Checks have succeeded. Write a '\1' byte back to the client to - * indicate this (otherwise the socket is abruptly closed). - * (NB. The '\1' byte is sent in an encrypted record). - */ - confirm->async = 1; - confirm->bufferLength = 1; - confirm->bufferOffset = 0; - confirm->buffer[0] = '\1'; - - client->tx = confirm; - return 0; -} - -#if HAVE_POLKIT -int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid) { -# ifdef SO_PEERCRED - struct ucred cr; - unsigned int cr_len = sizeof (cr); - - if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) < 0) { - char ebuf[1024]; - VIR_ERROR(_("Failed to verify client credentials: %s"), - virStrerror(errno, ebuf, sizeof ebuf)); - return -1; - } - - *pid = cr.pid; - *uid = cr.uid; -# else - /* XXX Many more OS support UNIX socket credentials we could port to. See dbus ....*/ -# error "UNIX socket credentials not supported/implemented on this platform yet..." -# endif - return 0; -} -#endif - - -static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket *sock) { - int fd; - virSocketAddr addr; - char *addrstr = NULL; - struct qemud_client *client = NULL; - int no_slow_start = 1; - int i; - - addr.len = sizeof(addr.data.stor); - if ((fd = accept(sock->fd, &addr.data.sa, &addr.len)) < 0) { - char ebuf[1024]; - if (errno == EAGAIN) - return 0; - VIR_ERROR(_("Failed to accept connection: %s"), - virStrerror(errno, ebuf, sizeof ebuf)); - return -1; - } - if (!(addrstr = virSocketFormatAddrFull(&addr, true, ";"))) { - VIR_ERROR(_("Failed to format addresss: out of memory")); - goto error; - } - - PROBE(CLIENT_CONNECT, "fd=%d, readonly=%d localAddr=%s remoteAddr=%s", - fd, sock->readonly, sock->addrstr, addrstr); - - if (server->nclients >= max_clients) { - VIR_ERROR(_("Too many active clients (%d), dropping connection from %s"), - max_clients, addrstr); - goto error; - } - - if (VIR_RESIZE_N(server->clients, server->nclients_max, - server->nclients, 1) < 0) { - VIR_ERROR(_("Out of memory allocating clients")); - goto error; - } - -#ifdef __sun - { - ucred_t *ucred = NULL; - const priv_set_t *privs; - - if (getpeerucred (fd, &ucred) == -1 || - (privs = ucred_getprivset (ucred, PRIV_EFFECTIVE)) == NULL) { - if (ucred != NULL) - ucred_free (ucred); - goto error; - } - - if (!priv_ismember (privs, PRIV_VIRT_MANAGE)) { - ucred_free (ucred); + if (config->auth_unix_rw == REMOTE_AUTH_SASL || + config->auth_unix_ro == REMOTE_AUTH_SASL || + config->auth_tcp == REMOTE_AUTH_SASL || + config->auth_tls == REMOTE_AUTH_SASL) { + saslCtxt = virNetSASLContextNewServer( + (const char *const*)config->sasl_allowed_username_list); + if (!saslCtxt) goto error; - } - - ucred_free (ucred); - } -#endif /* __sun */ - - /* Disable Nagle. Unix sockets will ignore this. */ - setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&no_slow_start, - sizeof no_slow_start); - - if (virSetCloseExec(fd) < 0 || - virSetNonBlock(fd) < 0) { - goto error; - } - - if (VIR_ALLOC(client) < 0) - goto error; - if (virMutexInit(&client->lock) < 0) { - VIR_ERROR(_("cannot initialize mutex")); - goto error; - } - - client->magic = QEMUD_CLIENT_MAGIC; - client->fd = fd; - client->readonly = sock->readonly; - client->type = sock->type; - client->auth = sock->auth; - client->addr = addr; - client->addrstr = addrstr; - addrstr = NULL; - - for (i = 0 ; i < VIR_DOMAIN_EVENT_ID_LAST ; i++) { - client->domainEventCallbackID[i] = -1; } - /* Prepare one for packet receive */ - if (VIR_ALLOC(client->rx) < 0) - goto error; - client->rx->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; +#if HAVE_POLKIT0 + if (auth_unix_rw == REMOTE_AUTH_POLKIT || + auth_unix_ro == REMOTE_AUTH_POLKIT) { + DBusError derr; + dbus_connection_set_change_sigpipe(FALSE); + dbus_threads_init_default(); -#if HAVE_POLKIT - /* Only do policy checks for non-root - allow root user - through with no checks, as a fail-safe - root can easily - change policykit policy anyway, so its pointless trying - to restrict root */ - if (client->auth == REMOTE_AUTH_POLKIT) { - uid_t uid; - pid_t pid; - - if (qemudGetSocketIdentity(client->fd, &uid, &pid) < 0) + dbus_error_init(&derr); + server->sysbus = dbus_bus_get(DBUS_BUS_SYSTEM, &derr); + if (!(server->sysbus)) { + VIR_ERROR(_("Failed to connect to system bus for PolicyKit auth: %s"), + derr.message); + dbus_error_free(&derr); goto error; - - /* Client is running as root, so disable auth */ - if (uid == 0) { - VIR_INFO("Turn off polkit auth for privileged client pid %d from %s", - pid, client->addrstr); - client->auth = REMOTE_AUTH_NONE; } + dbus_connection_set_exit_on_disconnect(server->sysbus, FALSE); } #endif - if (client->type != QEMUD_SOCK_TYPE_TLS) { - /* Plain socket, so prepare to read first message */ - if (qemudRegisterClientEvent (server, client) < 0) - goto error; - } else { - int ret; - - client->tlssession = remoteInitializeTLSSession (); - if (client->tlssession == NULL) - goto error; - - gnutls_transport_set_ptr (client->tlssession, - (gnutls_transport_ptr_t) (long) fd); - - /* Begin the TLS handshake. */ - ret = gnutls_handshake (client->tlssession); - if (ret == 0) { - client->handshake = 0; - - /* Unlikely, but ... Next step is to check the certificate. */ - if (remoteCheckAccess (client) == -1) - goto error; - - /* Handshake & cert check OK, so prepare to read first message */ - if (qemudRegisterClientEvent(server, client) < 0) - goto error; - } else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { - /* Most likely, need to do more handshake data */ - client->handshake = 1; - - if (qemudRegisterClientEvent (server, client) < 0) - goto error; - } else { - PROBE(CLIENT_TLS_FAIL, "fd=%d", client->fd); - VIR_ERROR(_("TLS handshake failed for client %s: %s"), - client->addrstr, gnutls_strerror (ret)); - goto error; - } - } - - server->clients[server->nclients++] = client; - - if (server->nclients > server->nactiveworkers && - server->nactiveworkers < server->nworkers) { - for (i = 0 ; i < server->nworkers ; i++) { - if (!server->workers[i].hasThread) { - if (qemudStartWorker(server, &server->workers[i]) < 0) - return -1; - server->nactiveworkers++; - break; - } - } - } - - return 0; error: - if (client) { - if (client->tlssession) gnutls_deinit (client->tlssession); - if (client) { - VIR_FREE(client->addrstr); - VIR_FREE(client->rx); - } - VIR_FREE(client); - } - VIR_FREE(addrstr); - VIR_FORCE_CLOSE(fd); - PROBE(CLIENT_DISCONNECT, "fd=%d", fd); + virNetServerServiceFree(svcTLS); + virNetServerServiceFree(svcTCP); + virNetServerServiceFree(svc); + virNetServerServiceFree(svcRO); return -1; } -/* - * You must hold lock for at least the client - * We don't free stuff here, merely disconnect the client's - * network socket & resources. - * We keep the libvirt connection open until any async - * jobs have finished, then clean it up elsehwere - */ -void qemudDispatchClientFailure(struct qemud_client *client) { - if (client->watch != -1) { - virEventRemoveHandle(client->watch); - client->watch = -1; - } - - /* Deregister event delivery callback */ - if (client->conn) { - int i; - - for (i = 0 ; i < VIR_DOMAIN_EVENT_ID_LAST ; i++) { - if (client->domainEventCallbackID[i] != -1) { - VIR_DEBUG("Deregistering to relay remote events %d", i); - virConnectDomainEventDeregisterAny(client->conn, - client->domainEventCallbackID[i]); - } - client->domainEventCallbackID[i] = -1; - } - } - -#if HAVE_SASL - if (client->saslconn) { - sasl_dispose(&client->saslconn); - client->saslconn = NULL; - } - VIR_FREE(client->saslUsername); -#endif - if (client->tlssession) { - gnutls_deinit (client->tlssession); - client->tlssession = NULL; - } - if (client->fd != -1) { - PROBE(CLIENT_DISCONNECT, "fd=%d", client->fd); - VIR_FORCE_CLOSE(client->fd); - } - VIR_FREE(client->addrstr); -} - - -/* Caller must hold server lock */ -static struct qemud_client *qemudPendingJob(struct qemud_server *server) -{ - int i; - for (i = 0 ; i < server->nclients ; i++) { - virMutexLock(&server->clients[i]->lock); - if (server->clients[i]->dx) { - /* Delibrately don't unlock client - caller wants the lock */ - return server->clients[i]; - } - virMutexUnlock(&server->clients[i]->lock); - } - return NULL; -} - -static void *qemudWorker(void *data) -{ - struct qemud_worker *worker = data; - struct qemud_server *server = worker->server; - - while (1) { - struct qemud_client *client = NULL; - struct qemud_client_message *msg; - - virMutexLock(&server->lock); - while ((client = qemudPendingJob(server)) == NULL) { - if (worker->quitRequest || - virCondWait(&server->job, &server->lock) < 0) { - virMutexUnlock(&server->lock); - return NULL; - } - } - if (worker->quitRequest) { - virMutexUnlock(&client->lock); - virMutexUnlock(&server->lock); - return NULL; - } - worker->processingCall = 1; - virMutexUnlock(&server->lock); - - /* We own a locked client now... */ - client->refs++; - - /* Remove our message from dispatch queue while we use it */ - msg = qemudClientMessageQueueServe(&client->dx); - - /* This function drops the lock during dispatch, - * and re-acquires it before returning */ - if (remoteDispatchClientRequest (server, client, msg) < 0) { - VIR_FREE(msg); - qemudDispatchClientFailure(client); - client->refs--; - virMutexUnlock(&client->lock); - continue; - } - - client->refs--; - virMutexUnlock(&client->lock); - - virMutexLock(&server->lock); - worker->processingCall = 0; - virMutexUnlock(&server->lock); - } -} - -static int -qemudStartWorker(struct qemud_server *server, - struct qemud_worker *worker) +static int daemonShutdownCheck(virNetServerPtr srv ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) { - pthread_attr_t attr; - int ret = -1; - - if (pthread_attr_init(&attr) != 0) - return -1; - /* We want to join workers, so don't detach them */ - /*pthread_attr_setdetachstate(&attr, 1);*/ - - if (worker->hasThread) - goto cleanup; - - worker->server = server; - worker->hasThread = 1; - worker->quitRequest = 0; - worker->processingCall = 0; - - if (pthread_create(&worker->thread, - &attr, - qemudWorker, - worker) != 0) { - worker->hasThread = 0; - worker->server = NULL; - goto cleanup; - } - - ret = 0; -cleanup: - pthread_attr_destroy(&attr); - return ret; -} - - -/* - * Read data into buffer using wire decoding (plain or TLS) - * - * Returns: - * -1 on error or EOF - * 0 on EAGAIN - * n number of bytes - */ -static ssize_t qemudClientReadBuf(struct qemud_client *client, - char *data, ssize_t len) { - ssize_t ret; - - if (len < 0) { - VIR_ERROR(_("unexpected negative length request %lld"), - (long long int) len); - qemudDispatchClientFailure(client); - return -1; - } - - /* VIR_DEBUG("qemudClientRead: len = %d", len);*/ - - if (!client->tlssession) { - char ebuf[1024]; - ret = read (client->fd, data, len); - if (ret == -1 && (errno == EAGAIN || - errno == EINTR)) - return 0; - if (ret <= 0) { - if (ret != 0) - VIR_ERROR(_("read: %s"), - virStrerror (errno, ebuf, sizeof ebuf)); - qemudDispatchClientFailure(client); - return -1; - } - } else { - ret = gnutls_record_recv (client->tlssession, data, len); - - if (ret < 0 && (ret == GNUTLS_E_AGAIN || - ret == GNUTLS_E_INTERRUPTED)) - return 0; - if (ret <= 0) { - if (ret != 0) - VIR_ERROR(_("gnutls_record_recv: %s"), - gnutls_strerror (ret)); - qemudDispatchClientFailure(client); - return -1; - } - } - - return ret; -} - -/* - * Read data into buffer without decoding - * - * Returns: - * -1 on error or EOF - * 0 on EAGAIN - * n number of bytes - */ -static ssize_t qemudClientReadPlain(struct qemud_client *client) { - ssize_t ret; - ret = qemudClientReadBuf(client, - client->rx->buffer + client->rx->bufferOffset, - client->rx->bufferLength - client->rx->bufferOffset); - if (ret <= 0) - return ret; /* -1 error, 0 eagain */ - - client->rx->bufferOffset += ret; - return ret; -} - -#if HAVE_SASL -/* - * Read data into buffer decoding with SASL - * - * Returns: - * -1 on error or EOF - * 0 on EAGAIN - * n number of bytes - */ -static ssize_t qemudClientReadSASL(struct qemud_client *client) { - ssize_t got, want; - - /* We're doing a SSF data read, so now its times to ensure - * future writes are under SSF too. - * - * cf remoteSASLCheckSSF in remote.c - */ - client->saslSSF |= QEMUD_SASL_SSF_WRITE; - - /* Need to read some more data off the wire */ - if (client->saslDecoded == NULL) { - int ret; - ssize_t encodedLen; - - encodedLen = qemudClientReadBuf(client, client->saslTemporary, - sizeof(client->saslTemporary)); - - if (encodedLen <= 0) - return encodedLen; - - ret = sasl_decode(client->saslconn, client->saslTemporary, encodedLen, - &client->saslDecoded, &client->saslDecodedLength); - - if (ret != SASL_OK) { - VIR_ERROR(_("failed to decode SASL data %s"), - sasl_errstring(ret, NULL, NULL)); - qemudDispatchClientFailure(client); - return -1; - } - - client->saslDecodedOffset = 0; - } - - /* Some buffered decoded data to return now */ - got = client->saslDecodedLength - client->saslDecodedOffset; - want = client->rx->bufferLength - client->rx->bufferOffset; - - if (want > got) - want = got; - - memcpy(client->rx->buffer + client->rx->bufferOffset, - client->saslDecoded + client->saslDecodedOffset, want); - client->saslDecodedOffset += want; - client->rx->bufferOffset += want; - - if (client->saslDecodedOffset == client->saslDecodedLength) { - client->saslDecoded = NULL; - client->saslDecodedOffset = client->saslDecodedLength = 0; - } - - return want; -} -#endif - -/* - * Read as much data off wire as possible till we fill our - * buffer, or would block on I/O - */ -static ssize_t qemudClientRead(struct qemud_client *client) { -#if HAVE_SASL - if (client->saslSSF & QEMUD_SASL_SSF_READ) - return qemudClientReadSASL(client); - else -#endif - return qemudClientReadPlain(client); -} - - -/* - * Read data until we get a complete message to process - */ -static void qemudDispatchClientRead(struct qemud_server *server, - struct qemud_client *client) { - /* VIR_DEBUG("qemudDispatchClientRead: mode = %d", client->mode);*/ - -readmore: - if (qemudClientRead(client) < 0) - return; /* Error */ - - if (client->rx->bufferOffset < client->rx->bufferLength) - return; /* Still not read enough */ - - /* Either done with length word header */ - if (client->rx->bufferLength == REMOTE_MESSAGE_HEADER_XDR_LEN) { - unsigned int len; - XDR x; - - xdrmem_create(&x, client->rx->buffer, client->rx->bufferLength, XDR_DECODE); - - if (!xdr_u_int(&x, &len)) { - xdr_destroy (&x); - VIR_DEBUG("Failed to decode packet length"); - qemudDispatchClientFailure(client); - return; - } - xdr_destroy (&x); - - if (len < REMOTE_MESSAGE_HEADER_XDR_LEN) { - VIR_DEBUG("Packet length %u too small", len); - qemudDispatchClientFailure(client); - return; - } - - /* Length includes the size of the length word itself */ - len -= REMOTE_MESSAGE_HEADER_XDR_LEN; - - if (len > REMOTE_MESSAGE_MAX) { - VIR_DEBUG("Packet length %u too large", len); - qemudDispatchClientFailure(client); - return; - } - - /* Prepare to read rest of message */ - client->rx->bufferLength += len; - - qemudUpdateClientEvent(client); - - /* Try and read payload immediately instead of going back - into poll() because chances are the data is already - waiting for us */ - goto readmore; - } else { - /* Grab the completed message */ - struct qemud_client_message *msg = qemudClientMessageQueueServe(&client->rx); - struct qemud_client_filter *filter; - - /* Decode the header so we can use it for routing decisions */ - if (remoteDecodeClientMessageHeader(msg) < 0) { - VIR_FREE(msg); - qemudDispatchClientFailure(client); - } - - /* Check if any filters match this message */ - filter = client->filters; - while (filter) { - int ret; - ret = (filter->query)(client, msg, filter->opaque); - if (ret == 1) { - msg = NULL; - break; - } else if (ret == -1) { - VIR_FREE(msg); - qemudDispatchClientFailure(client); - return; - } - filter = filter->next; - } - - /* Move completed message to the end of the dispatch queue */ - if (msg) - qemudClientMessageQueuePush(&client->dx, msg); - client->nrequests++; - - /* Possibly need to create another receive buffer */ - if ((client->nrequests < max_client_requests && - VIR_ALLOC(client->rx) < 0)) { - qemudDispatchClientFailure(client); - } else { - if (client->rx) - client->rx->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; - - qemudUpdateClientEvent(client); - - /* Tell one of the workers to get on with it... */ - virCondSignal(&server->job); - } - } -} - - -/* - * Send a chunk of data using wire encoding (plain or TLS) - * - * Returns: - * -1 on error - * 0 on EAGAIN - * n number of bytes - */ -static ssize_t qemudClientWriteBuf(struct qemud_client *client, - const char *data, ssize_t len) { - ssize_t ret; - - if (len < 0) { - VIR_ERROR(_("unexpected negative length request %lld"), - (long long int) len); - qemudDispatchClientFailure(client); - return -1; - } - - if (!client->tlssession) { - char ebuf[1024]; - if ((ret = write(client->fd, data, len)) == -1) { - if (errno == EAGAIN || errno == EINTR) - return 0; - VIR_ERROR(_("write: %s"), virStrerror (errno, ebuf, sizeof ebuf)); - qemudDispatchClientFailure(client); - return -1; - } - } else { - ret = gnutls_record_send (client->tlssession, data, len); - if (ret < 0) { - if (ret == GNUTLS_E_INTERRUPTED || - ret == GNUTLS_E_AGAIN) - return 0; - - VIR_ERROR(_("gnutls_record_send: %s"), gnutls_strerror (ret)); - qemudDispatchClientFailure(client); - return -1; - } - } - return ret; -} - - -/* - * Send client->tx using no encoding - * - * Returns: - * -1 on error or EOF - * 0 on EAGAIN - * n number of bytes - */ -static int qemudClientWritePlain(struct qemud_client *client) { - int ret = qemudClientWriteBuf(client, - client->tx->buffer + client->tx->bufferOffset, - client->tx->bufferLength - client->tx->bufferOffset); - if (ret <= 0) - return ret; /* -1 error, 0 = egain */ - client->tx->bufferOffset += ret; - return ret; -} - - -#if HAVE_SASL -/* - * Send client->tx using SASL encoding - * - * Returns: - * -1 on error - * 0 on EAGAIN - * n number of bytes - */ -static int qemudClientWriteSASL(struct qemud_client *client) { - int ret; - - /* Not got any pending encoded data, so we need to encode raw stuff */ - if (client->saslEncoded == NULL) { - ret = sasl_encode(client->saslconn, - client->tx->buffer + client->tx->bufferOffset, - client->tx->bufferLength - client->tx->bufferOffset, - &client->saslEncoded, - &client->saslEncodedLength); - - if (ret != SASL_OK) { - VIR_ERROR(_("failed to encode SASL data %s"), - sasl_errstring(ret, NULL, NULL)); - qemudDispatchClientFailure(client); - return -1; - } - - client->saslEncodedOffset = 0; - } - - /* Send some of the encoded stuff out on the wire */ - ret = qemudClientWriteBuf(client, - client->saslEncoded + client->saslEncodedOffset, - client->saslEncodedLength - client->saslEncodedOffset); - - if (ret <= 0) - return ret; /* -1 error, 0 == egain */ - - /* Note how much we sent */ - client->saslEncodedOffset += ret; - - /* Sent all encoded, so update raw buffer to indicate completion */ - if (client->saslEncodedOffset == client->saslEncodedLength) { - client->saslEncoded = NULL; - client->saslEncodedOffset = client->saslEncodedLength = 0; - - /* Mark as complete, so caller detects completion */ - client->tx->bufferOffset = client->tx->bufferLength; - } - - return ret; -} -#endif - -/* - * Send as much data in the client->tx as possible - * - * Returns: - * -1 on error or EOF - * 0 on EAGAIN - * n number of bytes - */ -static ssize_t qemudClientWrite(struct qemud_client *client) { -#if HAVE_SASL - if (client->saslSSF & QEMUD_SASL_SSF_WRITE) - return qemudClientWriteSASL(client); - else -#endif - return qemudClientWritePlain(client); -} - - -void -qemudClientMessageRelease(struct qemud_client *client, - struct qemud_client_message *msg) -{ - if (msg->streamTX) { - remoteStreamMessageFinished(client, msg); - } else if (!msg->async) - client->nrequests--; - - /* See if the recv queue is currently throttled */ - if (!client->rx && - client->nrequests < max_client_requests) { - /* Reset message record for next RX attempt */ - memset(msg, 0, sizeof(*msg)); - client->rx = msg; - /* Get ready to receive next message */ - client->rx->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; - } else { - VIR_FREE(msg); - } - - qemudUpdateClientEvent(client); -} - - -/* - * Process all queued client->tx messages until - * we would block on I/O - */ -static void -qemudDispatchClientWrite(struct qemud_client *client) { - while (client->tx) { - ssize_t ret; - - ret = qemudClientWrite(client); - if (ret < 0) { - qemudDispatchClientFailure(client); - return; - } - if (ret == 0) - return; /* Would block on write EAGAIN */ - - if (client->tx->bufferOffset == client->tx->bufferLength) { - struct qemud_client_message *reply; - - /* Get finished reply from head of tx queue */ - reply = qemudClientMessageQueueServe(&client->tx); - - qemudClientMessageRelease(client, reply); - - if (client->closing) - qemudDispatchClientFailure(client); - } - } -} - -static void -qemudDispatchClientHandshake(struct qemud_client *client) { - int ret; - /* Continue the handshake. */ - ret = gnutls_handshake (client->tlssession); - if (ret == 0) { - client->handshake = 0; - - /* Finished. Next step is to check the certificate. */ - if (remoteCheckAccess (client) == -1) - qemudDispatchClientFailure(client); - else - qemudUpdateClientEvent(client); - } else if (ret == GNUTLS_E_AGAIN || - ret == GNUTLS_E_INTERRUPTED) { - /* Carry on waiting for more handshake. Update - the events just in case handshake data flow - direction has changed */ - qemudUpdateClientEvent (client); - } else { - PROBE(CLIENT_TLS_FAIL, "fd=%d", client->fd); - /* Fatal error in handshake */ - VIR_ERROR(_("TLS handshake failed: %s"), - gnutls_strerror (ret)); - qemudDispatchClientFailure(client); - } -} - -static void -qemudDispatchClientEvent(int watch, int fd, int events, void *opaque) { - struct qemud_server *server = (struct qemud_server *)opaque; - struct qemud_client *client = NULL; - int i; - - virMutexLock(&server->lock); - - for (i = 0 ; i < server->nclients ; i++) { - virMutexLock(&server->clients[i]->lock); - if (server->clients[i]->watch == watch) { - client = server->clients[i]; - break; - } - virMutexUnlock(&server->clients[i]->lock); - } - - virMutexUnlock(&server->lock); - - if (!client) { - return; - } - - if (client->fd != fd) { - virMutexUnlock(&client->lock); - return; - } - - if (events & (VIR_EVENT_HANDLE_WRITABLE | - VIR_EVENT_HANDLE_READABLE)) { - if (client->handshake) { - qemudDispatchClientHandshake(client); - } else { - if (events & VIR_EVENT_HANDLE_WRITABLE) - qemudDispatchClientWrite(client); - if (events & VIR_EVENT_HANDLE_READABLE) - qemudDispatchClientRead(server, client); - } - } - - /* NB, will get HANGUP + READABLE at same time upon - * disconnect */ - if (events & (VIR_EVENT_HANDLE_ERROR | - VIR_EVENT_HANDLE_HANGUP)) - qemudDispatchClientFailure(client); - - virMutexUnlock(&client->lock); -} - - -/* - * @client: a locked client object - */ -static int -qemudCalculateHandleMode(struct qemud_client *client) { - int mode = 0; - - if (client->handshake) { - if (gnutls_record_get_direction (client->tlssession) == 0) - mode |= VIR_EVENT_HANDLE_READABLE; - else - mode |= VIR_EVENT_HANDLE_WRITABLE; - } else { - /* If there is a message on the rx queue then - * we're wanting more input */ - if (client->rx) - mode |= VIR_EVENT_HANDLE_READABLE; - - /* If there are one or more messages to send back to client, - then monitor for writability on socket */ - if (client->tx) - mode |= VIR_EVENT_HANDLE_WRITABLE; - } - - return mode; -} - -/* - * @server: a locked or unlocked server object - * @client: a locked client object - */ -int qemudRegisterClientEvent(struct qemud_server *server, - struct qemud_client *client) { - int mode; - - mode = qemudCalculateHandleMode(client); - - if ((client->watch = virEventAddHandle(client->fd, - mode, - qemudDispatchClientEvent, - server, NULL)) < 0) - return -1; - - return 0; -} - -/* - * @client: a locked client object - */ -void qemudUpdateClientEvent(struct qemud_client *client) { - int mode; - - mode = qemudCalculateHandleMode(client); - - virEventUpdateHandle(client->watch, mode); -} - - -static void -qemudDispatchServerEvent(int watch, int fd, int events, void *opaque) { - struct qemud_server *server = (struct qemud_server *)opaque; - struct qemud_socket *sock; - - virMutexLock(&server->lock); - - sock = server->sockets; - - while (sock) { - if (sock->watch == watch) - break; - - sock = sock->next; - } - - if (sock && sock->fd == fd && events) - qemudDispatchServer(server, sock); - - virMutexUnlock(&server->lock); -} - - -static int qemudOneLoop(void) { - sig_atomic_t errors; - - if (virEventRunDefaultImpl() < 0) - return -1; - - /* Check for any signal handling errors and log them. */ - errors = sig_errors; - if (errors) { - char ebuf[1024]; - sig_errors -= errors; - VIR_ERROR(_("Signal handler reported %d errors: last error: %s"), - errors, virStrerror (sig_lasterrno, ebuf, sizeof ebuf)); - return -1; - } - - return 0; -} - -static void qemudInactiveTimer(int timerid, void *data) { - struct qemud_server *server = (struct qemud_server *)data; - - if (virStateActive() || - server->clients) { - VIR_DEBUG("Timer expired but still active, not shutting down"); - virEventUpdateTimeout(timerid, -1); - } else { - VIR_DEBUG("Timer expired and inactive, shutting down"); - server->quitEventThread = 1; - } -} - -static void qemudFreeClient(struct qemud_client *client) { - while (client->rx) { - struct qemud_client_message *msg - = qemudClientMessageQueueServe(&client->rx); - VIR_FREE(msg); - } - while (client->dx) { - struct qemud_client_message *msg - = qemudClientMessageQueueServe(&client->dx); - VIR_FREE(msg); - } - while (client->tx) { - struct qemud_client_message *msg - = qemudClientMessageQueueServe(&client->tx); - VIR_FREE(msg); - } - - while (client->streams) - remoteRemoveClientStream(client, client->streams); - - if (client->conn) - virConnectClose(client->conn); - virMutexDestroy(&client->lock); - VIR_FREE(client->addrstr); - VIR_FREE(client); -} - -static void *qemudRunLoop(void *opaque) { - struct qemud_server *server = opaque; - int timerid = -1; - int i; - int timerActive = 0; - - virMutexLock(&server->lock); - - if (timeout > 0 && - (timerid = virEventAddTimeout(-1, - qemudInactiveTimer, - server, NULL)) < 0) { - VIR_ERROR(_("Failed to register shutdown timeout")); - return NULL; - } - - if (min_workers > max_workers) - max_workers = min_workers; - - server->nworkers = max_workers; - if (VIR_ALLOC_N(server->workers, server->nworkers) < 0) { - VIR_ERROR(_("Failed to allocate workers")); - return NULL; - } - - for (i = 0 ; i < min_workers ; i++) { - if (qemudStartWorker(server, &server->workers[i]) < 0) - goto cleanup; - server->nactiveworkers++; - } - - for (;!server->quitEventThread;) { - /* A shutdown timeout is specified, so check - * if any drivers have active state, if not - * shutdown after timeout seconds - */ - if (timeout > 0) { - if (timerActive) { - if (server->clients) { - VIR_DEBUG("Deactivating shutdown timer %d", timerid); - virEventUpdateTimeout(timerid, -1); - timerActive = 0; - } - } else { - if (!virStateActive() && - !server->clients) { - VIR_DEBUG("Activating shutdown timer %d", timerid); - virEventUpdateTimeout(timerid, timeout * 1000); - timerActive = 1; - } - } - } - - virMutexUnlock(&server->lock); - if (qemudOneLoop() < 0) { - virMutexLock(&server->lock); - VIR_DEBUG("Loop iteration error, exiting"); - break; - } - virMutexLock(&server->lock); - - reprocess: - for (i = 0 ; i < server->nclients ; i++) { - int inactive; - virMutexLock(&server->clients[i]->lock); - inactive = server->clients[i]->fd == -1 - && server->clients[i]->refs == 0; - virMutexUnlock(&server->clients[i]->lock); - if (inactive) { - qemudFreeClient(server->clients[i]); - server->nclients--; - if (i < server->nclients) - memmove(server->clients + i, - server->clients + i + 1, - sizeof (*server->clients) * (server->nclients - i)); - - VIR_SHRINK_N(server->clients, server->nclients_max, - server->nclients_max - server->nclients); - goto reprocess; - } - } - - /* If number of active workers exceeds both the min_workers - * threshold and the number of clients, then kill some - * off */ - for (i = 0 ; (i < server->nworkers && - server->nactiveworkers > server->nclients && - server->nactiveworkers > min_workers) ; i++) { - - if (server->workers[i].hasThread && - !server->workers[i].processingCall) { - server->workers[i].quitRequest = 1; - - virCondBroadcast(&server->job); - virMutexUnlock(&server->lock); - pthread_join(server->workers[i].thread, NULL); - virMutexLock(&server->lock); - server->workers[i].hasThread = 0; - server->nactiveworkers--; - } - } - } - -cleanup: - for (i = 0 ; i < server->nworkers ; i++) { - if (!server->workers[i].hasThread) - continue; - - server->workers[i].quitRequest = 1; - virCondBroadcast(&server->job); - - virMutexUnlock(&server->lock); - pthread_join(server->workers[i].thread, NULL); - virMutexLock(&server->lock); - server->workers[i].hasThread = 0; - } - VIR_FREE(server->workers); - for (i = 0; i < server->nclients; i++) - qemudFreeClient(server->clients[i]); - server->nclients = 0; - VIR_SHRINK_N(server->clients, server->nclients_max, server->nclients_max); - - virMutexUnlock(&server->lock); - return NULL; -} - - -static int -qemudStartEventLoop(struct qemud_server *server) -{ - pthread_attr_t attr; - int ret = -1; - - if (pthread_attr_init(&attr) != 0) - return -1; - /* We want to join the eventloop, so don't detach it */ - /*pthread_attr_setdetachstate(&attr, 1);*/ - - if (pthread_create(&server->eventThread, - &attr, - qemudRunLoop, - server) != 0) - goto cleanup; - - server->hasEventThread = 1; - - ret = 0; -cleanup: - pthread_attr_destroy(&attr); - return ret; -} - - -static void qemudCleanup(struct qemud_server *server) { - struct qemud_socket *sock; - - VIR_FORCE_CLOSE(server->sigread); - VIR_FORCE_CLOSE(server->sigwrite); - - sock = server->sockets; - while (sock) { - struct qemud_socket *next = sock->next; - if (sock->watch) - virEventRemoveHandle(sock->watch); - VIR_FORCE_CLOSE(sock->fd); - - /* Unlink unix domain sockets which are not in - * the abstract namespace */ - if (sock->path && - sock->path[0] != '@') - unlink(sock->path); - VIR_FREE(sock->path); - VIR_FREE(sock->addrstr); - - VIR_FREE(sock); - sock = next; - } - VIR_FREE(server->logDir); - -#ifdef HAVE_SASL - if (server->saslUsernameWhitelist) { - char **list = server->saslUsernameWhitelist; - while (*list) { - VIR_FREE(*list); - list++; - } - VIR_FREE(server->saslUsernameWhitelist); - } -#endif - -#if HAVE_POLKIT0 - if (server->sysbus) - dbus_connection_unref(server->sysbus); -#endif - - virStateCleanup(); - - if (virCondDestroy(&server->job) < 0) { - ; - } - virMutexDestroy(&server->lock); + if (virStateActive()) + return 0; - VIR_FREE(server); + return 1; } + /* Allocate an array of malloc'd strings from the config file, filename * (used only in diagnostics), using handle "conf". Upon error, return -1 * and free any allocated memory. Otherwise, save the array in *list_arg @@ -2632,13 +709,11 @@ checkType (virConfValuePtr p, const char *filename, virConfValuePtr p = virConfGetValue (conf, #var_name); \ if (p) { \ if (checkType (p, filename, #var_name, VIR_CONF_STRING) < 0) \ - goto free_and_fail; \ - (var_name) = strdup (p->str); \ - if ((var_name) == NULL) { \ - char ebuf[1024]; \ - VIR_ERROR(_("remoteReadConfigFile: %s"), \ - virStrerror(errno, ebuf, sizeof ebuf)); \ - goto free_and_fail; \ + goto error; \ + VIR_FREE(data->var_name); \ + if (!(data->var_name = strdup (p->str))) { \ + virReportOOMError(); \ + goto error; \ } \ } \ } while (0) @@ -2649,8 +724,8 @@ checkType (virConfValuePtr p, const char *filename, virConfValuePtr p = virConfGetValue (conf, #var_name); \ if (p) { \ if (checkType (p, filename, #var_name, VIR_CONF_LONG) < 0) \ - goto free_and_fail; \ - (var_name) = p->l; \ + goto error; \ + data->var_name = p->l; \ } \ } while (0) @@ -2669,15 +744,11 @@ static int remoteConfigGetAuth(virConfPtr conf, const char *key, int *auth, cons return 0; if (STREQ(p->str, "none")) { - *auth = REMOTE_AUTH_NONE; -#if HAVE_SASL + *auth = VIR_NET_SERVER_SERVICE_AUTH_NONE; } else if (STREQ(p->str, "sasl")) { - *auth = REMOTE_AUTH_SASL; -#endif -#if HAVE_POLKIT + *auth = VIR_NET_SERVER_SERVICE_AUTH_SASL; } else if (STREQ(p->str, "polkit")) { - *auth = REMOTE_AUTH_POLKIT; -#endif + *auth = VIR_NET_SERVER_SERVICE_AUTH_POLKIT; } else { VIR_ERROR(_("remoteReadConfigFile: %s: %s: unsupported auth %s"), filename, key, p->str); @@ -2687,26 +758,6 @@ static int remoteConfigGetAuth(virConfPtr conf, const char *key, int *auth, cons return 0; } -#ifdef HAVE_SASL -static inline int -remoteReadSaslAllowedUsernameList (virConfPtr conf, - struct qemud_server *server, - const char *filename) -{ - return - remoteConfigGetStringList (conf, "sasl_allowed_username_list", - &server->saslUsernameWhitelist, filename); -} -#else -static inline int -remoteReadSaslAllowedUsernameList (virConfPtr conf ATTRIBUTE_UNUSED, - struct qemud_server *server ATTRIBUTE_UNUSED, - const char *filename ATTRIBUTE_UNUSED) -{ - return 0; -} -#endif - /* * Set up the logging environment * By default if daemonized all errors go to the logfile libvirtd.log, @@ -2714,19 +765,11 @@ remoteReadSaslAllowedUsernameList (virConfPtr conf ATTRIBUTE_UNUSED, * informational and debug messages. Default size if 64 kB. */ static int -qemudSetLogging(struct qemud_server *server, virConfPtr conf, - const char *filename) +daemonSetupLogging(struct daemonConfig *config, + bool privileged, + bool verbose, + bool godaemon) { - int log_level = 0; - int log_buffer_size = 64; - char *log_filters = NULL; - char *log_outputs = NULL; - char *log_file = NULL; - int ret = -1; - - GET_CONF_INT (conf, filename, log_buffer_size); - virLogSetBufferSize(log_buffer_size); - virLogReset(); /* @@ -2743,21 +786,18 @@ qemudSetLogging(struct qemud_server *server, virConfPtr conf, * level has been set, we must process variables in the opposite * order, each one overriding the previous. */ - GET_CONF_INT (conf, filename, log_level); - if (log_level != 0) - virLogSetDefaultPriority(log_level); + if (config->log_level != 0) + virLogSetDefaultPriority(config->log_level); virLogSetFromEnv(); - if (virLogGetNbFilters() == 0) { - GET_CONF_STR (conf, filename, log_filters); - virLogParseFilters(log_filters); - } + virLogSetBufferSize(config->log_buffer_size); - if (virLogGetNbOutputs() == 0) { - GET_CONF_STR (conf, filename, log_outputs); - virLogParseOutputs(log_outputs); - } + if (virLogGetNbFilters() == 0) + virLogParseFilters(config->log_filters); + + if (virLogGetNbOutputs() == 0) + virLogParseOutputs(config->log_outputs); /* * If no defined outputs, then direct to libvirtd.log when running @@ -2767,26 +807,26 @@ qemudSetLogging(struct qemud_server *server, virConfPtr conf, char *tmp = NULL; if (godaemon) { - if (server->privileged) { + if (privileged) { if (virAsprintf(&tmp, "%d:file:%s/log/libvirt/libvirtd.log", virLogGetDefaultPriority(), LOCALSTATEDIR) == -1) - goto out_of_memory; + goto no_memory; } else { char *userdir = virGetUserDirectory(geteuid()); if (!userdir) - goto free_and_fail; + goto error; if (virAsprintf(&tmp, "%d:file:%s/.libvirt/libvirtd.log", virLogGetDefaultPriority(), userdir) == -1) { VIR_FREE(userdir); - goto out_of_memory; + goto no_memory; } VIR_FREE(userdir); } } else { if (virAsprintf(&tmp, "%d:stderr", virLogGetDefaultPriority()) < 0) - goto out_of_memory; + goto no_memory; } virLogParseOutputs(tmp); VIR_FREE(tmp); @@ -2798,60 +838,191 @@ qemudSetLogging(struct qemud_server *server, virConfPtr conf, if ((verbose) && (virLogGetDefaultPriority() > VIR_LOG_INFO)) virLogSetDefaultPriority(VIR_LOG_INFO); - ret = 0; + return 0; + +no_memory: + virReportOOMError(); +error: + return -1; +} + + +static int +daemonConfigFilePath(bool privileged, char **configfile) +{ + if (privileged) { + if (!(*configfile = strdup(SYSCONFDIR "/libvirt/libvirtd.conf"))) + goto no_memory; + } else { + char *userdir = NULL; + + if (!(userdir = virGetUserDirectory(geteuid()))) + goto error; + + if (virAsprintf(configfile, "%s/.libvirt/libvirtd.conf", userdir) < 0) { + VIR_FREE(userdir); + goto no_memory; + } + VIR_FREE(userdir); + } -free_and_fail: - VIR_FREE(log_filters); - VIR_FREE(log_outputs); - VIR_FREE(log_file); - return(ret); + return 0; -out_of_memory: +no_memory: virReportOOMError(); - goto free_and_fail; +error: + return -1; } -/* - * Stop logging - */ static void -qemudStopLogging(void) +daemonConfigFree(struct daemonConfig *data); + +static struct daemonConfig* +daemonConfigNew(bool privileged) { - virLogShutdown(); + struct daemonConfig *data; + char *localhost; + int ret; + + if (VIR_ALLOC(data) < 0) { + virReportOOMError(); + return NULL; + } + + data->listen_tls = 1; + data->listen_tcp = 0; + + if (!(data->tls_port = strdup(LIBVIRTD_TLS_PORT))) + goto no_memory; + if (!(data->tcp_port = strdup(LIBVIRTD_TCP_PORT))) + goto no_memory; + + /* Only default to PolicyKit if running as root */ +#if HAVE_POLKIT + if (privileged) { + data->auth_unix_rw = REMOTE_AUTH_POLKIT; + data->auth_unix_ro = REMOTE_AUTH_POLKIT; + } else { +#endif + data->auth_unix_rw = REMOTE_AUTH_NONE; + data->auth_unix_ro = REMOTE_AUTH_NONE; +#if HAVE_POLKIT + } +#endif + + if (data->auth_unix_rw == REMOTE_AUTH_POLKIT) + data->unix_sock_rw_perms = strdup("0777"); /* Allow world */ + else + data->unix_sock_rw_perms = strdup("0700"); /* Allow user only */ + data->unix_sock_ro_perms = strdup("0777"); /* Always allow world */ + if (!data->unix_sock_ro_perms || + !data->unix_sock_rw_perms) + goto no_memory; + +#if HAVE_SASL + data->auth_tcp = REMOTE_AUTH_SASL; +#else + data->auth_tcp = REMOTE_AUTH_NONE; +#endif + data->auth_tls = REMOTE_AUTH_NONE; + + data->mdns_adv = 1; + + data->min_workers = 5; + data->max_workers = 20; + data->max_clients = 20; + + data->max_requests = 20; + data->max_client_requests = 5; + + data->log_buffer_size = 64; + + data->audit_level = 1; + data->audit_logging = 0; + + localhost = virGetHostname(NULL); + if (localhost == NULL) { + /* we couldn't resolve the hostname; assume that we are + * running in disconnected operation, and report a less + * useful Avahi string + */ + ret = virAsprintf(&data->mdns_name, "Virtualization Host"); + } else { + char *tmp; + /* Extract the host part of the potentially FQDN */ + if ((tmp = strchr(localhost, '.'))) + *tmp = '\0'; + ret = virAsprintf(&data->mdns_name, "Virtualization Host %s", + localhost); + } + VIR_FREE(localhost); + if (ret < 0) + goto no_memory; + + return data; + +no_memory: + virReportOOMError(); + daemonConfigFree(data); + return NULL; +} + +static void +daemonConfigFree(struct daemonConfig *data) +{ + char **tmp; + + if (!data) + return; + + VIR_FREE(data->listen_addr); + VIR_FREE(data->tls_port); + VIR_FREE(data->tcp_port); + + VIR_FREE(data->unix_sock_ro_perms); + VIR_FREE(data->unix_sock_rw_perms); + VIR_FREE(data->unix_sock_group); + VIR_FREE(data->unix_sock_dir); + VIR_FREE(data->mdns_name); + + tmp = data->tls_allowed_dn_list; + while (tmp && *tmp) { + VIR_FREE(*tmp); + tmp++; + } + VIR_FREE(data->tls_allowed_dn_list); + + tmp = data->sasl_allowed_username_list; + while (tmp && *tmp) { + VIR_FREE(*tmp); + tmp++; + } + VIR_FREE(data->sasl_allowed_username_list); + + VIR_FREE(data->key_file); + VIR_FREE(data->ca_file); + VIR_FREE(data->cert_file); + VIR_FREE(data->crl_file); + + VIR_FREE(data->log_filters); + VIR_FREE(data->log_outputs); + + VIR_FREE(data); } + /* Read the config file if it exists. * Only used in the remote case, hence the name. */ static int -remoteReadConfigFile (struct qemud_server *server, const char *filename) +daemonConfigLoad(struct daemonConfig *data, + const char *filename) { virConfPtr conf; - /* The following variable names must match the corresponding - configuration strings. */ - char *unix_sock_ro_perms = NULL; - char *unix_sock_rw_perms = NULL; - char *unix_sock_group = NULL; - char *buf = NULL; - char *host_uuid = NULL; - -#if HAVE_POLKIT - /* Change the default back to no auth for non-root */ - if (!server->privileged && auth_unix_rw == REMOTE_AUTH_POLKIT) - auth_unix_rw = REMOTE_AUTH_NONE; - if (!server->privileged && auth_unix_ro == REMOTE_AUTH_POLKIT) - auth_unix_ro = REMOTE_AUTH_NONE; -#endif - conf = virConfReadFile (filename, 0); - if (!conf) return -1; - - /* - * First get all the logging settings and activate them - */ - if (qemudSetLogging(server, conf, filename) < 0) - goto free_and_fail; + if (!conf) + return -1; GET_CONF_INT (conf, filename, listen_tcp); GET_CONF_INT (conf, filename, listen_tls); @@ -2859,76 +1030,28 @@ remoteReadConfigFile (struct qemud_server *server, const char *filename) GET_CONF_STR (conf, filename, tcp_port); GET_CONF_STR (conf, filename, listen_addr); - if (remoteConfigGetAuth(conf, "auth_unix_rw", &auth_unix_rw, filename) < 0) - goto free_and_fail; + if (remoteConfigGetAuth(conf, "auth_unix_rw", &data->auth_unix_rw, filename) < 0) + goto error; #if HAVE_POLKIT /* Change default perms to be wide-open if PolicyKit is enabled. * Admin can always override in config file */ - if (auth_unix_rw == REMOTE_AUTH_POLKIT) - unix_sock_rw_mask = 0777; + if (data->auth_unix_rw == REMOTE_AUTH_POLKIT) { + VIR_FREE(data->unix_sock_rw_perms); + if (!(data->unix_sock_rw_perms = strdup("0777"))) + goto no_memory; + } #endif - if (remoteConfigGetAuth(conf, "auth_unix_ro", &auth_unix_ro, filename) < 0) - goto free_and_fail; - if (remoteConfigGetAuth(conf, "auth_tcp", &auth_tcp, filename) < 0) - goto free_and_fail; - if (remoteConfigGetAuth(conf, "auth_tls", &auth_tls, filename) < 0) - goto free_and_fail; + if (remoteConfigGetAuth(conf, "auth_unix_ro", &data->auth_unix_ro, filename) < 0) + goto error; + if (remoteConfigGetAuth(conf, "auth_tcp", &data->auth_tcp, filename) < 0) + goto error; + if (remoteConfigGetAuth(conf, "auth_tls", &data->auth_tls, filename) < 0) + goto error; GET_CONF_STR (conf, filename, unix_sock_group); - if (unix_sock_group) { - if (!server->privileged) { - VIR_WARN("Cannot set group when not running as root"); - } else { - int ret; - struct group grpdata, *grp; - size_t maxbuf = sysconf(_SC_GETGR_R_SIZE_MAX); - - if (maxbuf == -1) - maxbuf = 1024; - - if (VIR_ALLOC_N(buf, maxbuf) < 0) { - VIR_ERROR(_("Failed to allocate memory for buffer")); - goto free_and_fail; - } - - while ((ret = getgrnam_r(unix_sock_group, &grpdata, - buf, maxbuf, - &grp)) == ERANGE) { - maxbuf *= 2; - if (maxbuf > 65536 || VIR_REALLOC_N(buf, maxbuf) < 0) { - VIR_ERROR(_("Failed to reallocate enough memory for buffer")); - goto free_and_fail; - } - } - - if (ret != 0 || !grp) { - VIR_ERROR(_("Failed to lookup group '%s'"), unix_sock_group); - goto free_and_fail; - } - unix_sock_gid = grp->gr_gid; - VIR_FREE(buf); - } - VIR_FREE(unix_sock_group); - } - GET_CONF_STR (conf, filename, unix_sock_ro_perms); - if (unix_sock_ro_perms) { - if (virStrToLong_i (unix_sock_ro_perms, NULL, 8, &unix_sock_ro_mask) != 0) { - VIR_ERROR(_("Failed to parse mode '%s'"), unix_sock_ro_perms); - goto free_and_fail; - } - VIR_FREE(unix_sock_ro_perms); - } - GET_CONF_STR (conf, filename, unix_sock_rw_perms); - if (unix_sock_rw_perms) { - if (virStrToLong_i (unix_sock_rw_perms, NULL, 8, &unix_sock_rw_mask) != 0) { - VIR_ERROR(_("Failed to parse mode '%s'"), unix_sock_rw_perms); - goto free_and_fail; - } - VIR_FREE(unix_sock_rw_perms); - } GET_CONF_STR (conf, filename, unix_sock_dir); @@ -2942,12 +1065,14 @@ remoteReadConfigFile (struct qemud_server *server, const char *filename) GET_CONF_STR (conf, filename, ca_file); GET_CONF_STR (conf, filename, crl_file); - if (remoteConfigGetStringList (conf, "tls_allowed_dn_list", - &tls_allowed_dn_list, filename) < 0) - goto free_and_fail; + if (remoteConfigGetStringList(conf, "tls_allowed_dn_list", + &data->tls_allowed_dn_list, filename) < 0) + goto error; + - if (remoteReadSaslAllowedUsernameList (conf, server, filename) < 0) - goto free_and_fail; + if (remoteConfigGetStringList(conf, "sasl_allowed_username_list", + &data->sasl_allowed_username_list, filename) < 0) + goto error; GET_CONF_INT (conf, filename, min_workers); @@ -2961,51 +1086,32 @@ remoteReadConfigFile (struct qemud_server *server, const char *filename) GET_CONF_INT (conf, filename, audit_logging); GET_CONF_STR (conf, filename, host_uuid); - if (virSetHostUUIDStr(host_uuid)) { - VIR_ERROR(_("invalid host UUID: %s"), host_uuid); - goto free_and_fail; - } - VIR_FREE(host_uuid); + GET_CONF_INT (conf, filename, log_level); + GET_CONF_STR (conf, filename, log_filters); + GET_CONF_STR (conf, filename, log_outputs); + GET_CONF_INT (conf, filename, log_buffer_size); virConfFree (conf); return 0; - free_and_fail: +no_memory: + virReportOOMError(); +error: virConfFree (conf); - VIR_FREE(host_uuid); - VIR_FREE(mdns_name); - VIR_FREE(unix_sock_ro_perms); - VIR_FREE(unix_sock_rw_perms); - VIR_FREE(unix_sock_group); - VIR_FREE(buf); - - /* Don't bother trying to free listen_addr, tcp_port, tls_port, key_file, - cert_file, ca_file, or crl_file, since they are initialized to - non-malloc'd strings. Besides, these are static variables, and callers - are unlikely to call this function more than once, so there wouldn't - even be a real leak. */ - - if (tls_allowed_dn_list) { - int i; - for (i = 0; tls_allowed_dn_list[i]; i++) - VIR_FREE(tls_allowed_dn_list[i]); - VIR_FREE(tls_allowed_dn_list); - } - return -1; } /* Display version information. */ static void -version (void) +daemonVersion(const char *argv0) { printf ("%s (%s) %s\n", argv0, PACKAGE_NAME, PACKAGE_VERSION); } #ifdef __sun static int -qemudSetupPrivs (void) +daemonSetupPrivs(void) { chown ("/var/run/libvirt", SYSTEM_UID, SYSTEM_UID); @@ -3024,84 +1130,61 @@ qemudSetupPrivs (void) return 0; } #else -# define qemudSetupPrivs() 0 +# define daemonSetupPrivs() 0 #endif -/* - * Doing anything non-trivial in signal handlers is pretty dangerous, - * since there are very few async-signal safe POSIX funtions. To - * deal with this we setup a very simple signal handler. It simply - * writes the signal number to a pipe. The main event loop then sees - * the signal on the pipe and can safely do the processing from - * event loop context - */ -static int -daemonSetupSignals(struct qemud_server *server) +static void daemonShutdownHandler(virNetServerPtr srv, + siginfo_t *sig ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) { - struct sigaction sig_action; - int sigpipe[2]; + virNetServerQuit(srv); +} - if (pipe(sigpipe) < 0) +static int daemonSetupSignals(virNetServerPtr srv) +{ + if (virNetServerAddSignalHandler(srv, SIGINT, daemonShutdownHandler, NULL) < 0) return -1; + if (virNetServerAddSignalHandler(srv, SIGQUIT, daemonShutdownHandler, NULL) < 0) + return -1; + if (virNetServerAddSignalHandler(srv, SIGTERM, daemonShutdownHandler, NULL) < 0) + return -1; + return 0; +} - if (virSetNonBlock(sigpipe[0]) < 0 || - virSetNonBlock(sigpipe[1]) < 0 || - virSetCloseExec(sigpipe[0]) < 0 || - virSetCloseExec(sigpipe[1]) < 0) { - char ebuf[1024]; - VIR_ERROR(_("Failed to create pipe: %s"), - virStrerror(errno, ebuf, sizeof ebuf)); - goto error; - } - - sig_action.sa_sigaction = sig_handler; - sig_action.sa_flags = SA_SIGINFO; - sigemptyset(&sig_action.sa_mask); - - sigaction(SIGHUP, &sig_action, NULL); - sigaction(SIGINT, &sig_action, NULL); - sigaction(SIGQUIT, &sig_action, NULL); - sigaction(SIGTERM, &sig_action, NULL); +static void daemonRunStateInit(void *opaque) +{ + virNetServerPtr srv = opaque; - /* - * catch fatal errors to dump a log, also hook to USR2 for dynamic - * debugging purposes or testing - */ - sig_action.sa_sigaction = sig_fatal; - sigaction(SIGFPE, &sig_action, NULL); - sigaction(SIGSEGV, &sig_action, NULL); - sigaction(SIGILL, &sig_action, NULL); - sigaction(SIGABRT, &sig_action, NULL); - sigaction(SIGBUS, &sig_action, NULL); - sigaction(SIGUSR2, &sig_action, NULL); - - sig_action.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sig_action, NULL); - - if (virEventAddHandle(sigpipe[0], - VIR_EVENT_HANDLE_READABLE, - qemudDispatchSignalEvent, - server, NULL) < 0) { - VIR_ERROR(_("Failed to register callback for signal pipe")); - goto error; + /* Start the stateful HV drivers + * This is delibrately done after telling the parent process + * we're ready, since it can take a long time and this will + * seriously delay OS bootup process */ + if (virStateInitialize(virNetServerIsPrivileged(srv)) < 0) { + VIR_ERROR(_("Driver state initialization failed")); + virNetServerFree(srv); + return; } - server->sigread = sigpipe[0]; - server->sigwrite = sigpipe[1]; - sigwrite = sigpipe[1]; + /* Only now accept clients from network */ + virNetServerUpdateServices(srv, true); + virNetServerFree(srv); +} +static int daemonStateInit(virNetServerPtr srv) +{ + virThread thr; + virNetServerRef(srv); + if (virThreadCreate(&thr, false, daemonRunStateInit, srv) < 0) { + virNetServerFree(srv); + return -1; + } return 0; - -error: - VIR_FORCE_CLOSE(sigpipe[0]); - VIR_FORCE_CLOSE(sigpipe[1]); - return -1; } /* Print command-line usage. */ static void -usage (void) +daemonUsage(const char *argv0, bool privileged) { fprintf (stderr, _("\n\ @@ -3117,38 +1200,55 @@ Options:\n\ | --version Display version information.\n\ -p | --pid-file <file> Change name of PID file.\n\ \n\ -libvirt management daemon:\n\ -\n\ +libvirt management daemon:\n"), argv0); + + if (privileged) { + fprintf(stderr, + _("\n\ Default paths:\n\ \n\ Configuration file (unless overridden by -f):\n\ %s/libvirt/libvirtd.conf\n\ \n\ - Sockets (as root):\n\ + Sockets:\n\ %s/run/libvirt/libvirt-sock\n\ %s/run/libvirt/libvirt-sock-ro\n\ \n\ - Sockets (as non-root):\n\ + TLS:\n\ + CA certificate: %s/pki/CA/caert.pem\n\ + Server certificate: %s/pki/libvirt/servercert.pem\n\ + Server private key: %s/pki/libvirt/private/serverkey.pem\n\ +\n\ + PID file (unless overridden by -p):\n\ + %s/run/libvirtd.pid\n\ +\n"), + SYSCONFDIR, + LOCALSTATEDIR, + LOCALSTATEDIR, + SYSCONFDIR, + SYSCONFDIR, + SYSCONFDIR, + LOCALSTATEDIR); + } else { + fprintf(stderr, + "%s", _("\n\ + Default paths:\n\ +\n\ + Configuration file (unless overridden by -f):\n\ + $HOME/.libvirt/libvirtd.conf\n\ +\n\ + Sockets:\n\ $HOME/.libvirt/libvirt-sock (in UNIX abstract namespace)\n\ \n\ TLS:\n\ - CA certificate: %s\n\ - Server certificate: %s\n\ - Server private key: %s\n\ + CA certificate: $HOME/.pki/libvirt/cacert.pem\n\ + Server certificate: $HOME/.pki/libvirt/servercert.pem\n\ + Server private key: $HOME/.pki/libvirt/serverkey.pem\n\ \n\ - PID file (unless overridden by --pid-file):\n\ - %s\n\ -\n"), - argv0, - SYSCONFDIR, - LOCALSTATEDIR, - LOCALSTATEDIR, - LIBVIRT_CACERT, - LIBVIRT_SERVERCERT, - LIBVIRT_SERVERKEY, - (REMOTE_PID_FILE[0] != '\0' - ? REMOTE_PID_FILE - : _("(disabled in ./configure)"))); + PID file:\n\ + $HOME/.libvirt/libvirtd.pid\n\ +\n")); + } } enum { @@ -3157,12 +1257,19 @@ enum { #define MAX_LISTEN 5 int main(int argc, char **argv) { - struct qemud_server *server = NULL; - const char *pid_file = NULL; - const char *remote_config_file = NULL; + virNetServerPtr srv = NULL; + char *remote_config_file = NULL; int statuswrite = -1; int ret = 1; - argv0 = argv[0]; + char *pid_file = NULL; + char *sock_file = NULL; + char *sock_file_ro = NULL; + int timeout = -1; /* -t: Shutdown timeout */ + int verbose = 0; + int godaemon = 0; + int ipsock = 0; + struct daemonConfig *config; + bool privileged = geteuid() == 0 ? true : false; struct option opts[] = { { "verbose", no_argument, &verbose, 1}, @@ -3180,7 +1287,7 @@ int main(int argc, char **argv) { bindtextdomain (PACKAGE, LOCALEDIR) == NULL || textdomain(PACKAGE) == NULL || virInitialize() < 0) { - fprintf(stderr, _("%s: initialization failed\n"), argv0); + fprintf(stderr, _("%s: initialization failed\n"), argv[0]); exit(EXIT_FAILURE); } @@ -3218,69 +1325,91 @@ int main(int argc, char **argv) { break; case 'p': - pid_file = optarg; + VIR_FREE(pid_file); + if (!(pid_file = strdup(optarg))) + exit(EXIT_FAILURE); break; case 'f': - remote_config_file = optarg; + if (!(remote_config_file = strdup(optarg))) + exit(EXIT_FAILURE); break; case OPT_VERSION: - version (); + daemonVersion(argv[0]); return 0; case '?': - usage (); + daemonUsage(argv[0], privileged); return 2; default: fprintf (stderr, _("%s: internal error: unknown flag: %c\n"), - argv0, c); + argv[0], c); exit (EXIT_FAILURE); } } - if (remote_config_file == NULL) { - static const char *default_config_file - = SYSCONFDIR "/libvirt/libvirtd.conf"; - remote_config_file = - (access(default_config_file, R_OK) == 0 - ? default_config_file - : "/dev/null"); + if (!(config = daemonConfigNew(privileged))) + exit(EXIT_FAILURE); + + /* No explicit config, so try and find a default one */ + if (remote_config_file == NULL && + daemonConfigFilePath(privileged, + &remote_config_file) < 0) + exit(EXIT_FAILURE); + + /* Read the config file if it exists*/ + if (remote_config_file && + daemonConfigLoad(config, remote_config_file) < 0) + exit(EXIT_FAILURE); + + if (config->host_uuid && + virSetHostUUIDStr(config->host_uuid) < 0) { + VIR_ERROR(_("invalid host UUID: %s"), config->host_uuid); + exit(EXIT_FAILURE); } + if (daemonSetupLogging(config, privileged, verbose, godaemon) < 0) + exit(EXIT_FAILURE); + + if (!pid_file && privileged && + daemonPidFilePath(privileged, + &pid_file) < 0) + exit(EXIT_FAILURE); + + if (daemonUnixSocketPaths(config, + privileged, + &sock_file, + &sock_file_ro) < 0) + exit(EXIT_FAILURE); + if (godaemon) { char ebuf[1024]; if (chdir("/") < 0) { VIR_ERROR(_("cannot change to root directory: %s"), virStrerror(errno, ebuf, sizeof(ebuf))); - goto error; + goto cleanup; } - if ((statuswrite = daemonForkIntoBackground()) < 0) { + if ((statuswrite = daemonForkIntoBackground(argv[0])) < 0) { VIR_ERROR(_("Failed to fork as daemon: %s"), virStrerror(errno, ebuf, sizeof ebuf)); - goto error; + goto cleanup; } } - /* If running as root and no PID file is set, use the default */ - if (pid_file == NULL && - geteuid() == 0 && - REMOTE_PID_FILE[0] != '\0') - pid_file = REMOTE_PID_FILE; - /* If we have a pidfile set, claim it now, exiting if already taken */ if (pid_file != NULL && - qemudWritePidFile (pid_file) < 0) { - pid_file = NULL; /* Prevent unlinking of someone else's pid ! */ + daemonWritePidFile(pid_file, argv[0]) < 0) { + VIR_FREE(pid_file); /* Prevent unlinking of someone else's pid ! */ ret = VIR_DAEMON_ERR_PIDFILE; - goto error; + goto cleanup; } /* Ensure the rundir exists (on tmpfs on some systems) */ - if (geteuid() == 0) { + if (privileged) { const char *rundir = LOCALSTATEDIR "/run/libvirt"; mode_t old_umask; @@ -3292,62 +1421,89 @@ int main(int argc, char **argv) { virStrerror(errno, ebuf, sizeof(ebuf))); ret = VIR_DAEMON_ERR_RUNDIR; umask(old_umask); - goto error; + goto cleanup; } } umask(old_umask); } + if (!(srv = virNetServerNew(config->min_workers, + config->max_workers, + config->max_clients, + config->mdns_adv ? config->mdns_name : NULL, + remoteClientInitHook))) { + ret = VIR_DAEMON_ERR_INIT; + goto cleanup; + } + /* Beyond this point, nothing should rely on using * getuid/geteuid() == 0, for privilege level checks. - * It must all use the flag 'server->privileged' - * which is also passed into all libvirt stateful - * drivers */ - if (qemudSetupPrivs() < 0) { + if (daemonSetupPrivs() < 0) { ret = VIR_DAEMON_ERR_PRIVS; - goto error; + goto cleanup; } - /* Initialise GnuTLS. Required even if we don't use TLS - * for libvirtd, because QEMU driver needs to be able to - * parse x590 certificates for seamless migration */ - gnutls_global_init(); + daemonInitialize(); - if (!(server = qemudInitialize())) { + remoteProcs[REMOTE_PROC_AUTH_LIST].needAuth = false; + remoteProcs[REMOTE_PROC_AUTH_SASL_INIT].needAuth = false; + remoteProcs[REMOTE_PROC_AUTH_SASL_STEP].needAuth = false; + remoteProcs[REMOTE_PROC_AUTH_SASL_START].needAuth = false; + remoteProcs[REMOTE_PROC_AUTH_POLKIT].needAuth = false; + if (!(remoteProgram = virNetServerProgramNew(REMOTE_PROGRAM, + REMOTE_PROTOCOL_VERSION, + remoteProcs, + remoteNProcs))) { ret = VIR_DAEMON_ERR_INIT; - goto error; + goto cleanup; + } + if (virNetServerAddProgram(srv, remoteProgram) < 0) { + ret = VIR_DAEMON_ERR_INIT; + goto cleanup; } - if ((daemonSetupSignals(server)) < 0) { - ret = VIR_DAEMON_ERR_SIGNAL; - goto error; + if (!(qemuProgram = virNetServerProgramNew(QEMU_PROGRAM, + QEMU_PROTOCOL_VERSION, + qemuProcs, + qemuNProcs))) { + ret = VIR_DAEMON_ERR_INIT; + goto cleanup; + } + if (virNetServerAddProgram(srv, qemuProgram) < 0) { + ret = VIR_DAEMON_ERR_INIT; + goto cleanup; } - /* Read the config file (if it exists). */ - if (remoteReadConfigFile (server, remote_config_file) < 0) { - ret = VIR_DAEMON_ERR_CONFIG; - goto error; + if (timeout != -1) + virNetServerAutoShutdown(srv, + timeout, + daemonShutdownCheck, + NULL); + + if ((daemonSetupSignals(srv)) < 0) { + ret = VIR_DAEMON_ERR_SIGNAL; + goto cleanup; } - if (audit_level) { + if (config->audit_level) { if (virAuditOpen() < 0) { - if (audit_level > 1) { + if (config->audit_level > 1) { ret = VIR_DAEMON_ERR_AUDIT; - goto error; + goto cleanup; } } } - virAuditLog(audit_logging); + virAuditLog(config->audit_logging); /* setup the hooks if any */ if (virHookInitialize() < 0) { ret = VIR_DAEMON_ERR_HOOKS; - goto error; + goto cleanup; } /* Disable error func, now logging is setup */ - virSetErrorFunc(NULL, virshErrorHandler); + virSetErrorFunc(NULL, daemonErrorHandler); virSetErrorLogPriorityFunc(daemonErrorLogFilter); /* @@ -3358,9 +1514,11 @@ int main(int argc, char **argv) { virHookCall(VIR_HOOK_DRIVER_DAEMON, "-", VIR_HOOK_DAEMON_OP_START, 0, "start", NULL); - if (qemudNetworkInit(server) < 0) { + if (daemonSetupNetworking(srv, config, + sock_file, sock_file_ro, + ipsock, privileged) < 0) { ret = VIR_DAEMON_ERR_NETWORK; - goto error; + goto cleanup; } /* Tell parent of daemon that basic initialization is complete @@ -3375,52 +1533,24 @@ int main(int argc, char **argv) { VIR_FORCE_CLOSE(statuswrite); } - /* Start the event loop in a background thread, since - * state initialization needs events to be being processed */ - if (qemudStartEventLoop(server) < 0) { - VIR_ERROR(_("Event thread startup failed")); - goto error; - } - - /* Start the stateful HV drivers - * This is delibrately done after telling the parent process - * we're ready, since it can take a long time and this will - * seriously delay OS bootup process */ - if (virStateInitialize(server->privileged) < 0) { - VIR_ERROR(_("Driver state initialization failed")); - goto shutdown; + /* Initialize drivers & then start accepting new clients from network */ + if (daemonStateInit(srv) < 0) { + ret = VIR_DAEMON_ERR_INIT; + goto cleanup; } - /* Start accepting new clients from network */ - virMutexLock(&server->lock); - if (qemudNetworkEnable(server) < 0) { - VIR_ERROR(_("Network event loop enablement failed")); - goto shutdown; - } - virMutexUnlock(&server->lock); + /* Run event loop. */ + virNetServerRun(srv); ret = 0; -shutdown: - /* In a non-0 shutdown scenario we need to tell event loop - * to quit immediately. Otherwise in normal case we just - * sit in the thread join forever. Sure this means the - * main thread doesn't do anything useful ever, but that's - * not too much of drain on resources - */ - if (ret != 0) { - virMutexLock(&server->lock); - if (server->hasEventThread) - /* This SIGQUIT triggers the shutdown process */ - kill(getpid(), SIGQUIT); - virMutexUnlock(&server->lock); - } - pthread_join(server->eventThread, NULL); - virHookCall(VIR_HOOK_DRIVER_DAEMON, "-", VIR_HOOK_DAEMON_OP_SHUTDOWN, 0, "shutdown", NULL); -error: +cleanup: + virNetServerProgramFree(remoteProgram); + virNetServerProgramFree(qemuProgram); + virNetServerFree(srv); if (statuswrite != -1) { if (ret != 0) { /* Tell parent of daemon what failed */ @@ -3431,10 +1561,15 @@ error: } VIR_FORCE_CLOSE(statuswrite); } - if (server) - qemudCleanup(server); if (pid_file) unlink (pid_file); - qemudStopLogging(); + + VIR_FREE(sock_file); + VIR_FREE(sock_file_ro); + VIR_FREE(pid_file); + VIR_FREE(remote_config_file); + daemonConfigFree(config); + virLogShutdown(); + return ret; } diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index ea00d5c..6c604fc 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -27,13 +27,6 @@ # include <config.h> -# include <gnutls/gnutls.h> -# include <gnutls/x509.h> -# include "gnutls_1_0_compat.h" -# if HAVE_SASL -# include <sasl/sasl.h> -# endif - # if HAVE_POLKIT0 # include <dbus/dbus.h> # endif @@ -45,6 +38,8 @@ # include "logging.h" # include "threads.h" # include "network.h" +# include "virnetsaslcontext.h" +# include "virnetserverprogram.h" # if WITH_DTRACE # ifndef LIBVIRTD_PROBES_H @@ -63,230 +58,37 @@ #NAME ": " FMT, __VA_ARGS__); # endif -# ifdef __GNUC__ -# ifdef HAVE_ANSIDECL_H -# include <ansidecl.h> -# endif - -# ifndef __GNUC_PREREQ -# if defined __GNUC__ && defined __GNUC_MINOR__ -# define __GNUC_PREREQ(maj, min) \ - ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) -# else -# define __GNUC_PREREQ(maj,min) 0 -# endif -# endif - -/** - * ATTRIBUTE_UNUSED: - * - * Macro to flag conciously unused parameters to functions - */ -# ifndef ATTRIBUTE_UNUSED -# define ATTRIBUTE_UNUSED __attribute__((__unused__)) -# endif - -/** - * ATTRIBUTE_FMT_PRINTF - * - * Macro used to check printf like functions, if compiling - * with gcc. - * - * We use gnulib which guarentees we always have GNU style - * printf format specifiers even on broken Win32 platforms - * hence we have to force 'gnu_printf' for new GCC - */ -# ifndef ATTRIBUTE_FMT_PRINTF -# if __GNUC_PREREQ (4, 4) -# define ATTRIBUTE_FMT_PRINTF(fmtpos,argpos) __attribute__((__format__ (gnu_printf, fmtpos,argpos))) -# else -# define ATTRIBUTE_FMT_PRINTF(fmtpos,argpos) __attribute__((__format__ (printf, fmtpos,argpos))) -# endif -# endif - -# ifndef ATTRIBUTE_RETURN_CHECK -# if __GNUC_PREREQ (3, 4) -# define ATTRIBUTE_RETURN_CHECK __attribute__((__warn_unused_result__)) -# else -# define ATTRIBUTE_RETURN_CHECK -# endif -# endif - -# else -# ifndef ATTRIBUTE_UNUSED -# define ATTRIBUTE_UNUSED -# endif -# ifndef ATTRIBUTE_FMT_PRINTF -# define ATTRIBUTE_FMT_PRINTF(...) -# endif -# ifndef ATTRIBUTE_RETURN_CHECK -# define ATTRIBUTE_RETURN_CHECK -# endif -# endif - -/* Whether we're passing reads & writes through a sasl SSF */ -enum qemud_sasl_ssf { - QEMUD_SASL_SSF_NONE = 0, - QEMUD_SASL_SSF_READ = 1, - QEMUD_SASL_SSF_WRITE = 2, -}; - -enum qemud_sock_type { - QEMUD_SOCK_TYPE_UNIX = 0, - QEMUD_SOCK_TYPE_TCP = 1, - QEMUD_SOCK_TYPE_TLS = 2, -}; - -struct qemud_client_message { - char buffer [REMOTE_MESSAGE_MAX + REMOTE_MESSAGE_HEADER_XDR_LEN]; - unsigned int bufferLength; - unsigned int bufferOffset; - - unsigned int async : 1; - unsigned int streamTX : 1; - - remote_message_header hdr; - - struct qemud_client_message *next; -}; - -struct qemud_client; - -/* Allow for filtering of incoming messages to a custom - * dispatch processing queue, instead of client->dx. - */ -typedef int (*qemud_client_filter_func)(struct qemud_client *client, - struct qemud_client_message *msg, void *opaque); -struct qemud_client_filter { - qemud_client_filter_func query; - void *opaque; - - struct qemud_client_filter *next; -}; - -struct qemud_client_stream { - virStreamPtr st; - int procedure; - int serial; - - unsigned int recvEOF : 1; - unsigned int closed : 1; - - struct qemud_client_filter filter; - - struct qemud_client_message *rx; - int tx; - - struct qemud_client_stream *next; -}; +typedef struct daemonClientStream daemonClientStream; +typedef daemonClientStream *daemonClientStreamPtr; +typedef struct daemonClientPrivate daemonClientPrivate; +typedef daemonClientPrivate *daemonClientPrivatePtr; /* Stores the per-client connection state */ -struct qemud_client { +struct daemonClientPrivate { + /* Hold while accessing any data except conn */ virMutex lock; - int magic; - - int fd; - int watch; - unsigned int readonly :1; - unsigned int closing :1; int domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LAST]; - virSocketAddr addr; - const char *addrstr; - - int type; /* qemud_sock_type */ - gnutls_session_t tlssession; - int auth; - unsigned int handshake :1; /* If we're in progress for TLS handshake */ -# if HAVE_SASL - sasl_conn_t *saslconn; - int saslSSF; - const char *saslDecoded; - unsigned int saslDecodedLength; - unsigned int saslDecodedOffset; - const char *saslEncoded; - unsigned int saslEncodedLength; - unsigned int saslEncodedOffset; - char *saslUsername; - char saslTemporary[8192]; /* temorary holds data to be decoded */ -# endif - - /* Count of meages in 'dx' or 'tx' queue - * ie RPC calls in progress. Does not count - * async events which are not used for - * throttling calculations */ - int nrequests; - /* Zero or one messages being received. Zero if - * nrequests >= max_clients and throttling */ - struct qemud_client_message *rx; - /* Zero or many messages waiting for a worker - * to process them */ - struct qemud_client_message *dx; - /* Zero or many messages waiting for transmit - * back to client, including async events */ - struct qemud_client_message *tx; - /* Filters to capture messages that would otherwise - * end up on the 'dx' queue */ - struct qemud_client_filter *filters; - - /* Data streams */ - struct qemud_client_stream *streams; - + virNetSASLSessionPtr sasl; /* This is only valid if a remote open call has been made on this * connection, otherwise it will be NULL. Also if remote close is * called, it will be set back to NULL if that succeeds. */ virConnectPtr conn; - int refs; - -}; - -# define QEMUD_CLIENT_MAGIC 0x7788aaee - -struct qemud_socket { - char *path; - - virSocketAddr addr; - const char *addrstr; - - int fd; - int watch; - int readonly; - int type; /* qemud_sock_type */ - int auth; - - struct qemud_socket *next; + daemonClientStreamPtr streams; }; -struct qemud_worker { - pthread_t thread; - unsigned int hasThread :1; - unsigned int processingCall :1; - unsigned int quitRequest :1; - - /* back-pointer to our server */ - struct qemud_server *server; -}; +extern virNetSASLContextPtr saslCtxt; +extern virNetServerProgramPtr remoteProgram; +extern virNetServerProgramPtr qemuProgram; /* Main server state */ struct qemud_server { - virMutex lock; - virCond job; - int privileged; - size_t nworkers; - size_t nactiveworkers; - struct qemud_worker *workers; - size_t nsockets; - struct qemud_socket *sockets; - size_t nclients; - size_t nclients_max; - struct qemud_client **clients; - int sigread; int sigwrite; char *logDir; @@ -304,27 +106,6 @@ struct qemud_server { # endif }; -void qemudLog(int priority, const char *fmt, ...) - ATTRIBUTE_FMT_PRINTF(2,3); - - - -int qemudRegisterClientEvent(struct qemud_server *server, - struct qemud_client *client); -void qemudUpdateClientEvent(struct qemud_client *client); - -void qemudDispatchClientFailure(struct qemud_client *client); - -void -qemudClientMessageQueuePush(struct qemud_client_message **queue, - struct qemud_client_message *msg); -struct qemud_client_message * -qemudClientMessageQueueServe(struct qemud_client_message **queue); - -void -qemudClientMessageRelease(struct qemud_client *client, - struct qemud_client_message *msg); - # if HAVE_POLKIT int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid); diff --git a/daemon/remote.c b/daemon/remote.c index aa726cf..3df6f87 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -22,26 +22,6 @@ #include <config.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/stat.h> -#include <unistd.h> -#include <fcntl.h> -#include <limits.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <sys/poll.h> -#include <netinet/in.h> -#include <netdb.h> -#include <stdlib.h> -#include <pwd.h> -#include <stdio.h> -#include <stdarg.h> -#include <syslog.h> -#include <string.h> -#include <errno.h> -#include <fnmatch.h> -#include <arpa/inet.h> #include "virterror_internal.h" #if HAVE_POLKIT0 @@ -50,11 +30,11 @@ #endif #include "remote.h" -#include "dispatch.h" - +#include "libvirtd.h" #include "libvirt_internal.h" #include "datatypes.h" #include "memory.h" +#include "logging.h" #include "util.h" #include "stream.h" #include "uuid.h" @@ -62,8 +42,13 @@ #include "libvirt/libvirt-qemu.h" #include "command.h" #include "intprops.h" +#include "virnetserverservice.h" + +#include "remote_protocol.h" +#include "qemu_protocol.h" -#define VIR_FROM_THIS VIR_FROM_REMOTE + +#define VIR_FROM_THIS VIR_FROM_RPC #define virNetError(code, ...) \ virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \ @@ -105,41 +90,25 @@ static void make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr static void make_nonnull_nwfilter(remote_nonnull_nwfilter *net_dst, virNWFilterPtr nwfilter_src); static void make_nonnull_domain_snapshot(remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src); +static int +remoteSerializeTypedParameters(virTypedParameterPtr params, + int nparams, + remote_typed_param **ret_params_val, + u_int *ret_params_len); +static virTypedParameterPtr +remoteDeserializeTypedParameters(remote_typed_param *args_params_val, + u_int args_params_len, + int limit, + int *nparams); -#include "remote_dispatch_prototypes.h" -#include "qemu_dispatch_prototypes.h" - -static const dispatch_data const dispatch_table[] = { -#include "remote_dispatch_table.h" -}; - -static const dispatch_data const qemu_dispatch_table[] = { -#include "qemu_dispatch_table.h" -}; - -const dispatch_data const *remoteGetDispatchData(int proc) -{ - if (proc >= ARRAY_CARDINALITY(dispatch_table) || - dispatch_table[proc].fn == NULL) { - return NULL; - } - - return &(dispatch_table[proc]); -} - -const dispatch_data const *qemuGetDispatchData(int proc) -{ - if (proc >= ARRAY_CARDINALITY(qemu_dispatch_table) || - qemu_dispatch_table[proc].fn == NULL) { - return NULL; - } +#include "remote_dispatch.h" +#include "qemu_dispatch.h" - return &(qemu_dispatch_table[proc]); -} /* Prototypes */ static void -remoteDispatchDomainEventSend(struct qemud_client *client, +remoteDispatchDomainEventSend(virNetServerClientPtr client, + virNetServerProgramPtr program, int procnr, xdrproc_t proc, void *data); @@ -150,7 +119,7 @@ static int remoteRelayDomainEventLifecycle(virConnectPtr conn ATTRIBUTE_UNUSED, int detail, void *opaque) { - struct qemud_client *client = opaque; + virNetServerClientPtr client = opaque; remote_domain_event_lifecycle_msg data; if (!client) @@ -158,20 +127,16 @@ static int remoteRelayDomainEventLifecycle(virConnectPtr conn ATTRIBUTE_UNUSED, VIR_DEBUG("Relaying domain lifecycle event %d %d", event, detail); - virMutexLock(&client->lock); - /* build return data */ memset(&data, 0, sizeof data); make_nonnull_domain(&data.dom, dom); data.event = event; data.detail = detail; - remoteDispatchDomainEventSend(client, + remoteDispatchDomainEventSend(client, remoteProgram, REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE, (xdrproc_t)xdr_remote_domain_event_lifecycle_msg, &data); - virMutexUnlock(&client->lock); - return 0; } @@ -179,7 +144,7 @@ static int remoteRelayDomainEventReboot(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, void *opaque) { - struct qemud_client *client = opaque; + virNetServerClientPtr client = opaque; remote_domain_event_reboot_msg data; if (!client) @@ -187,18 +152,14 @@ static int remoteRelayDomainEventReboot(virConnectPtr conn ATTRIBUTE_UNUSED, VIR_DEBUG("Relaying domain reboot event %s %d", dom->name, dom->id); - virMutexLock(&client->lock); - /* build return data */ memset(&data, 0, sizeof data); make_nonnull_domain(&data.dom, dom); - remoteDispatchDomainEventSend(client, + remoteDispatchDomainEventSend(client, remoteProgram, REMOTE_PROC_DOMAIN_EVENT_REBOOT, (xdrproc_t)xdr_remote_domain_event_reboot_msg, &data); - virMutexUnlock(&client->lock); - return 0; } @@ -208,7 +169,7 @@ static int remoteRelayDomainEventRTCChange(virConnectPtr conn ATTRIBUTE_UNUSED, long long offset, void *opaque) { - struct qemud_client *client = opaque; + virNetServerClientPtr client = opaque; remote_domain_event_rtc_change_msg data; if (!client) @@ -216,19 +177,15 @@ static int remoteRelayDomainEventRTCChange(virConnectPtr conn ATTRIBUTE_UNUSED, VIR_DEBUG("Relaying domain rtc change event %s %d %lld", dom->name, dom->id, offset); - virMutexLock(&client->lock); - /* build return data */ memset(&data, 0, sizeof data); make_nonnull_domain(&data.dom, dom); data.offset = offset; - remoteDispatchDomainEventSend(client, + remoteDispatchDomainEventSend(client, remoteProgram, REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE, (xdrproc_t)xdr_remote_domain_event_rtc_change_msg, &data); - virMutexUnlock(&client->lock); - return 0; } @@ -238,7 +195,7 @@ static int remoteRelayDomainEventWatchdog(virConnectPtr conn ATTRIBUTE_UNUSED, int action, void *opaque) { - struct qemud_client *client = opaque; + virNetServerClientPtr client = opaque; remote_domain_event_watchdog_msg data; if (!client) @@ -246,19 +203,15 @@ static int remoteRelayDomainEventWatchdog(virConnectPtr conn ATTRIBUTE_UNUSED, VIR_DEBUG("Relaying domain watchdog event %s %d %d", dom->name, dom->id, action); - virMutexLock(&client->lock); - /* build return data */ memset(&data, 0, sizeof data); make_nonnull_domain(&data.dom, dom); data.action = action; - remoteDispatchDomainEventSend(client, + remoteDispatchDomainEventSend(client, remoteProgram, REMOTE_PROC_DOMAIN_EVENT_WATCHDOG, (xdrproc_t)xdr_remote_domain_event_watchdog_msg, &data); - virMutexUnlock(&client->lock); - return 0; } @@ -270,7 +223,7 @@ static int remoteRelayDomainEventIOError(virConnectPtr conn ATTRIBUTE_UNUSED, int action, void *opaque) { - struct qemud_client *client = opaque; + virNetServerClientPtr client = opaque; remote_domain_event_io_error_msg data; if (!client) @@ -278,8 +231,6 @@ static int remoteRelayDomainEventIOError(virConnectPtr conn ATTRIBUTE_UNUSED, VIR_DEBUG("Relaying domain io error %s %d %s %s %d", dom->name, dom->id, srcPath, devAlias, action); - virMutexLock(&client->lock); - /* build return data */ memset(&data, 0, sizeof data); make_nonnull_domain(&data.dom, dom); @@ -287,12 +238,10 @@ static int remoteRelayDomainEventIOError(virConnectPtr conn ATTRIBUTE_UNUSED, data.devAlias = (char*)devAlias; data.action = action; - remoteDispatchDomainEventSend(client, + remoteDispatchDomainEventSend(client, remoteProgram, REMOTE_PROC_DOMAIN_EVENT_IO_ERROR, (xdrproc_t)xdr_remote_domain_event_io_error_msg, &data); - virMutexUnlock(&client->lock); - return 0; } @@ -305,7 +254,7 @@ static int remoteRelayDomainEventIOErrorReason(virConnectPtr conn ATTRIBUTE_UNUS const char *reason, void *opaque) { - struct qemud_client *client = opaque; + virNetServerClientPtr client = opaque; remote_domain_event_io_error_reason_msg data; if (!client) @@ -314,8 +263,6 @@ static int remoteRelayDomainEventIOErrorReason(virConnectPtr conn ATTRIBUTE_UNUS VIR_DEBUG("Relaying domain io error %s %d %s %s %d %s", dom->name, dom->id, srcPath, devAlias, action, reason); - virMutexLock(&client->lock); - /* build return data */ memset(&data, 0, sizeof data); make_nonnull_domain(&data.dom, dom); @@ -324,12 +271,10 @@ static int remoteRelayDomainEventIOErrorReason(virConnectPtr conn ATTRIBUTE_UNUS data.action = action; data.reason = (char*)reason; - remoteDispatchDomainEventSend(client, + remoteDispatchDomainEventSend(client, remoteProgram, REMOTE_PROC_DOMAIN_EVENT_IO_ERROR_REASON, (xdrproc_t)xdr_remote_domain_event_io_error_reason_msg, &data); - virMutexUnlock(&client->lock); - return 0; } @@ -343,7 +288,7 @@ static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainEventGraphicsSubjectPtr subject, void *opaque) { - struct qemud_client *client = opaque; + virNetServerClientPtr client = opaque; remote_domain_event_graphics_msg data; int i; @@ -360,8 +305,6 @@ static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED, VIR_DEBUG(" %s=%s", subject->identities[i].type, subject->identities[i].name); } - virMutexLock(&client->lock); - /* build return data */ memset(&data, 0, sizeof data); make_nonnull_domain(&data.dom, dom); @@ -378,7 +321,7 @@ static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED, data.subject.subject_len = subject->nidentity; if (VIR_ALLOC_N(data.subject.subject_val, data.subject.subject_len) < 0) { - VIR_WARN("cannot allocate memory for graphics event subject"); + virReportOOMError(); return -1; } for (i = 0 ; i < data.subject.subject_len ; i++) { @@ -386,14 +329,12 @@ static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED, data.subject.subject_val[i].name = (char*)subject->identities[i].name; } - remoteDispatchDomainEventSend(client, + remoteDispatchDomainEventSend(client, remoteProgram, REMOTE_PROC_DOMAIN_EVENT_GRAPHICS, (xdrproc_t)xdr_remote_domain_event_graphics_msg, &data); VIR_FREE(data.subject.subject_val); - virMutexUnlock(&client->lock); - return 0; } @@ -402,7 +343,7 @@ static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSE virDomainPtr dom, void *opaque) { - struct qemud_client *client = opaque; + virNetServerClientPtr client = opaque; remote_domain_event_control_error_msg data; if (!client) @@ -410,18 +351,14 @@ static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSE VIR_DEBUG("Relaying domain control error %s %d", dom->name, dom->id); - virMutexLock(&client->lock); - /* build return data */ memset(&data, 0, sizeof data); make_nonnull_domain(&data.dom, dom); - remoteDispatchDomainEventSend(client, + remoteDispatchDomainEventSend(client, remoteProgram, REMOTE_PROC_DOMAIN_EVENT_CONTROL_ERROR, (xdrproc_t)xdr_remote_domain_event_control_error_msg, &data); - virMutexUnlock(&client->lock); - return 0; } @@ -439,25 +376,80 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); +/* + * You must hold lock for at least the client + * We don't free stuff here, merely disconnect the client's + * network socket & resources. + * We keep the libvirt connection open until any async + * jobs have finished, then clean it up elsehwere + */ +static void remoteClientFreeFunc(void *data) +{ + struct daemonClientPrivate *priv = data; + + /* Deregister event delivery callback */ + if (priv->conn) { + int i; + + for (i = 0 ; i < VIR_DOMAIN_EVENT_ID_LAST ; i++) { + if (priv->domainEventCallbackID[i] != -1) { + VIR_DEBUG("Deregistering to relay remote events %d", i); + virConnectDomainEventDeregisterAny(priv->conn, + priv->domainEventCallbackID[i]); + } + priv->domainEventCallbackID[i] = -1; + } + + virConnectClose(priv->conn); + } + + VIR_FREE(priv); +} + + + +int remoteClientInitHook(virNetServerPtr srv ATTRIBUTE_UNUSED, + virNetServerClientPtr client) +{ + struct daemonClientPrivate *priv; + int i; + + if (VIR_ALLOC(priv) < 0) { + virReportOOMError(); + return -1; + } + + if (virMutexInit(&priv->lock) < 0) { + VIR_FREE(priv); + virReportOOMError(); + return -1; + } + + for (i = 0 ; i < VIR_DOMAIN_EVENT_ID_LAST ; i++) + priv->domainEventCallbackID[i] = -1; + + virNetServerClientSetPrivateData(client, priv, remoteClientFreeFunc); + return 0; +} + /*----- Functions. -----*/ static int -remoteDispatchOpen(struct qemud_server *server, - struct qemud_client *client, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - struct remote_open_args *args, void *ret ATTRIBUTE_UNUSED) +remoteDispatchOpen(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + struct remote_open_args *args) { const char *name; int flags; + struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); int rv = -1; - virMutexLock(&server->lock); - virMutexLock(&client->lock); - virMutexUnlock(&server->lock); - - if (conn) { + VIR_DEBUG("priv=%p conn=%p", priv, priv->conn); + virMutexLock(&priv->lock); + /* Already opened? */ + if (priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection already open")); goto cleanup; } @@ -468,50 +460,43 @@ remoteDispatchOpen(struct qemud_server *server, * the connection to be readonly. */ flags = args->flags; - if (client->readonly) flags |= VIR_CONNECT_RO; + if (virNetServerClientGetReadonly(client)) + flags |= VIR_CONNECT_RO; - client->conn = + priv->conn = flags & VIR_CONNECT_RO ? virConnectOpenReadOnly(name) : virConnectOpen(name); - if (client->conn == NULL) + if (priv->conn == NULL) goto cleanup; rv = 0; cleanup: if (rv < 0) - remoteDispatchError(rerr); - virMutexUnlock(&client->lock); + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); return rv; } static int -remoteDispatchClose(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn ATTRIBUTE_UNUSED, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr ATTRIBUTE_UNUSED, - void *args ATTRIBUTE_UNUSED, void *ret ATTRIBUTE_UNUSED) +remoteDispatchClose(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED) { - virMutexLock(&server->lock); - virMutexLock(&client->lock); - virMutexUnlock(&server->lock); - - client->closing = 1; - - virMutexUnlock(&client->lock); + virNetServerClientClose(client); return 0; } + static int -remoteDispatchDomainGetSchedulerType(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainGetSchedulerType(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_get_scheduler_type_args *args, remote_domain_get_scheduler_type_ret *ret) { @@ -519,13 +504,15 @@ remoteDispatchDomainGetSchedulerType(struct qemud_server *server ATTRIBUTE_UNUSE char *type; int nparams; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; if (!(type = virDomainGetSchedulerType(dom, &nparams))) @@ -537,7 +524,7 @@ remoteDispatchDomainGetSchedulerType(struct qemud_server *server ATTRIBUTE_UNUSE cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (dom) virDomainFree(dom); return rv; @@ -681,11 +668,10 @@ cleanup: } static int -remoteDispatchDomainGetSchedulerParameters(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainGetSchedulerParameters(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_get_scheduler_parameters_args *args, remote_domain_get_scheduler_parameters_ret *ret) { @@ -693,8 +679,10 @@ remoteDispatchDomainGetSchedulerParameters(struct qemud_server *server ATTRIBUTE virTypedParameterPtr params = NULL; int nparams = args->nparams; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } @@ -706,7 +694,7 @@ remoteDispatchDomainGetSchedulerParameters(struct qemud_server *server ATTRIBUTE if (VIR_ALLOC_N(params, nparams) < 0) goto no_memory; - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; if (virDomainGetSchedulerParameters(dom, params, &nparams) < 0) @@ -721,7 +709,7 @@ remoteDispatchDomainGetSchedulerParameters(struct qemud_server *server ATTRIBUTE cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (dom) virDomainFree(dom); VIR_FREE(params); @@ -733,11 +721,10 @@ no_memory: } static int -remoteDispatchDomainGetSchedulerParametersFlags(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainGetSchedulerParametersFlags(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_get_scheduler_parameters_flags_args *args, remote_domain_get_scheduler_parameters_flags_ret *ret) { @@ -745,8 +732,10 @@ remoteDispatchDomainGetSchedulerParametersFlags(struct qemud_server *server ATTR virTypedParameterPtr params = NULL; int nparams = args->nparams; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } @@ -758,7 +747,7 @@ remoteDispatchDomainGetSchedulerParametersFlags(struct qemud_server *server ATTR if (VIR_ALLOC_N(params, nparams) < 0) goto no_memory; - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; if (virDomainGetSchedulerParametersFlags(dom, params, &nparams, @@ -774,7 +763,7 @@ remoteDispatchDomainGetSchedulerParametersFlags(struct qemud_server *server ATTR cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (dom) virDomainFree(dom); VIR_FREE(params); @@ -786,11 +775,10 @@ no_memory: } static int -remoteDispatchDomainMemoryStats(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainMemoryStats(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_memory_stats_args *args, remote_domain_memory_stats_ret *ret) { @@ -798,8 +786,10 @@ remoteDispatchDomainMemoryStats(struct qemud_server *server ATTRIBUTE_UNUSED, struct _virDomainMemoryStat *stats; int nr_stats, i; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } @@ -810,7 +800,7 @@ remoteDispatchDomainMemoryStats(struct qemud_server *server ATTRIBUTE_UNUSED, goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; /* Allocate stats array for making dispatch call */ @@ -839,7 +829,7 @@ remoteDispatchDomainMemoryStats(struct qemud_server *server ATTRIBUTE_UNUSED, cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (dom) virDomainFree(dom); VIR_FREE(stats); @@ -847,11 +837,10 @@ cleanup: } static int -remoteDispatchDomainBlockPeek(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainBlockPeek(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_block_peek_args *args, remote_domain_block_peek_ret *ret) { @@ -861,13 +850,15 @@ remoteDispatchDomainBlockPeek(struct qemud_server *server ATTRIBUTE_UNUSED, size_t size; unsigned int flags; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; path = args->path; offset = args->offset; @@ -894,7 +885,7 @@ remoteDispatchDomainBlockPeek(struct qemud_server *server ATTRIBUTE_UNUSED, cleanup: if (rv < 0) { - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); VIR_FREE(ret->buffer.buffer_val); } if (dom) @@ -903,11 +894,10 @@ cleanup: } static int -remoteDispatchDomainMemoryPeek(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainMemoryPeek(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_memory_peek_args *args, remote_domain_memory_peek_ret *ret) { @@ -916,13 +906,15 @@ remoteDispatchDomainMemoryPeek(struct qemud_server *server ATTRIBUTE_UNUSED, size_t size; unsigned int flags; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; offset = args->offset; size = args->size; @@ -948,7 +940,7 @@ remoteDispatchDomainMemoryPeek(struct qemud_server *server ATTRIBUTE_UNUSED, cleanup: if (rv < 0) { - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); VIR_FREE(ret->buffer.buffer_val); } if (dom) @@ -957,24 +949,25 @@ cleanup: } static int -remoteDispatchDomainGetSecurityLabel(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainGetSecurityLabel(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_get_security_label_args *args, remote_domain_get_security_label_ret *ret) { virDomainPtr dom = NULL; virSecurityLabelPtr seclabel = NULL; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; if (VIR_ALLOC(seclabel) < 0) { @@ -997,7 +990,7 @@ remoteDispatchDomainGetSecurityLabel(struct qemud_server *server ATTRIBUTE_UNUSE cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (dom) virDomainFree(dom); VIR_FREE(seclabel); @@ -1005,24 +998,24 @@ cleanup: } static int -remoteDispatchNodeGetSecurityModel(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - void *args ATTRIBUTE_UNUSED, +remoteDispatchNodeGetSecurityModel(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_node_get_security_model_ret *ret) { virSecurityModel secmodel; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } memset(&secmodel, 0, sizeof secmodel); - if (virNodeGetSecurityModel(conn, &secmodel) < 0) + if (virNodeGetSecurityModel(priv->conn, &secmodel) < 0) goto cleanup; ret->model.model_len = strlen(secmodel.model) + 1; @@ -1043,16 +1036,15 @@ remoteDispatchNodeGetSecurityModel(struct qemud_server *server ATTRIBUTE_UNUSED, cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); return rv; } static int -remoteDispatchDomainGetVcpupinInfo(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainGetVcpupinInfo(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_get_vcpupin_info_args *args, remote_domain_get_vcpupin_info_ret *ret) { @@ -1060,13 +1052,15 @@ remoteDispatchDomainGetVcpupinInfo(struct qemud_server *server ATTRIBUTE_UNUSED, unsigned char *cpumaps = NULL; int num; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; if (args->ncpumaps > REMOTE_VCPUINFO_MAX) { @@ -1105,7 +1099,7 @@ remoteDispatchDomainGetVcpupinInfo(struct qemud_server *server ATTRIBUTE_UNUSED, cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); VIR_FREE(cpumaps); if (dom) virDomainFree(dom); @@ -1117,11 +1111,10 @@ no_memory: } static int -remoteDispatchDomainGetVcpus(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainGetVcpus(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_get_vcpus_args *args, remote_domain_get_vcpus_ret *ret) { @@ -1130,13 +1123,15 @@ remoteDispatchDomainGetVcpus(struct qemud_server *server ATTRIBUTE_UNUSED, unsigned char *cpumaps = NULL; int info_len, i; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; if (args->maxinfo > REMOTE_VCPUINFO_MAX) { @@ -1186,7 +1181,7 @@ remoteDispatchDomainGetVcpus(struct qemud_server *server ATTRIBUTE_UNUSED, cleanup: if (rv < 0) { - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); VIR_FREE(ret->info.info_val); } VIR_FREE(cpumaps); @@ -1201,11 +1196,10 @@ no_memory: } static int -remoteDispatchDomainMigratePrepare(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainMigratePrepare(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_migrate_prepare_args *args, remote_domain_migrate_prepare_ret *ret) { @@ -1215,8 +1209,10 @@ remoteDispatchDomainMigratePrepare(struct qemud_server *server ATTRIBUTE_UNUSED, char **uri_out; char *dname; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } @@ -1230,7 +1226,7 @@ remoteDispatchDomainMigratePrepare(struct qemud_server *server ATTRIBUTE_UNUSED, goto cleanup; } - if (virDomainMigratePrepare(conn, &cookie, &cookielen, + if (virDomainMigratePrepare(priv->conn, &cookie, &cookielen, uri_in, uri_out, args->flags, dname, args->resource) < 0) goto cleanup; @@ -1251,17 +1247,16 @@ remoteDispatchDomainMigratePrepare(struct qemud_server *server ATTRIBUTE_UNUSED, cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); VIR_FREE(uri_out); return rv; } static int -remoteDispatchDomainMigratePrepare2(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainMigratePrepare2(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_migrate_prepare2_args *args, remote_domain_migrate_prepare2_ret *ret) { @@ -1271,8 +1266,10 @@ remoteDispatchDomainMigratePrepare2(struct qemud_server *server ATTRIBUTE_UNUSED char **uri_out; char *dname; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } @@ -1286,7 +1283,7 @@ remoteDispatchDomainMigratePrepare2(struct qemud_server *server ATTRIBUTE_UNUSED goto cleanup; } - if (virDomainMigratePrepare2(conn, &cookie, &cookielen, + if (virDomainMigratePrepare2(priv->conn, &cookie, &cookielen, uri_in, uri_out, args->flags, dname, args->resource, args->dom_xml) < 0) @@ -1303,31 +1300,27 @@ remoteDispatchDomainMigratePrepare2(struct qemud_server *server ATTRIBUTE_UNUSED cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); return rv; } static int -remoteDispatchDomainGetMemoryParameters(struct qemud_server *server - ATTRIBUTE_UNUSED, - struct qemud_client *client - ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header * - hdr ATTRIBUTE_UNUSED, - remote_error * rerr, - remote_domain_get_memory_parameters_args - * args, - remote_domain_get_memory_parameters_ret - * ret) +remoteDispatchDomainGetMemoryParameters(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_memory_parameters_args *args, + remote_domain_get_memory_parameters_ret *ret) { virDomainPtr dom = NULL; virTypedParameterPtr params = NULL; int nparams = args->nparams; unsigned int flags; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } @@ -1343,7 +1336,7 @@ remoteDispatchDomainGetMemoryParameters(struct qemud_server *server goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; if (virDomainGetMemoryParameters(dom, params, &nparams, flags) < 0) @@ -1367,7 +1360,7 @@ success: cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (dom) virDomainFree(dom); VIR_FREE(params); @@ -1375,26 +1368,22 @@ cleanup: } static int -remoteDispatchDomainGetBlkioParameters(struct qemud_server *server - ATTRIBUTE_UNUSED, - struct qemud_client *client - ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header * - hdr ATTRIBUTE_UNUSED, - remote_error * rerr, - remote_domain_get_blkio_parameters_args - * args, - remote_domain_get_blkio_parameters_ret - * ret) +remoteDispatchDomainGetBlkioParameters(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_blkio_parameters_args *args, + remote_domain_get_blkio_parameters_ret *ret) { virDomainPtr dom = NULL; virTypedParameterPtr params = NULL; int nparams = args->nparams; unsigned int flags; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } @@ -1410,7 +1399,7 @@ remoteDispatchDomainGetBlkioParameters(struct qemud_server *server goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; if (virDomainGetBlkioParameters(dom, params, &nparams, flags) < 0) @@ -1434,7 +1423,7 @@ success: cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); VIR_FREE(params); if (dom) virDomainFree(dom); @@ -1442,13 +1431,12 @@ cleanup: } static int -remoteDispatchNodeGetCPUStats (struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - remote_node_get_cpu_stats_args *args, - remote_node_get_cpu_stats_ret *ret) +remoteDispatchNodeGetCPUStats(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_node_get_cpu_stats_args *args, + remote_node_get_cpu_stats_ret *ret) { virCPUStatsPtr params = NULL; int i; @@ -1456,8 +1444,10 @@ remoteDispatchNodeGetCPUStats (struct qemud_server *server ATTRIBUTE_UNUSED, int nparams = args->nparams; unsigned int flags; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } @@ -1473,7 +1463,7 @@ remoteDispatchNodeGetCPUStats (struct qemud_server *server ATTRIBUTE_UNUSED, goto cleanup; } - if (virNodeGetCPUStats(conn, cpuNum, params, &nparams, flags) < 0) + if (virNodeGetCPUStats(priv->conn, cpuNum, params, &nparams, flags) < 0) goto cleanup; /* In this case, we need to send back the number of stats @@ -1503,7 +1493,7 @@ success: cleanup: if (rv < 0) { - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (ret->params.params_val) { for (i = 0; i < nparams; i++) VIR_FREE(ret->params.params_val[i].field); @@ -1519,13 +1509,12 @@ no_memory: } static int -remoteDispatchNodeGetMemoryStats (struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - remote_node_get_memory_stats_args *args, - remote_node_get_memory_stats_ret *ret) +remoteDispatchNodeGetMemoryStats(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_node_get_memory_stats_args *args, + remote_node_get_memory_stats_ret *ret) { virMemoryStatsPtr params = NULL; int i; @@ -1533,8 +1522,10 @@ remoteDispatchNodeGetMemoryStats (struct qemud_server *server ATTRIBUTE_UNUSED, int nparams = args->nparams; unsigned int flags; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } @@ -1550,7 +1541,7 @@ remoteDispatchNodeGetMemoryStats (struct qemud_server *server ATTRIBUTE_UNUSED, goto cleanup; } - if (virNodeGetMemoryStats(conn, cellNum, params, &nparams, flags) < 0) + if (virNodeGetMemoryStats(priv->conn, cellNum, params, &nparams, flags) < 0) goto cleanup; /* In this case, we need to send back the number of parameters @@ -1580,7 +1571,7 @@ success: cleanup: if (rv < 0) { - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (ret->params.params_val) { for (i = 0; i < nparams; i++) VIR_FREE(ret->params.params_val[i].field); @@ -1598,360 +1589,242 @@ no_memory: /*-------------------------------------------------------------*/ static int -remoteDispatchAuthList(struct qemud_server *server, - struct qemud_client *client, - virConnectPtr conn ATTRIBUTE_UNUSED, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - void *args ATTRIBUTE_UNUSED, +remoteDispatchAuthList(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_auth_list_ret *ret) { int rv = -1; + int auth = virNetServerClientGetAuth(client); + uid_t callerUid; + pid_t callerPid; + + /* If the client is root then we want to bypass the + * policykit auth to avoid root being denied if + * some piece of polkit isn't present/running + */ + if (auth == VIR_NET_SERVER_SERVICE_AUTH_POLKIT) { + if (virNetServerClientGetLocalIdentity(client, &callerUid, &callerPid) < 0) { + /* Don't do anything on error - it'll be validated at next + * phase of auth anyway */ + virResetLastError(); + } else if (callerUid == 0) { + char ident[100]; + rv = snprintf(ident, sizeof ident, "pid:%d,uid:%d", callerPid, callerUid); + if (rv > 0 || rv < sizeof ident) { + VIR_INFO("Bypass polkit auth for privileged client %s", + ident); + if (virNetServerClientSetIdentity(client, ident) < 0) + virResetLastError(); + else + auth = VIR_NET_SERVER_SERVICE_AUTH_NONE; + } + rv = -1; + } + } ret->types.types_len = 1; if (VIR_ALLOC_N(ret->types.types_val, ret->types.types_len) < 0) { virReportOOMError(); goto cleanup; } - virMutexLock(&server->lock); - virMutexLock(&client->lock); - virMutexUnlock(&server->lock); - ret->types.types_val[0] = client->auth; - virMutexUnlock(&client->lock); + + switch (auth) { + case VIR_NET_SERVER_SERVICE_AUTH_NONE: + ret->types.types_val[0] = REMOTE_AUTH_NONE; + break; + case VIR_NET_SERVER_SERVICE_AUTH_POLKIT: + ret->types.types_val[0] = REMOTE_AUTH_POLKIT; + break; + case VIR_NET_SERVER_SERVICE_AUTH_SASL: + ret->types.types_val[0] = REMOTE_AUTH_SASL; + break; + default: + ret->types.types_val[0] = REMOTE_AUTH_NONE; + } rv = 0; cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); return rv; } -#if HAVE_SASL /* * Initializes the SASL session in prepare for authentication * and gives the client a list of allowed mechanisms to choose - * - * XXX callbacks for stuff like password verification ? */ static int -remoteDispatchAuthSaslInit(struct qemud_server *server, - struct qemud_client *client, - virConnectPtr conn ATTRIBUTE_UNUSED, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - void *args ATTRIBUTE_UNUSED, +remoteDispatchAuthSaslInit(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_auth_sasl_init_ret *ret) { - const char *mechlist = NULL; - sasl_security_properties_t secprops; - int err; - virSocketAddr sa; - char *localAddr, *remoteAddr; + virNetSASLSessionPtr sasl = NULL; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - virMutexLock(&server->lock); - virMutexLock(&client->lock); - virMutexUnlock(&server->lock); + virMutexLock(&priv->lock); - VIR_DEBUG("Initialize SASL auth %d", client->fd); - if (client->auth != REMOTE_AUTH_SASL || - client->saslconn != NULL) { + VIR_DEBUG("Initialize SASL auth %d", virNetServerClientGetFD(client)); + if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_SASL || + priv->sasl != NULL) { VIR_ERROR(_("client tried invalid SASL init request")); goto authfail; } - /* Get local address in form IPADDR:PORT */ - sa.len = sizeof(sa.data.stor); - if (getsockname(client->fd, &sa.data.sa, &sa.len) < 0) { - char ebuf[1024]; - virNetError(VIR_ERR_INTERNAL_ERROR, - _("failed to get sock address: %s"), - virStrerror(errno, ebuf, sizeof ebuf)); - goto error; - } - if ((localAddr = virSocketFormatAddrFull(&sa, true, ";")) == NULL) - goto error; - - /* Get remote address in form IPADDR:PORT */ - sa.len = sizeof(sa.data.stor); - if (getpeername(client->fd, &sa.data.sa, &sa.len) < 0) { - char ebuf[1024]; - virNetError(VIR_ERR_INTERNAL_ERROR, _("failed to get peer address: %s"), - virStrerror(errno, ebuf, sizeof ebuf)); - VIR_FREE(localAddr); - goto error; - } - if ((remoteAddr = virSocketFormatAddrFull(&sa, true, ";")) == NULL) { - VIR_FREE(localAddr); - goto error; - } - - err = sasl_server_new("libvirt", - NULL, /* FQDN - just delegates to gethostname */ - NULL, /* User realm */ - localAddr, - remoteAddr, - NULL, /* XXX Callbacks */ - SASL_SUCCESS_DATA, - &client->saslconn); - VIR_FREE(localAddr); - VIR_FREE(remoteAddr); - if (err != SASL_OK) { - VIR_ERROR(_("sasl context setup failed %d (%s)"), - err, sasl_errstring(err, NULL, NULL)); - client->saslconn = NULL; + sasl = virNetSASLSessionNewServer(saslCtxt, + "libvirt", + virNetServerClientLocalAddrString(client), + virNetServerClientRemoteAddrString(client)); + if (!sasl) goto authfail; - } /* Inform SASL that we've got an external SSF layer from TLS */ - if (client->type == QEMUD_SOCK_TYPE_TLS) { - gnutls_cipher_algorithm_t cipher; - sasl_ssf_t ssf; - - cipher = gnutls_cipher_get(client->tlssession); - if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) { - VIR_ERROR(_("cannot get TLS cipher size")); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; + if (virNetServerClientHasTLSSession(client)) { + int ssf; + + if ((ssf = virNetServerClientGetTLSKeySize(client)) < 0) goto authfail; - } - ssf *= 8; /* tls key size is bytes, sasl wants bits */ - - err = sasl_setprop(client->saslconn, SASL_SSF_EXTERNAL, &ssf); - if (err != SASL_OK) { - VIR_ERROR(_("cannot set SASL external SSF %d (%s)"), - err, sasl_errstring(err, NULL, NULL)); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; + + ssf *= 8; /* key size is bytes, sasl wants bits */ + + VIR_DEBUG("Setting external SSF %d", ssf); + if (virNetSASLSessionExtKeySize(sasl, ssf) < 0) goto authfail; - } } - memset(&secprops, 0, sizeof secprops); - if (client->type == QEMUD_SOCK_TYPE_TLS || - client->type == QEMUD_SOCK_TYPE_UNIX) { + if (virNetServerClientIsSecure(client)) /* If we've got TLS or UNIX domain sock, we don't care about SSF */ - secprops.min_ssf = 0; - secprops.max_ssf = 0; - secprops.maxbufsize = 8192; - secprops.security_flags = 0; - } else { + virNetSASLSessionSecProps(sasl, 0, 0, true); + else /* Plain TCP, better get an SSF layer */ - secprops.min_ssf = 56; /* Good enough to require kerberos */ - secprops.max_ssf = 100000; /* Arbitrary big number */ - secprops.maxbufsize = 8192; - /* Forbid any anonymous or trivially crackable auth */ - secprops.security_flags = - SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; - } - - err = sasl_setprop(client->saslconn, SASL_SEC_PROPS, &secprops); - if (err != SASL_OK) { - VIR_ERROR(_("cannot set SASL security props %d (%s)"), - err, sasl_errstring(err, NULL, NULL)); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; - goto authfail; - } + virNetSASLSessionSecProps(sasl, + 56, /* Good enough to require kerberos */ + 100000, /* Arbitrary big number */ + false); /* No anonymous */ - err = sasl_listmech(client->saslconn, - NULL, /* Don't need to set user */ - "", /* Prefix */ - ",", /* Separator */ - "", /* Suffix */ - &mechlist, - NULL, - NULL); - if (err != SASL_OK) { - VIR_ERROR(_("cannot list SASL mechanisms %d (%s)"), - err, sasl_errdetail(client->saslconn)); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; - goto authfail; - } - VIR_DEBUG("Available mechanisms for client: '%s'", mechlist); - ret->mechlist = strdup(mechlist); - if (!ret->mechlist) { - VIR_ERROR(_("cannot allocate mechlist")); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; + if (!(ret->mechlist = virNetSASLSessionListMechanisms(sasl))) goto authfail; - } + VIR_DEBUG("Available mechanisms for client: '%s'", ret->mechlist); - virMutexUnlock(&client->lock); + priv->sasl = sasl; + virMutexUnlock(&priv->lock); return 0; authfail: - remoteDispatchAuthError(rerr); -error: - PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_SASL); - virMutexUnlock(&client->lock); + virResetLastError(); + virNetError(VIR_ERR_AUTH_FAILED, "%s", + _("authentication failed")); + virNetMessageSaveError(rerr); + PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", + virNetServerClientGetFD(client), REMOTE_AUTH_SASL); + virNetSASLSessionFree(sasl); + virMutexUnlock(&priv->lock); return -1; } - -/* We asked for an SSF layer, so sanity check that we actually - * got what we asked for +/* * Returns 0 if ok, -1 on error, -2 if rejected */ static int -remoteSASLCheckSSF(struct qemud_client *client, - remote_error *rerr) { - const void *val; - int err, ssf; - - if (client->type == QEMUD_SOCK_TYPE_TLS || - client->type == QEMUD_SOCK_TYPE_UNIX) - return 0; /* TLS or UNIX domain sockets trivially OK */ - - err = sasl_getprop(client->saslconn, SASL_SSF, &val); - if (err != SASL_OK) { - VIR_ERROR(_("cannot query SASL ssf on connection %d (%s)"), - err, sasl_errstring(err, NULL, NULL)); - remoteDispatchAuthError(rerr); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; - return -1; +remoteSASLFinish(virNetServerClientPtr client) +{ + const char *identity; + struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); + int ssf; + + /* TLS or UNIX domain sockets trivially OK */ + if (!virNetServerClientIsSecure(client)) { + if ((ssf = virNetSASLSessionGetKeySize(priv->sasl)) < 0) + goto error; + + VIR_DEBUG("negotiated an SSF of %d", ssf); + if (ssf < 56) { /* 56 is good for Kerberos */ + VIR_ERROR(_("negotiated SSF %d was not strong enough"), ssf); + return -2; + } } - ssf = *(const int *)val; - VIR_DEBUG("negotiated an SSF of %d", ssf); - if (ssf < 56) { /* 56 is good for Kerberos */ - VIR_ERROR(_("negotiated SSF %d was not strong enough"), ssf); - remoteDispatchAuthError(rerr); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; + + if (!(identity = virNetSASLSessionGetIdentity(priv->sasl))) return -2; - } - /* Only setup for read initially, because we're about to send an RPC - * reply which must be in plain text. When the next incoming RPC - * arrives, we'll switch on writes too - * - * cf qemudClientReadSASL in qemud.c - */ - client->saslSSF = QEMUD_SASL_SSF_READ; + if (!virNetSASLContextCheckIdentity(saslCtxt, identity)) + return -2; - /* We have a SSF !*/ - return 0; -} + if (virNetServerClientSetIdentity(client, identity) < 0) + goto error; -/* - * Returns 0 if ok, -1 on error, -2 if rejected - */ -static int -remoteSASLCheckAccess(struct qemud_server *server, - struct qemud_client *client, - remote_error *rerr) { - const void *val; - int err; - char **wildcards; - - err = sasl_getprop(client->saslconn, SASL_USERNAME, &val); - if (err != SASL_OK) { - VIR_ERROR(_("cannot query SASL username on connection %d (%s)"), - err, sasl_errstring(err, NULL, NULL)); - remoteDispatchAuthError(rerr); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; - return -1; - } - if (val == NULL) { - VIR_ERROR(_("no client username was found")); - remoteDispatchAuthError(rerr); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; - return -1; - } - VIR_DEBUG("SASL client username %s", (const char *)val); - client->saslUsername = strdup((const char*)val); - if (client->saslUsername == NULL) { - VIR_ERROR(_("out of memory copying username")); - remoteDispatchAuthError(rerr); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; - return -1; - } + virNetServerClientSetSASLSession(client, priv->sasl); + + VIR_DEBUG("Authentication successful %d", virNetServerClientGetFD(client)); + PROBE(CLIENT_AUTH_ALLOW, "fd=%d, auth=%d, username=%s", + virNetServerClientGetFD(client), REMOTE_AUTH_SASL, + virNetSASLSessionGetIdentity(priv->sasl)); - /* If the list is not set, allow any DN. */ - wildcards = server->saslUsernameWhitelist; - if (!wildcards) - return 0; /* No ACL, allow all */ + virNetSASLSessionFree(priv->sasl); + priv->sasl = NULL; - while (*wildcards) { - if (fnmatch(*wildcards, client->saslUsername, 0) == 0) - return 0; /* Allowed */ - wildcards++; - } + return 0; - /* Denied */ - VIR_ERROR(_("SASL client %s not allowed in whitelist"), client->saslUsername); - remoteDispatchAuthError(rerr); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; - return -2; +error: + return -1; } - /* * This starts the SASL authentication negotiation. */ static int -remoteDispatchAuthSaslStart(struct qemud_server *server, - struct qemud_client *client, - virConnectPtr conn ATTRIBUTE_UNUSED, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchAuthSaslStart(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret) { const char *serverout; - unsigned int serveroutlen; + size_t serveroutlen; int err; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - virMutexLock(&server->lock); - virMutexLock(&client->lock); - virMutexUnlock(&server->lock); + virMutexLock(&priv->lock); - VIR_DEBUG("Start SASL auth %d", client->fd); - if (client->auth != REMOTE_AUTH_SASL || - client->saslconn == NULL) { + VIR_DEBUG("Start SASL auth %d", virNetServerClientGetFD(client)); + if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_SASL || + priv->sasl == NULL) { VIR_ERROR(_("client tried invalid SASL start request")); goto authfail; } VIR_DEBUG("Using SASL mechanism %s. Data %d bytes, nil: %d", args->mech, args->data.data_len, args->nil); - err = sasl_server_start(client->saslconn, - args->mech, - /* NB, distinction of NULL vs "" is *critical* in SASL */ - args->nil ? NULL : args->data.data_val, - args->data.data_len, - &serverout, - &serveroutlen); - if (err != SASL_OK && - err != SASL_CONTINUE) { - VIR_ERROR(_("sasl start failed %d (%s)"), - err, sasl_errdetail(client->saslconn)); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; + err = virNetSASLSessionServerStart(priv->sasl, + args->mech, + /* NB, distinction of NULL vs "" is *critical* in SASL */ + args->nil ? NULL : args->data.data_val, + args->data.data_len, + &serverout, + &serveroutlen); + if (err != VIR_NET_SASL_COMPLETE && + err != VIR_NET_SASL_CONTINUE) goto authfail; - } + if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) { - VIR_ERROR(_("sasl start reply data too long %d"), serveroutlen); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; + VIR_ERROR(_("sasl start reply data too long %d"), (int)serveroutlen); goto authfail; } /* NB, distinction of NULL vs "" is *critical* in SASL */ if (serverout) { - if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0) { - virReportOOMError(); - remoteDispatchError(rerr); - goto error; - } + if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0) + goto authfail; memcpy(ret->data.data_val, serverout, serveroutlen); } else { ret->data.data_val = NULL; @@ -1960,100 +1833,94 @@ remoteDispatchAuthSaslStart(struct qemud_server *server, ret->data.data_len = serveroutlen; VIR_DEBUG("SASL return data %d bytes, nil; %d", ret->data.data_len, ret->nil); - if (err == SASL_CONTINUE) { + if (err == VIR_NET_SASL_CONTINUE) { ret->complete = 0; } else { /* Check username whitelist ACL */ - if ((err = remoteSASLCheckAccess(server, client, rerr)) < 0 || - (err = remoteSASLCheckSSF(client, rerr)) < 0) { + if ((err = remoteSASLFinish(client)) < 0) { if (err == -2) goto authdeny; else goto authfail; } - VIR_DEBUG("Authentication successful %d", client->fd); - PROBE(CLIENT_AUTH_ALLOW, "fd=%d, auth=%d, username=%s", - client->fd, REMOTE_AUTH_SASL, client->saslUsername); ret->complete = 1; - client->auth = REMOTE_AUTH_NONE; } - virMutexUnlock(&client->lock); + virMutexUnlock(&priv->lock); return 0; authfail: - PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_SASL); - remoteDispatchAuthError(rerr); + PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", + virNetServerClientGetFD(client), REMOTE_AUTH_SASL); goto error; authdeny: PROBE(CLIENT_AUTH_DENY, "fd=%d, auth=%d, username=%s", - client->fd, REMOTE_AUTH_SASL, client->saslUsername); + virNetServerClientGetFD(client), REMOTE_AUTH_SASL, + virNetSASLSessionGetIdentity(priv->sasl)); goto error; error: - virMutexUnlock(&client->lock); + virNetSASLSessionFree(priv->sasl); + priv->sasl = NULL; + virResetLastError(); + virNetError(VIR_ERR_AUTH_FAILED, "%s", + _("authentication failed")); + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); return -1; } static int -remoteDispatchAuthSaslStep(struct qemud_server *server, - struct qemud_client *client, - virConnectPtr conn ATTRIBUTE_UNUSED, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchAuthSaslStep(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret) { const char *serverout; - unsigned int serveroutlen; + size_t serveroutlen; int err; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - virMutexLock(&server->lock); - virMutexLock(&client->lock); - virMutexUnlock(&server->lock); - VIR_DEBUG("Step SASL auth %d", client->fd); - if (client->auth != REMOTE_AUTH_SASL || - client->saslconn == NULL) { + virMutexLock(&priv->lock); + + VIR_DEBUG("Step SASL auth %d", virNetServerClientGetFD(client)); + if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_SASL || + priv->sasl == NULL) { VIR_ERROR(_("client tried invalid SASL start request")); goto authfail; } - VIR_DEBUG("Using SASL Data %d bytes, nil: %d", + VIR_DEBUG("Step using SASL Data %d bytes, nil: %d", args->data.data_len, args->nil); - err = sasl_server_step(client->saslconn, - /* NB, distinction of NULL vs "" is *critical* in SASL */ - args->nil ? NULL : args->data.data_val, - args->data.data_len, - &serverout, - &serveroutlen); - if (err != SASL_OK && - err != SASL_CONTINUE) { - VIR_ERROR(_("sasl step failed %d (%s)"), - err, sasl_errdetail(client->saslconn)); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; + err = virNetSASLSessionServerStep(priv->sasl, + /* NB, distinction of NULL vs "" is *critical* in SASL */ + args->nil ? NULL : args->data.data_val, + args->data.data_len, + &serverout, + &serveroutlen); + if (err != VIR_NET_SASL_COMPLETE && + err != VIR_NET_SASL_CONTINUE) goto authfail; - } if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) { VIR_ERROR(_("sasl step reply data too long %d"), - serveroutlen); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; + (int)serveroutlen); goto authfail; } /* NB, distinction of NULL vs "" is *critical* in SASL */ if (serverout) { - if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0) { - virReportOOMError(); - remoteDispatchError(rerr); - goto error; - } + if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0) + goto authfail; memcpy(ret->data.data_val, serverout, serveroutlen); } else { ret->data.data_val = NULL; @@ -2062,100 +1929,54 @@ remoteDispatchAuthSaslStep(struct qemud_server *server, ret->data.data_len = serveroutlen; VIR_DEBUG("SASL return data %d bytes, nil; %d", ret->data.data_len, ret->nil); - if (err == SASL_CONTINUE) { + if (err == VIR_NET_SASL_CONTINUE) { ret->complete = 0; } else { /* Check username whitelist ACL */ - if ((err = remoteSASLCheckAccess(server, client, rerr)) < 0 || - (err = remoteSASLCheckSSF(client, rerr)) < 0) { + if ((err = remoteSASLFinish(client)) < 0) { if (err == -2) goto authdeny; else goto authfail; } - VIR_DEBUG("Authentication successful %d", client->fd); - PROBE(CLIENT_AUTH_ALLOW, "fd=%d, auth=%d, username=%s", - client->fd, REMOTE_AUTH_SASL, client->saslUsername); ret->complete = 1; - client->auth = REMOTE_AUTH_NONE; } - virMutexUnlock(&client->lock); + virMutexUnlock(&priv->lock); return 0; authfail: - PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_SASL); - remoteDispatchAuthError(rerr); + PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", + virNetServerClientGetFD(client), REMOTE_AUTH_SASL); goto error; authdeny: PROBE(CLIENT_AUTH_DENY, "fd=%d, auth=%d, username=%s", - client->fd, REMOTE_AUTH_SASL, client->saslUsername); + virNetServerClientGetFD(client), REMOTE_AUTH_SASL, + virNetSASLSessionGetIdentity(priv->sasl)); goto error; error: - virMutexUnlock(&client->lock); - return -1; -} - - -#else /* HAVE_SASL */ -static int -remoteDispatchAuthSaslInit(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn ATTRIBUTE_UNUSED, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - void *args ATTRIBUTE_UNUSED, - remote_auth_sasl_init_ret *ret ATTRIBUTE_UNUSED) -{ - VIR_ERROR(_("client tried unsupported SASL init request")); - PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_SASL); - remoteDispatchAuthError(rerr); - return -1; -} - -static int -remoteDispatchAuthSaslStart(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn ATTRIBUTE_UNUSED, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - remote_auth_sasl_start_args *args ATTRIBUTE_UNUSED, - remote_auth_sasl_start_ret *ret ATTRIBUTE_UNUSED) -{ - VIR_ERROR(_("client tried unsupported SASL start request")); - PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_SASL); - remoteDispatchAuthError(rerr); + virNetSASLSessionFree(priv->sasl); + priv->sasl = NULL; + virResetLastError(); + virNetError(VIR_ERR_AUTH_FAILED, "%s", + _("authentication failed")); + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); return -1; } -static int -remoteDispatchAuthSaslStep(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn ATTRIBUTE_UNUSED, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - remote_auth_sasl_step_args *args ATTRIBUTE_UNUSED, - remote_auth_sasl_step_ret *ret ATTRIBUTE_UNUSED) -{ - VIR_ERROR(_("client tried unsupported SASL step request")); - PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_SASL); - remoteDispatchAuthError(rerr); - return -1; -} -#endif /* HAVE_SASL */ #if HAVE_POLKIT1 static int -remoteDispatchAuthPolkit(struct qemud_server *server, - struct qemud_client *client, - virConnectPtr conn ATTRIBUTE_UNUSED, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - void *args ATTRIBUTE_UNUSED, +remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_auth_polkit_ret *ret) { pid_t callerPid = -1; @@ -2164,15 +1985,14 @@ remoteDispatchAuthPolkit(struct qemud_server *server, int status = -1; char pidbuf[50]; char ident[100]; - int rv; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); memset(ident, 0, sizeof ident); - virMutexLock(&server->lock); - virMutexLock(&client->lock); - virMutexUnlock(&server->lock); - - action = client->readonly ? + virMutexLock(&priv->lock); + action = virNetServerClientGetReadonly(client) ? "org.libvirt.unix.monitor" : "org.libvirt.unix.manage"; @@ -2184,14 +2004,13 @@ remoteDispatchAuthPolkit(struct qemud_server *server, NULL }; - VIR_DEBUG("Start PolicyKit auth %d", client->fd); - if (client->auth != REMOTE_AUTH_POLKIT) { + VIR_DEBUG("Start PolicyKit auth %d", virNetServerClientGetFD(client)); + if (virNetServerClientGetAuth(client) != VIR_NET_SERVER_SERVICE_AUTH_POLKIT) { VIR_ERROR(_("client tried invalid PolicyKit init request")); goto authfail; } - if (qemudGetSocketIdentity(client->fd, &callerUid, &callerPid) < 0) { - VIR_ERROR(_("cannot get peer socket identity")); + if (virNetServerClientGetLocalIdentity(client, &callerUid, &callerPid) < 0) { goto authfail; } @@ -2221,37 +2040,40 @@ remoteDispatchAuthPolkit(struct qemud_server *server, goto authdeny; } PROBE(CLIENT_AUTH_ALLOW, "fd=%d, auth=%d, username=%s", - client->fd, REMOTE_AUTH_POLKIT, (char *)ident); + virNetServerClientGetFD(client), REMOTE_AUTH_POLKIT, ident); VIR_INFO("Policy allowed action %s from pid %d, uid %d", action, callerPid, callerUid); ret->complete = 1; - client->auth = REMOTE_AUTH_NONE; - virMutexUnlock(&client->lock); + virNetServerClientSetIdentity(client, ident); + virMutexUnlock(&priv->lock); + return 0; +error: + virResetLastError(); + virNetError(VIR_ERR_AUTH_FAILED, "%s", + _("authentication failed")); + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return -1; + authfail: - PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_POLKIT); + PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", + virNetServerClientGetFD(client), REMOTE_AUTH_POLKIT); goto error; authdeny: PROBE(CLIENT_AUTH_DENY, "fd=%d, auth=%d, username=%s", - client->fd, REMOTE_AUTH_POLKIT, (char *)ident); + virNetServerClientGetFD(client), REMOTE_AUTH_POLKIT, (char *)ident); goto error; - -error: - remoteDispatchAuthError(rerr); - virMutexUnlock(&client->lock); - return -1; } #elif HAVE_POLKIT0 static int -remoteDispatchAuthPolkit(struct qemud_server *server, - struct qemud_client *client, - virConnectPtr conn ATTRIBUTE_UNUSED, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - void *args ATTRIBUTE_UNUSED, +remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_auth_polkit_ret *ret) { pid_t callerPid; @@ -2264,7 +2086,9 @@ remoteDispatchAuthPolkit(struct qemud_server *server, DBusError err; const char *action; char ident[100]; - int rv; + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); memset(ident, 0, sizeof ident); @@ -2276,13 +2100,13 @@ remoteDispatchAuthPolkit(struct qemud_server *server, "org.libvirt.unix.monitor" : "org.libvirt.unix.manage"; - VIR_DEBUG("Start PolicyKit auth %d", client->fd); + VIR_DEBUG("Start PolicyKit auth %d", virNetServerClientGetFD(client)); if (client->auth != REMOTE_AUTH_POLKIT) { VIR_ERROR(_("client tried invalid PolicyKit init request")); goto authfail; } - if (qemudGetSocketIdentity(client->fd, &callerUid, &callerPid) < 0) { + if (qemudGetSocketIdentity(virNetServerClientGetFD(client), &callerUid, &callerPid) < 0) { VIR_ERROR(_("cannot get peer socket identity")); goto authfail; } @@ -2352,7 +2176,7 @@ remoteDispatchAuthPolkit(struct qemud_server *server, goto authdeny; } PROBE(CLIENT_AUTH_ALLOW, "fd=%d, auth=%d, username=%s", - client->fd, REMOTE_AUTH_POLKIT, ident); + virNetServerClientGetFD(client), REMOTE_AUTH_POLKIT, ident); VIR_INFO("Policy allowed action %s from pid %d, uid %d, result %s", action, callerPid, callerUid, polkit_result_to_string_representation(pkresult)); @@ -2362,34 +2186,38 @@ remoteDispatchAuthPolkit(struct qemud_server *server, virMutexUnlock(&client->lock); return 0; +error: + virResetLastError(); + virNetError(VIR_ERR_AUTH_FAILED, "%s", + _("authentication failed")); + virNetMessageSaveError(rerr); + virMutexUnlock(&client->lock); + return -1; + authfail: - PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_POLKIT); + PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", + virNetServerClientGetFD(client), REMOTE_AUTH_POLKIT); goto error; authdeny: PROBE(CLIENT_AUTH_DENY, "fd=%d, auth=%d, username=%s", - client->fd, REMOTE_AUTH_POLKIT, ident); + virNetServerClientGetFD(client), REMOTE_AUTH_POLKIT, ident); goto error; - -error: - remoteDispatchAuthError(rerr); - virMutexUnlock(&client->lock); - return -1; } #else /* !HAVE_POLKIT0 & !HAVE_POLKIT1*/ static int -remoteDispatchAuthPolkit(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn ATTRIBUTE_UNUSED, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - void *args ATTRIBUTE_UNUSED, +remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_auth_polkit_ret *ret ATTRIBUTE_UNUSED) { VIR_ERROR(_("client tried unsupported PolicyKit init request")); - remoteDispatchAuthError(rerr); + virNetError(VIR_ERR_AUTH_FAILED, "%s", + _("authentication failed")); + virNetMessageSaveError(rerr); return -1; } #endif /* HAVE_POLKIT1 */ @@ -2400,24 +2228,25 @@ remoteDispatchAuthPolkit(struct qemud_server *server ATTRIBUTE_UNUSED, **************************************************************/ static int -remoteDispatchNodeDeviceGetParent(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchNodeDeviceGetParent(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_node_device_get_parent_args *args, remote_node_device_get_parent_ret *ret) { virNodeDevicePtr dev = NULL; const char *parent = NULL; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (!(dev = virNodeDeviceLookupByName(conn, args->name))) + if (!(dev = virNodeDeviceLookupByName(priv->conn, args->name))) goto cleanup; parent = virNodeDeviceGetParent(dev); @@ -2443,7 +2272,7 @@ remoteDispatchNodeDeviceGetParent(struct qemud_server *server ATTRIBUTE_UNUSED, cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (dev) virNodeDeviceFree(dev); return rv; @@ -2454,148 +2283,124 @@ cleanup: * Register / deregister events ***************************/ static int -remoteDispatchDomainEventsRegister(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr ATTRIBUTE_UNUSED, - void *args ATTRIBUTE_UNUSED, +remoteDispatchDomainEventsRegister(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, remote_domain_events_register_ret *ret ATTRIBUTE_UNUSED) { int callbackID; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] != -1) { + virMutexLock(&priv->lock); + + if (priv->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] != -1) { virNetError(VIR_ERR_INTERNAL_ERROR, _("domain event %d already registered"), VIR_DOMAIN_EVENT_ID_LIFECYCLE); goto cleanup; } - if ((callbackID = virConnectDomainEventRegisterAny(conn, + if ((callbackID = virConnectDomainEventRegisterAny(priv->conn, NULL, VIR_DOMAIN_EVENT_ID_LIFECYCLE, VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), client, NULL)) < 0) goto cleanup; - client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] = callbackID; + priv->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] = callbackID; rv = 0; cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); return rv; } static int -remoteDispatchDomainEventsDeregister(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr ATTRIBUTE_UNUSED, - void *args ATTRIBUTE_UNUSED, +remoteDispatchDomainEventsDeregister(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, remote_domain_events_deregister_ret *ret ATTRIBUTE_UNUSED) { int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] < 0) { + virMutexLock(&priv->lock); + + if (priv->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] < 0) { virNetError(VIR_ERR_INTERNAL_ERROR, _("domain event %d not registered"), VIR_DOMAIN_EVENT_ID_LIFECYCLE); goto cleanup; } - if (virConnectDomainEventDeregisterAny(conn, - client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE]) < 0) + if (virConnectDomainEventDeregisterAny(priv->conn, + priv->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE]) < 0) goto cleanup; - client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] = -1; + priv->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] = -1; + rv = 0; cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); return rv; } static void -remoteDispatchDomainEventSend(struct qemud_client *client, +remoteDispatchDomainEventSend(virNetServerClientPtr client, + virNetServerProgramPtr program, int procnr, xdrproc_t proc, void *data) { - struct qemud_client_message *msg = NULL; - XDR xdr; - unsigned int len; + virNetMessagePtr msg; - if (VIR_ALLOC(msg) < 0) + if (!(msg = virNetMessageNew())) return; - msg->hdr.prog = REMOTE_PROGRAM; - msg->hdr.vers = REMOTE_PROTOCOL_VERSION; - msg->hdr.proc = procnr; - msg->hdr.type = REMOTE_MESSAGE; - msg->hdr.serial = 1; - msg->hdr.status = REMOTE_OK; + msg->header.prog = virNetServerProgramGetID(program); + msg->header.vers = virNetServerProgramGetVersion(program); + msg->header.proc = procnr; + msg->header.type = VIR_NET_MESSAGE; + msg->header.serial = 1; + msg->header.status = VIR_NET_OK; - if (remoteEncodeClientMessageHeader(msg) < 0) + if (virNetMessageEncodeHeader(msg) < 0) goto cleanup; - /* Serialise the return header and event. */ - xdrmem_create(&xdr, - msg->buffer, - msg->bufferLength, - XDR_ENCODE); - - /* Skip over the header we just wrote */ - if (xdr_setpos(&xdr, msg->bufferOffset) == 0) - goto xdr_cleanup; - - if (!(proc)(&xdr, data)) { - VIR_WARN("Failed to serialize domain event %d", procnr); - goto xdr_cleanup; - } - - /* Update length word to include payload*/ - len = msg->bufferOffset = xdr_getpos(&xdr); - if (xdr_setpos(&xdr, 0) == 0) - goto xdr_cleanup; - - if (!xdr_u_int(&xdr, &len)) - goto xdr_cleanup; - - /* Send it. */ - msg->async = 1; - msg->bufferLength = len; - msg->bufferOffset = 0; + if (virNetMessageEncodePayload(msg, proc, data) < 0) + goto cleanup; - VIR_DEBUG("Queue event %d %d", procnr, msg->bufferLength); - qemudClientMessageQueuePush(&client->tx, msg); - qemudUpdateClientEvent(client); + VIR_DEBUG("Queue event %d %Zu", procnr, msg->bufferLength); + virNetServerClientSendMessage(client, msg); - xdr_destroy(&xdr); return; -xdr_cleanup: - xdr_destroy(&xdr); cleanup: - VIR_FREE(msg); + virNetMessageFree(msg); } static int -remoteDispatchSecretGetValue(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchSecretGetValue(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_secret_get_value_args *args, remote_secret_get_value_ret *ret) { @@ -2603,13 +2408,15 @@ remoteDispatchSecretGetValue(struct qemud_server *server ATTRIBUTE_UNUSED, size_t value_size; unsigned char *value; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (!(secret = get_nonnull_secret(conn, args->secret))) + if (!(secret = get_nonnull_secret(priv->conn, args->secret))) goto cleanup; if (!(value = virSecretGetValue(secret, &value_size, args->flags))) @@ -2622,30 +2429,31 @@ remoteDispatchSecretGetValue(struct qemud_server *server ATTRIBUTE_UNUSED, cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (secret) virSecretFree(secret); return rv; } static int -remoteDispatchDomainGetState(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainGetState(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_get_state_args *args, remote_domain_get_state_ret *ret) { virDomainPtr dom = NULL; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; if (virDomainGetState(dom, &ret->state, &ret->reason, args->flags) < 0) @@ -2655,116 +2463,125 @@ remoteDispatchDomainGetState(struct qemud_server *server ATTRIBUTE_UNUSED, cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (dom) virDomainFree(dom); return rv; } static int -remoteDispatchDomainEventsRegisterAny(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr ATTRIBUTE_UNUSED, - remote_domain_events_register_any_args *args, - void *ret ATTRIBUTE_UNUSED) +remoteDispatchDomainEventsRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, + remote_domain_events_register_any_args *args) { int callbackID; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } + virMutexLock(&priv->lock); + if (args->eventID >= VIR_DOMAIN_EVENT_ID_LAST || args->eventID < 0) { virNetError(VIR_ERR_INTERNAL_ERROR, _("unsupported event ID %d"), args->eventID); goto cleanup; } - if (client->domainEventCallbackID[args->eventID] != -1) { + if (priv->domainEventCallbackID[args->eventID] != -1) { virNetError(VIR_ERR_INTERNAL_ERROR, _("domain event %d already registered"), args->eventID); goto cleanup; } - if ((callbackID = virConnectDomainEventRegisterAny(conn, + if ((callbackID = virConnectDomainEventRegisterAny(priv->conn, NULL, args->eventID, domainEventCallbacks[args->eventID], client, NULL)) < 0) goto cleanup; - client->domainEventCallbackID[args->eventID] = callbackID; + priv->domainEventCallbackID[args->eventID] = callbackID; rv = 0; cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); return rv; } static int -remoteDispatchDomainEventsDeregisterAny(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr ATTRIBUTE_UNUSED, - remote_domain_events_deregister_any_args *args, - void *ret ATTRIBUTE_UNUSED) +remoteDispatchDomainEventsDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, + remote_domain_events_deregister_any_args *args) { int callbackID = -1; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } + virMutexLock(&priv->lock); + if (args->eventID >= VIR_DOMAIN_EVENT_ID_LAST || args->eventID < 0) { virNetError(VIR_ERR_INTERNAL_ERROR, _("unsupported event ID %d"), args->eventID); goto cleanup; } - if ((callbackID = client->domainEventCallbackID[args->eventID]) < 0) { + callbackID = priv->domainEventCallbackID[args->eventID]; + if (callbackID < 0) { virNetError(VIR_ERR_INTERNAL_ERROR, _("domain event %d not registered"), args->eventID); goto cleanup; } - if (virConnectDomainEventDeregisterAny(conn, callbackID) < 0) + if (virConnectDomainEventDeregisterAny(priv->conn, callbackID) < 0) goto cleanup; - client->domainEventCallbackID[args->eventID] = -1; + priv->domainEventCallbackID[args->eventID] = -1; + rv = 0; cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); return rv; } static int -qemuDispatchMonitorCommand(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +qemuDispatchMonitorCommand(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, qemu_monitor_command_args *args, qemu_monitor_command_ret *ret) { virDomainPtr dom = NULL; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; if (virDomainQemuMonitorCommand(dom, args->cmd, &ret->result, @@ -2775,23 +2592,18 @@ qemuDispatchMonitorCommand(struct qemud_server *server ATTRIBUTE_UNUSED, cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (dom) virDomainFree(dom); return rv; } -#include "remote_dispatch_bodies.h" -#include "qemu_dispatch_bodies.h" - - static int -remoteDispatchDomainMigrateBegin3(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainMigrateBegin3(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_migrate_begin3_args *args, remote_domain_migrate_begin3_ret *ret) { @@ -2802,13 +2614,15 @@ remoteDispatchDomainMigrateBegin3(struct qemud_server *server ATTRIBUTE_UNUSED, char *cookieout = NULL; int cookieoutlen = 0; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; xmlin = args->xmlin == NULL ? NULL : *args->xmlin; @@ -2830,7 +2644,7 @@ remoteDispatchDomainMigrateBegin3(struct qemud_server *server ATTRIBUTE_UNUSED, cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (dom) virDomainFree(dom); return rv; @@ -2838,11 +2652,10 @@ cleanup: static int -remoteDispatchDomainMigratePrepare3(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainMigratePrepare3(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_migrate_prepare3_args *args, remote_domain_migrate_prepare3_ret *ret) { @@ -2852,8 +2665,10 @@ remoteDispatchDomainMigratePrepare3(struct qemud_server *server ATTRIBUTE_UNUSED char **uri_out; char *dname; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } @@ -2867,7 +2682,7 @@ remoteDispatchDomainMigratePrepare3(struct qemud_server *server ATTRIBUTE_UNUSED goto cleanup; } - if (virDomainMigratePrepare3(conn, + if (virDomainMigratePrepare3(priv->conn, args->cookie_in.cookie_in_val, args->cookie_in.cookie_in_len, &cookieout, &cookieoutlen, @@ -2887,18 +2702,18 @@ remoteDispatchDomainMigratePrepare3(struct qemud_server *server ATTRIBUTE_UNUSED cleanup: if (rv < 0) { - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); VIR_FREE(uri_out); } return rv; } + static int -remoteDispatchDomainMigratePerform3(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainMigratePerform3(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_migrate_perform3_args *args, remote_domain_migrate_perform3_ret *ret) { @@ -2910,13 +2725,15 @@ remoteDispatchDomainMigratePerform3(struct qemud_server *server ATTRIBUTE_UNUSED char *cookieout = NULL; int cookieoutlen = 0; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; xmlin = args->xmlin == NULL ? NULL : *args->xmlin; @@ -2941,7 +2758,7 @@ remoteDispatchDomainMigratePerform3(struct qemud_server *server ATTRIBUTE_UNUSED cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (dom) virDomainFree(dom); return rv; @@ -2949,11 +2766,10 @@ cleanup: static int -remoteDispatchDomainMigrateFinish3(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainMigrateFinish3(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_migrate_finish3_args *args, remote_domain_migrate_finish3_ret *ret) { @@ -2963,8 +2779,10 @@ remoteDispatchDomainMigrateFinish3(struct qemud_server *server ATTRIBUTE_UNUSED, char *uri; char *dconnuri; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } @@ -2972,7 +2790,7 @@ remoteDispatchDomainMigrateFinish3(struct qemud_server *server ATTRIBUTE_UNUSED, uri = args->uri == NULL ? NULL : *args->uri; dconnuri = args->dconnuri == NULL ? NULL : *args->dconnuri; - if (!(dom = virDomainMigrateFinish3(conn, args->dname, + if (!(dom = virDomainMigrateFinish3(priv->conn, args->dname, args->cookie_in.cookie_in_val, args->cookie_in.cookie_in_len, &cookieout, &cookieoutlen, @@ -2992,7 +2810,7 @@ remoteDispatchDomainMigrateFinish3(struct qemud_server *server ATTRIBUTE_UNUSED, cleanup: if (rv < 0) { - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); VIR_FREE(cookieout); } if (dom) @@ -3002,23 +2820,23 @@ cleanup: static int -remoteDispatchDomainMigrateConfirm3(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - remote_domain_migrate_confirm3_args *args, - void *ret ATTRIBUTE_UNUSED) +remoteDispatchDomainMigrateConfirm3(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_migrate_confirm3_args *args) { virDomainPtr dom = NULL; int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); - if (!conn) { + if (!priv->conn) { virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); goto cleanup; } - if (!(dom = get_nonnull_domain(conn, args->dom))) + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) goto cleanup; if (virDomainMigrateConfirm3(dom, @@ -3031,7 +2849,7 @@ remoteDispatchDomainMigrateConfirm3(struct qemud_server *server ATTRIBUTE_UNUSED cleanup: if (rv < 0) - remoteDispatchError(rerr); + virNetMessageSaveError(rerr); if (dom) virDomainFree(dom); return rv; diff --git a/daemon/remote.h b/daemon/remote.h index 1eb8386..c9bf5d7 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -24,62 +24,20 @@ #ifndef __LIBVIRTD_REMOTE_H__ # define __LIBVIRTD_REMOTE_H__ +# include "remote_protocol.h" +# include "rpc/virnetserverprogram.h" +# include "rpc/virnetserverclient.h" -# include "libvirtd.h" -typedef union { -# include "remote_dispatch_args.h" -} dispatch_args; -verify(sizeof(dispatch_args) > 0); - -typedef union { -# include "remote_dispatch_ret.h" -} dispatch_ret; -verify(sizeof(dispatch_ret) > 0); - -typedef union { -# include "qemu_dispatch_args.h" -} qemu_dispatch_args; -verify(sizeof(qemu_dispatch_args) > 0); - -typedef union { -# include "qemu_dispatch_ret.h" -} qemu_dispatch_ret; -verify(sizeof(qemu_dispatch_ret) > 0); - - - -/** - * When the RPC handler is called: - * - * - Server object is unlocked - * - Client object is unlocked - * - * Both must be locked before use. Server lock must - * be held before attempting to lock client. - * - * Without any locking, it is safe to use: - * - * 'conn', 'rerr', 'args and 'ret' - */ -typedef int (*dispatch_fn) (struct qemud_server *server, - struct qemud_client *client, - virConnectPtr conn, - remote_message_header *hdr, - remote_error *err, - dispatch_args *args, - dispatch_ret *ret); - -typedef struct { - dispatch_fn fn; - xdrproc_t args_filter; - xdrproc_t ret_filter; -} dispatch_data; - - -const dispatch_data const *remoteGetDispatchData(int proc); -const dispatch_data const *qemuGetDispatchData(int proc); +extern virNetServerProgramProc remoteProcs[]; +extern size_t remoteNProcs; +extern virNetServerProgramErrorHander remoteErr; +extern virNetServerProgramProc qemuProcs[]; +extern size_t qemuNProcs; +extern virNetServerProgramErrorHander qemuErr; +int remoteClientInitHook(virNetServerPtr srv, + virNetServerClientPtr client); #endif /* __LIBVIRTD_REMOTE_H__ */ diff --git a/daemon/stream.c b/daemon/stream.c index 48085da..89ad42f 100644 --- a/daemon/stream.c +++ b/daemon/stream.c @@ -24,32 +24,57 @@ #include <config.h> #include "stream.h" +#include "remote.h" #include "memory.h" -#include "dispatch.h" #include "logging.h" +#include "virnetserverclient.h" #include "virterror_internal.h" #define VIR_FROM_THIS VIR_FROM_STREAMS +#define virNetError(code, ...) \ + virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +struct daemonClientStream { + daemonClientPrivatePtr priv; + + virNetServerProgramPtr prog; + + virStreamPtr st; + int procedure; + int serial; + + unsigned int recvEOF : 1; + unsigned int closed : 1; + + int filterID; + + virNetMessagePtr rx; + int tx; + + daemonClientStreamPtr next; +}; + static int -remoteStreamHandleWrite(struct qemud_client *client, - struct qemud_client_stream *stream); +daemonStreamHandleWrite(virNetServerClientPtr client, + daemonClientStream *stream); static int -remoteStreamHandleRead(struct qemud_client *client, - struct qemud_client_stream *stream); +daemonStreamHandleRead(virNetServerClientPtr client, + daemonClientStream *stream); static int -remoteStreamHandleFinish(struct qemud_client *client, - struct qemud_client_stream *stream, - struct qemud_client_message *msg); +daemonStreamHandleFinish(virNetServerClientPtr client, + daemonClientStream *stream, + virNetMessagePtr msg); static int -remoteStreamHandleAbort(struct qemud_client *client, - struct qemud_client_stream *stream, - struct qemud_client_message *msg); +daemonStreamHandleAbort(virNetServerClientPtr client, + daemonClientStream *stream, + virNetMessagePtr msg); static void -remoteStreamUpdateEvents(struct qemud_client_stream *stream) +daemonStreamUpdateEvents(daemonClientStream *stream) { int newEvents = 0; if (stream->rx) @@ -65,19 +90,15 @@ remoteStreamUpdateEvents(struct qemud_client_stream *stream) * Callback that gets invoked when a stream becomes writable/readable */ static void -remoteStreamEvent(virStreamPtr st, int events, void *opaque) +daemonStreamEvent(virStreamPtr st, int events, void *opaque) { - struct qemud_client *client = opaque; - struct qemud_client_stream *stream; + virNetServerClientPtr client = opaque; + daemonClientStream *stream; + daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client); - /* XXX sub-optimal - we really should be taking the server lock - * first, but we have no handle to the server object - * We're lucky to get away with it for now, due to this callback - * executing in the main thread, but this should really be fixed - */ - virMutexLock(&client->lock); + virMutexLock(&priv->lock); - stream = remoteFindClientStream(client, st); + stream = daemonFindClientStream(client, st); if (!stream) { VIR_WARN("event for client=%p stream st=%p, but missing stream state", client, st); @@ -88,9 +109,9 @@ remoteStreamEvent(virStreamPtr st, int events, void *opaque) VIR_DEBUG("st=%p events=%d", st, events); if (events & VIR_STREAM_EVENT_WRITABLE) { - if (remoteStreamHandleWrite(client, stream) < 0) { - remoteRemoveClientStream(client, stream); - qemudDispatchClientFailure(client); + if (daemonStreamHandleWrite(client, stream) < 0) { + daemonRemoveClientStream(client, stream); + virNetServerClientClose(client); goto cleanup; } } @@ -98,9 +119,9 @@ remoteStreamEvent(virStreamPtr st, int events, void *opaque) if (!stream->recvEOF && (events & (VIR_STREAM_EVENT_READABLE | VIR_STREAM_EVENT_HANGUP))) { events = events & ~(VIR_STREAM_EVENT_READABLE | VIR_STREAM_EVENT_HANGUP); - if (remoteStreamHandleRead(client, stream) < 0) { - remoteRemoveClientStream(client, stream); - qemudDispatchClientFailure(client); + if (daemonStreamHandleRead(client, stream) < 0) { + daemonRemoveClientStream(client, stream); + virNetServerClientClose(client); goto cleanup; } } @@ -108,30 +129,45 @@ remoteStreamEvent(virStreamPtr st, int events, void *opaque) if (!stream->closed && (events & (VIR_STREAM_EVENT_ERROR | VIR_STREAM_EVENT_HANGUP))) { int ret; - remote_error rerr; - memset(&rerr, 0, sizeof rerr); + virNetMessagePtr msg; + virNetMessageError rerr; + + memset(&rerr, 0, sizeof(rerr)); stream->closed = 1; virStreamEventRemoveCallback(stream->st); virStreamAbort(stream->st); if (events & VIR_STREAM_EVENT_HANGUP) - remoteDispatchFormatError(&rerr, "%s", _("stream had unexpected termination")); + virNetError(VIR_ERR_RPC, + "%s", _("stream had unexpected termination")); else - remoteDispatchFormatError(&rerr, "%s", _("stream had I/O failure")); - ret = remoteSerializeStreamError(client, &rerr, stream->procedure, stream->serial); - remoteRemoveClientStream(client, stream); + virNetError(VIR_ERR_RPC, + "%s", _("stream had I/O failure")); + + msg = virNetMessageNew(); + if (!msg) { + ret = -1; + } else { + ret = virNetServerProgramSendStreamError(remoteProgram, + client, + msg, + &rerr, + stream->procedure, + stream->serial); + } + daemonRemoveClientStream(client, stream); if (ret < 0) - qemudDispatchClientFailure(client); + virNetServerClientClose(client); goto cleanup; } if (stream->closed) { - remoteRemoveClientStream(client, stream); + daemonRemoveClientStream(client, stream); } else { - remoteStreamUpdateEvents(stream); + daemonStreamUpdateEvents(stream); } cleanup: - virMutexUnlock(&client->lock); + virMutexUnlock(&priv->lock); } @@ -144,90 +180,70 @@ cleanup: * -1 on fatal client error */ static int -remoteStreamFilter(struct qemud_client *client, - struct qemud_client_message *msg, void *opaque) +daemonStreamFilter(virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg, + void *opaque) { - struct qemud_client_stream *stream = opaque; - - if (msg->hdr.serial == stream->serial && - msg->hdr.proc == stream->procedure && - msg->hdr.type == REMOTE_STREAM) { - VIR_DEBUG("Incoming rx=%p serial=%d proc=%d status=%d", - stream->rx, msg->hdr.proc, msg->hdr.serial, msg->hdr.status); - - /* If there are queued packets, we need to queue all further - * messages, since they must be processed strictly in order. - * If there are no queued packets, then OK/ERROR messages - * should be processed immediately. Data packets are still - * queued to only be processed when the stream is marked as - * writable. - */ - if (stream->rx) { - qemudClientMessageQueuePush(&stream->rx, msg); - remoteStreamUpdateEvents(stream); - } else { - int ret = 0; - switch (msg->hdr.status) { - case REMOTE_OK: - ret = remoteStreamHandleFinish(client, stream, msg); - if (ret == 0) - qemudClientMessageRelease(client, msg); - break; - - case REMOTE_CONTINUE: - qemudClientMessageQueuePush(&stream->rx, msg); - remoteStreamUpdateEvents(stream); - break; - - case REMOTE_ERROR: - default: - ret = remoteStreamHandleAbort(client, stream, msg); - if (ret == 0) - qemudClientMessageRelease(client, msg); - break; - } - - if (ret < 0) - return -1; - } - return 1; - } - return 0; + daemonClientStream *stream = opaque; + int ret = 0; + + virMutexLock(&stream->priv->lock); + + if (msg->header.type != VIR_NET_STREAM) + goto cleanup; + + if (!virNetServerProgramMatches(stream->prog, msg)) + goto cleanup; + + if (msg->header.proc != stream->procedure || + msg->header.serial != stream->serial) + goto cleanup; + + VIR_DEBUG("Incoming rx=%p serial=%d proc=%d status=%d", + stream->rx, msg->header.proc, msg->header.serial, msg->header.status); + + virNetMessageQueuePush(&stream->rx, msg); + daemonStreamUpdateEvents(stream); + ret = 1; + +cleanup: + virMutexUnlock(&stream->priv->lock); + return ret; } /* * @conn: a connection object to associate the stream with - * @hdr: the method call to associate with the stram + * @header: the method call to associate with the stram * * Creates a new stream for this conn * * Returns a new stream object, or NULL upon OOM */ -struct qemud_client_stream * -remoteCreateClientStream(virConnectPtr conn, - remote_message_header *hdr) +daemonClientStream * +daemonCreateClientStream(virNetServerClientPtr client, + virStreamPtr st, + virNetServerProgramPtr prog, + virNetMessageHeaderPtr header) { - struct qemud_client_stream *stream; + daemonClientStream *stream; + daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client); - VIR_DEBUG("proc=%d serial=%d", hdr->proc, hdr->serial); + VIR_DEBUG("proc=%d serial=%d", header->proc, header->serial); if (VIR_ALLOC(stream) < 0) { virReportOOMError(); return NULL; } - stream->procedure = hdr->proc; - stream->serial = hdr->serial; - - stream->st = virStreamNew(conn, VIR_STREAM_NONBLOCK); - if (!stream->st) { - VIR_FREE(stream); - return NULL; - } + stream->priv = priv; + stream->prog = prog; + stream->procedure = header->proc; + stream->serial = header->serial; + stream->filterID = -1; + stream->st = st; - stream->filter.query = remoteStreamFilter; - stream->filter.opaque = stream; + virNetServerProgramRef(prog); return stream; } @@ -238,25 +254,36 @@ remoteCreateClientStream(virConnectPtr conn, * Frees the memory associated with this inactive client * stream */ -void remoteFreeClientStream(struct qemud_client *client, - struct qemud_client_stream *stream) +int daemonFreeClientStream(virNetServerClientPtr client, + daemonClientStream *stream) { - struct qemud_client_message *msg; + virNetMessagePtr msg; + int ret = 0; if (!stream) - return; + return 0; VIR_DEBUG("proc=%d serial=%d", stream->procedure, stream->serial); + virNetServerProgramFree(stream->prog); + msg = stream->rx; while (msg) { - struct qemud_client_message *tmp = msg->next; - qemudClientMessageRelease(client, msg); + virNetMessagePtr tmp = msg->next; + /* Send a dummy reply to free up 'msg' & unblock client rx */ + memset(msg, 0, sizeof(*msg)); + if (virNetServerClientSendMessage(client, msg) < 0) { + virNetServerClientMarkClose(client); + virNetMessageFree(msg); + ret = -1; + } msg = tmp; } virStreamFree(stream->st); VIR_FREE(stream); + + return ret; } @@ -264,33 +291,32 @@ void remoteFreeClientStream(struct qemud_client *client, * @client: a locked client to add the stream to * @stream: a stream to add */ -int remoteAddClientStream(struct qemud_client *client, - struct qemud_client_stream *stream, +int daemonAddClientStream(virNetServerClientPtr client, + daemonClientStream *stream, int transmit) { - struct qemud_client_stream *tmp = client->streams; - VIR_DEBUG("client=%p proc=%d serial=%d", client, stream->procedure, stream->serial); + if (stream->filterID != -1) { + VIR_WARN("Filter already added to client %p", client); + return -1; + } + if (virStreamEventAddCallback(stream->st, 0, - remoteStreamEvent, client, NULL) < 0) + daemonStreamEvent, client, NULL) < 0) return -1; - if (tmp) { - while (tmp->next) - tmp = tmp->next; - tmp->next = stream; - } else { - client->streams = stream; + if ((stream->filterID = virNetServerClientAddFilter(client, + daemonStreamFilter, + stream)) < 0) { + virStreamEventRemoveCallback(stream->st); + return -1; } - stream->filter.next = client->filters; - client->filters = &stream->filter; - if (transmit) stream->tx = 1; - remoteStreamUpdateEvents(stream); + daemonStreamUpdateEvents(stream); return 0; } @@ -305,11 +331,12 @@ int remoteAddClientStream(struct qemud_client *client, * * Returns a stream object matching the procedure+serial number, or NULL */ -struct qemud_client_stream * -remoteFindClientStream(struct qemud_client *client, +daemonClientStream * +daemonFindClientStream(virNetServerClientPtr client, virStreamPtr st) { - struct qemud_client_stream *stream = client->streams; + daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client); + daemonClientStream *stream = priv->streams; while (stream) { if (stream->st == st) @@ -330,26 +357,18 @@ remoteFindClientStream(struct qemud_client *client, * Returns 0 if the stream was removd, -1 if it doesn't exist */ int -remoteRemoveClientStream(struct qemud_client *client, - struct qemud_client_stream *stream) +daemonRemoveClientStream(virNetServerClientPtr client, + daemonClientStream *stream) { VIR_DEBUG("client=%p proc=%d serial=%d", client, stream->procedure, stream->serial); - - struct qemud_client_stream *curr = client->streams; - struct qemud_client_stream *prev = NULL; - struct qemud_client_filter *filter = NULL; - - if (client->filters == &stream->filter) { - client->filters = client->filters->next; - } else { - filter = client->filters; - while (filter) { - if (filter->next == &stream->filter) { - filter->next = filter->next->next; - break; - } - filter = filter->next; - } + daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client); + daemonClientStream *curr = priv->streams; + daemonClientStream *prev = NULL; + + if (stream->filterID != -1) { + virNetServerClientRemoveFilter(client, + stream->filterID); + stream->filterID = -1; } if (!stream->closed) { @@ -362,9 +381,8 @@ remoteRemoveClientStream(struct qemud_client *client, if (prev) prev->next = curr->next; else - client->streams = curr->next; - remoteFreeClientStream(client, stream); - return 0; + priv->streams = curr->next; + return daemonFreeClientStream(client, stream); } prev = curr; curr = curr->next; @@ -380,17 +398,15 @@ remoteRemoveClientStream(struct qemud_client *client, * 1 if message is still being processed */ static int -remoteStreamHandleWriteData(struct qemud_client *client, - struct qemud_client_stream *stream, - struct qemud_client_message *msg) +daemonStreamHandleWriteData(virNetServerClientPtr client, + daemonClientStream *stream, + virNetMessagePtr msg) { - remote_error rerr; int ret; - VIR_DEBUG("stream=%p proc=%d serial=%d len=%d offset=%d", - stream, msg->hdr.proc, msg->hdr.serial, msg->bufferLength, msg->bufferOffset); - - memset(&rerr, 0, sizeof rerr); + VIR_DEBUG("stream=%p proc=%d serial=%d len=%zu offset=%zu", + stream, msg->header.proc, msg->header.serial, + msg->bufferLength, msg->bufferOffset); ret = virStreamSend(stream->st, msg->buffer + msg->bufferOffset, @@ -402,14 +418,25 @@ remoteStreamHandleWriteData(struct qemud_client *client, /* Partial write, so indicate we have more todo later */ if (msg->bufferOffset < msg->bufferLength) return 1; + + /* A dummy 'send' just to free up 'msg' object */ + memset(msg, 0, sizeof(*msg)); + return virNetServerClientSendMessage(client, msg); } else if (ret == -2) { /* Blocking, so indicate we have more todo later */ return 1; } else { + virNetMessageError rerr; + + memset(&rerr, 0, sizeof(rerr)); + VIR_INFO("Stream send failed"); stream->closed = 1; - remoteDispatchError(&rerr); - return remoteSerializeReplyError(client, &rerr, &msg->hdr); + return virNetServerProgramSendReplyError(stream->prog, + client, + msg, + &rerr, + &msg->header); } return 0; @@ -419,38 +446,42 @@ remoteStreamHandleWriteData(struct qemud_client *client, /* * Process an finish handshake from the client. * - * Returns a REMOTE_OK confirmation if successful, or a REMOTE_ERROR + * Returns a VIR_NET_OK confirmation if successful, or a VIR_NET_ERROR * if there was a stream error * * Returns 0 if successfully sent RPC reply, -1 upon fatal error */ static int -remoteStreamHandleFinish(struct qemud_client *client, - struct qemud_client_stream *stream, - struct qemud_client_message *msg) +daemonStreamHandleFinish(virNetServerClientPtr client, + daemonClientStream *stream, + virNetMessagePtr msg) { - remote_error rerr; int ret; VIR_DEBUG("stream=%p proc=%d serial=%d", - stream, msg->hdr.proc, msg->hdr.serial); - - memset(&rerr, 0, sizeof rerr); + stream, msg->header.proc, msg->header.serial); stream->closed = 1; virStreamEventRemoveCallback(stream->st); ret = virStreamFinish(stream->st); if (ret < 0) { - remoteDispatchError(&rerr); - return remoteSerializeReplyError(client, &rerr, &msg->hdr); + virNetMessageError rerr; + memset(&rerr, 0, sizeof(rerr)); + return virNetServerProgramSendReplyError(stream->prog, + client, + msg, + &rerr, + &msg->header); } else { /* Send zero-length confirm */ - if (remoteSendStreamData(client, stream, NULL, 0) < 0) - return -1; + return virNetServerProgramSendStreamData(stream->prog, + client, + msg, + stream->procedure, + stream->serial, + NULL, 0); } - - return 0; } @@ -460,30 +491,35 @@ remoteStreamHandleFinish(struct qemud_client *client, * Returns 0 if successfully aborted, -1 upon error */ static int -remoteStreamHandleAbort(struct qemud_client *client, - struct qemud_client_stream *stream, - struct qemud_client_message *msg) +daemonStreamHandleAbort(virNetServerClientPtr client, + daemonClientStream *stream, + virNetMessagePtr msg) { - remote_error rerr; - VIR_DEBUG("stream=%p proc=%d serial=%d", - stream, msg->hdr.proc, msg->hdr.serial); + stream, msg->header.proc, msg->header.serial); + virNetMessageError rerr; - memset(&rerr, 0, sizeof rerr); + memset(&rerr, 0, sizeof(rerr)); stream->closed = 1; virStreamEventRemoveCallback(stream->st); virStreamAbort(stream->st); - if (msg->hdr.status == REMOTE_ERROR) - remoteDispatchFormatError(&rerr, "%s", _("stream aborted at client request")); + if (msg->header.status == VIR_NET_ERROR) + virNetError(VIR_ERR_RPC, + "%s", _("stream aborted at client request")); else { - VIR_WARN("unexpected stream status %d", msg->hdr.status); - remoteDispatchFormatError(&rerr, _("stream aborted with unexpected status %d"), - msg->hdr.status); + VIR_WARN("unexpected stream status %d", msg->header.status); + virNetError(VIR_ERR_RPC, + _("stream aborted with unexpected status %d"), + msg->header.status); } - return remoteSerializeReplyError(client, &rerr, &msg->hdr); + return virNetServerProgramSendReplyError(remoteProgram, + client, + msg, + &rerr, + &msg->header); } @@ -496,41 +532,39 @@ remoteStreamHandleAbort(struct qemud_client *client, * Returns 0 on success, or -1 upon fatal error */ static int -remoteStreamHandleWrite(struct qemud_client *client, - struct qemud_client_stream *stream) +daemonStreamHandleWrite(virNetServerClientPtr client, + daemonClientStream *stream) { - struct qemud_client_message *msg, *tmp; - VIR_DEBUG("stream=%p", stream); - msg = stream->rx; - while (msg && !stream->closed) { + while (stream->rx && !stream->closed) { + virNetMessagePtr msg = stream->rx; int ret; - switch (msg->hdr.status) { - case REMOTE_OK: - ret = remoteStreamHandleFinish(client, stream, msg); + + switch (msg->header.status) { + case VIR_NET_OK: + ret = daemonStreamHandleFinish(client, stream, msg); break; - case REMOTE_CONTINUE: - ret = remoteStreamHandleWriteData(client, stream, msg); + case VIR_NET_CONTINUE: + ret = daemonStreamHandleWriteData(client, stream, msg); break; - case REMOTE_ERROR: + case VIR_NET_ERROR: default: - ret = remoteStreamHandleAbort(client, stream, msg); + ret = daemonStreamHandleAbort(client, stream, msg); break; } - if (ret == 0) - qemudClientMessageQueueServe(&stream->rx); - else if (ret < 0) - return -1; - else - break; /* still processing data */ + if (ret > 0) + break; /* still processing data from msg */ - tmp = msg->next; - qemudClientMessageRelease(client, msg); - msg = tmp; + virNetMessageQueueServe(&stream->rx); + if (ret < 0) { + virNetMessageFree(msg); + virNetServerClientMarkClose(client); + return -1; + } } return 0; @@ -549,11 +583,11 @@ remoteStreamHandleWrite(struct qemud_client *client, * be killed */ static int -remoteStreamHandleRead(struct qemud_client *client, - struct qemud_client_stream *stream) +daemonStreamHandleRead(virNetServerClientPtr client, + daemonClientStream *stream) { char *buffer; - size_t bufferLen = REMOTE_MESSAGE_PAYLOAD_MAX; + size_t bufferLen = VIR_NET_MESSAGE_PAYLOAD_MAX; int ret; VIR_DEBUG("stream=%p", stream); @@ -572,16 +606,34 @@ remoteStreamHandleRead(struct qemud_client *client, * we're readable, but hey things change... */ ret = 0; } else if (ret < 0) { - remote_error rerr; - memset(&rerr, 0, sizeof rerr); - remoteDispatchError(&rerr); + virNetMessagePtr msg; + virNetMessageError rerr; + + memset(&rerr, 0, sizeof(rerr)); - ret = remoteSerializeStreamError(client, &rerr, stream->procedure, stream->serial); + if (!(msg = virNetMessageNew())) + ret = -1; + else + ret = virNetServerProgramSendStreamError(remoteProgram, + client, + msg, + &rerr, + stream->procedure, + stream->serial); } else { + virNetMessagePtr msg; stream->tx = 0; if (ret == 0) stream->recvEOF = 1; - ret = remoteSendStreamData(client, stream, buffer, ret); + if (!(msg = virNetMessageNew())) + ret = -1; + else + ret = virNetServerProgramSendStreamData(remoteProgram, + client, + msg, + stream->procedure, + stream->serial, + buffer, ret); } VIR_FREE(buffer); @@ -597,22 +649,23 @@ remoteStreamHandleRead(struct qemud_client *client, * fast stream, but slow client */ void -remoteStreamMessageFinished(struct qemud_client *client, - struct qemud_client_message *msg) +daemonStreamMessageFinished(virNetServerClientPtr client, + virNetMessagePtr msg) { - struct qemud_client_stream *stream = client->streams; + daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client); + daemonClientStream *stream = priv->streams; while (stream) { - if (msg->hdr.proc == stream->procedure && - msg->hdr.serial == stream->serial) + if (msg->header.proc == stream->procedure && + msg->header.serial == stream->serial) break; stream = stream->next; } - VIR_DEBUG("Message client=%p stream=%p proc=%d serial=%d", client, stream, msg->hdr.proc, msg->hdr.serial); + VIR_DEBUG("Message client=%p stream=%p proc=%d serial=%d", client, stream, msg->header.proc, msg->header.serial); if (stream) { stream->tx = 1; - remoteStreamUpdateEvents(stream); + daemonStreamUpdateEvents(stream); } } diff --git a/daemon/stream.h b/daemon/stream.h index 767a763..3855c89 100644 --- a/daemon/stream.h +++ b/daemon/stream.h @@ -28,27 +28,29 @@ -struct qemud_client_stream * -remoteCreateClientStream(virConnectPtr conn, - remote_message_header *hdr); +daemonClientStream * +daemonCreateClientStream(virNetServerClientPtr client, + virStreamPtr st, + virNetServerProgramPtr prog, + virNetMessageHeaderPtr hdr); -void remoteFreeClientStream(struct qemud_client *client, - struct qemud_client_stream *stream); +int daemonFreeClientStream(virNetServerClientPtr client, + daemonClientStream *stream); -int remoteAddClientStream(struct qemud_client *client, - struct qemud_client_stream *stream, +int daemonAddClientStream(virNetServerClientPtr client, + daemonClientStream *stream, int transmit); -struct qemud_client_stream * -remoteFindClientStream(struct qemud_client *client, +daemonClientStream * +daemonFindClientStream(virNetServerClientPtr client, virStreamPtr stream); int -remoteRemoveClientStream(struct qemud_client *client, - struct qemud_client_stream *stream); +daemonRemoveClientStream(virNetServerClientPtr client, + daemonClientStream *stream); void -remoteStreamMessageFinished(struct qemud_client *client, - struct qemud_client_message *msg); +daemonStreamMessageFinished(virNetServerClientPtr client, + virNetMessagePtr msg); #endif /* __LIBVIRTD_STREAM_H__ */ diff --git a/po/POTFILES.in b/po/POTFILES.in index 6b07386..a81fc55 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,4 +1,3 @@ -daemon/dispatch.c daemon/libvirtd.c daemon/remote.c daemon/remote_dispatch_bodies.h diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index d6264b9..2fd5ba8 100755 --- a/src/rpc/gendispatch.pl +++ b/src/rpc/gendispatch.pl @@ -258,80 +258,6 @@ if ($opt_d) { } } -# Prototypes for dispatch functions ("remote_dispatch_prototypes.h"). -elsif ($opt_p) { - my @keys = sort (keys %calls); - foreach (@keys) { - # Skip things which are REMOTE_MESSAGE - next if $calls{$_}->{msg}; - - print "static int ${structprefix}Dispatch$calls{$_}->{ProcName}(\n"; - print " struct qemud_server *server,\n"; - print " struct qemud_client *client,\n"; - print " virConnectPtr conn,\n"; - print " remote_message_header *hdr,\n"; - print " remote_error *rerr,\n"; - print " $calls{$_}->{args} *args,\n"; - print " $calls{$_}->{ret} *ret);\n"; - } -} - -# Union of all arg types -# ("remote_dispatch_args.h"). -elsif ($opt_a) { - for ($id = 0 ; $id <= $#calls ; $id++) { - if (defined $calls[$id] && - !$calls[$id]->{msg} && - $calls[$id]->{args} ne "void") { - print " $calls[$id]->{args} val_$calls[$id]->{args};\n"; - } - } -} - -# Union of all arg types -# ("remote_dispatch_ret.h"). -elsif ($opt_r) { - for ($id = 0 ; $id <= $#calls ; $id++) { - if (defined $calls[$id] && - !$calls[$id]->{msg} && - $calls[$id]->{ret} ne "void") { - print " $calls[$id]->{ret} val_$calls[$id]->{ret};\n"; - } - } -} - -# Inside the switch statement, prepare the 'fn', 'args_filter', etc -# ("remote_dispatch_table.h"). -elsif ($opt_t) { - for ($id = 0 ; $id <= $#calls ; $id++) { - if (defined $calls[$id] && !$calls[$id]->{msg}) { - print "{ /* $calls[$id]->{ProcName} => $id */\n"; - print " .fn = (dispatch_fn) ${structprefix}Dispatch$calls[$id]->{ProcName},\n"; - if ($calls[$id]->{args} ne "void") { - print " .args_filter = (xdrproc_t) xdr_$calls[$id]->{args},\n"; - } else { - print " .args_filter = (xdrproc_t) xdr_void,\n"; - } - if ($calls[$id]->{ret} ne "void") { - print " .ret_filter = (xdrproc_t) xdr_$calls[$id]->{ret},\n"; - } else { - print " .ret_filter = (xdrproc_t) xdr_void,\n"; - } - print "},\n"; - } else { - if ($calls[$id]->{msg}) { - print "{ /* Async event $calls[$id]->{ProcName} => $id */\n"; - } else { - print "{ /* (unused) => $id */\n"; - } - print " .fn = NULL,\n"; - print " .args_filter = (xdrproc_t) xdr_void,\n"; - print " .ret_filter = (xdrproc_t) xdr_void,\n"; - print "},\n"; - } - } -} - # Bodies for dispatch functions ("remote_dispatch_bodies.h"). elsif ($opt_b) { my %generate = map { $_ => 1 } @autogen; @@ -343,8 +269,58 @@ elsif ($opt_b) { # skip things which are REMOTE_MESSAGE next if $call->{msg}; - # skip procedures not on generate list - next if ! exists($generate{$call->{ProcName}}); + my $name = $structprefix . "Dispatch" . $call->{ProcName}; + my $argtype = $call->{args}; + my $rettype = $call->{ret}; + + my $argann = $argtype ne "void" ? "" : " ATTRIBUTE_UNUSED"; + my $retann = $rettype ne "void" ? "" : " ATTRIBUTE_UNUSED"; + + # First we print out a function declaration for the + # real dispatcher body + print "static int ${name}(\n"; + print " virNetServerPtr server,\n"; + print " virNetServerClientPtr client,\n"; + print " virNetMessageHeaderPtr hdr,\n"; + print " virNetMessageErrorPtr rerr"; + if ($argtype ne "void") { + print ",\n $argtype *args"; + } + if ($rettype ne "void") { + print ",\n $rettype *ret"; + } + print ");\n"; + + + # Next we print out a generic wrapper method which has + # fixed function signature, for use in the dispatcher + # table. This simply callers the real dispatcher method + print "static int ${name}Helper(\n"; + print " virNetServerPtr server,\n"; + print " virNetServerClientPtr client,\n"; + print " virNetMessageHeaderPtr hdr,\n"; + print " virNetMessageErrorPtr rerr,\n"; + print " void *args$argann,\n"; + print " void *ret$retann)\n"; + print "{\n"; + print " VIR_DEBUG(\"server=%p client=%p hdr=%p rerr=%p args=%p ret=%p\", server, client, hdr, rerr, args, ret);\n"; + print " return $name(server, client, hdr, rerr"; + if ($argtype ne "void") { + print ", args"; + } + if ($rettype ne "void") { + print ", ret"; + } + print ");\n"; + print "}\n"; + + # Finally we print out the dispatcher method body impl + # (if possible) + if (!exists($generate{$call->{ProcName}})) { + print "/* ${structprefix}Dispatch$call->{ProcName} body has " . + "to be implemented manually */\n\n\n\n"; + next; + } my $has_node_device = 0; my @vars_list = (); @@ -354,18 +330,18 @@ elsif ($opt_b) { my @prepare_ret_list = (); my @ret_list = (); my @free_list = (); - my @free_list_on_error = ("remoteDispatchError(rerr);"); + my @free_list_on_error = ("virNetMessageSaveError(rerr);"); # handle arguments to the function - if ($call->{args} ne "void") { + if ($argtype ne "void") { # node device is special, as it's identified by name - if ($call->{args} =~ m/^remote_node_device_/ and - !($call->{args} =~ m/^remote_node_device_lookup_by_name_/) and - !($call->{args} =~ m/^remote_node_device_create_xml_/)) { + if ($argtype =~ m/^remote_node_device_/ and + !($argtype =~ m/^remote_node_device_lookup_by_name_/) and + !($argtype =~ m/^remote_node_device_create_xml_/)) { $has_node_device = 1; push(@vars_list, "virNodeDevicePtr dev = NULL"); push(@getters_list, - " if (!(dev = virNodeDeviceLookupByName(conn, args->name)))\n" . + " if (!(dev = virNodeDeviceLookupByName(priv->conn, args->name)))\n" . " goto cleanup;\n"); push(@args_list, "dev"); push(@free_list, @@ -382,7 +358,7 @@ elsif ($opt_b) { push(@vars_list, "vir${type_name}Ptr $2 = NULL"); push(@getters_list, - " if (!($2 = get_nonnull_$1(conn, args->$2)))\n" . + " if (!($2 = get_nonnull_$1(priv->conn, args->$2)))\n" . " goto cleanup;\n"); push(@args_list, "$2"); push(@free_list, @@ -392,7 +368,7 @@ elsif ($opt_b) { push(@vars_list, "virDomainPtr dom = NULL"); push(@vars_list, "virDomainSnapshotPtr snapshot = NULL"); push(@getters_list, - " if (!(dom = get_nonnull_domain(conn, args->snap.dom)))\n" . + " if (!(dom = get_nonnull_domain(priv->conn, args->snap.dom)))\n" . " goto cleanup;\n" . "\n" . " if (!(snapshot = get_nonnull_domain_snapshot(dom, args->snap)))\n" . @@ -405,14 +381,14 @@ elsif ($opt_b) { " virDomainFree(dom);"); } elsif ($args_member =~ m/^(?:remote_string|remote_uuid) (\S+)<\S+>;/) { if (! @args_list) { - push(@args_list, "conn"); + push(@args_list, "priv->conn"); } push(@args_list, "args->$1.$1_val"); push(@args_list, "args->$1.$1_len"); } elsif ($args_member =~ m/^(?:opaque|remote_nonnull_string) (\S+)<\S+>;(.*)$/) { if (! @args_list) { - push(@args_list, "conn"); + push(@args_list, "priv->conn"); } my $cast = ""; @@ -431,7 +407,7 @@ elsif ($opt_b) { push(@args_list, "args->$arg_name.${arg_name}_len"); } elsif ($args_member =~ m/^(?:unsigned )?int (\S+)<\S+>;/) { if (! @args_list) { - push(@args_list, "conn"); + push(@args_list, "priv->conn"); } push(@args_list, "args->$1.$1_val"); @@ -452,13 +428,13 @@ elsif ($opt_b) { die "unhandled type for argument value: $args_member"; } elsif ($args_member =~ m/^remote_uuid (\S+);/) { if (! @args_list) { - push(@args_list, "conn"); + push(@args_list, "priv->conn"); } push(@args_list, "(unsigned char *) args->$1"); } elsif ($args_member =~ m/^remote_string (\S+);/) { if (! @args_list) { - push(@args_list, "conn"); + push(@args_list, "priv->conn"); } push(@vars_list, "char *$1"); @@ -466,19 +442,19 @@ elsif ($opt_b) { push(@args_list, "$1"); } elsif ($args_member =~ m/^remote_nonnull_string (\S+);/) { if (! @args_list) { - push(@args_list, "conn"); + push(@args_list, "priv->conn"); } push(@args_list, "args->$1"); } elsif ($args_member =~ m/^(unsigned )?int (\S+);/) { if (! @args_list) { - push(@args_list, "conn"); + push(@args_list, "priv->conn"); } push(@args_list, "args->$2"); } elsif ($args_member =~ m/^(unsigned )?hyper (\S+);/) { if (! @args_list) { - push(@args_list, "conn"); + push(@args_list, "priv->conn"); } my $arg_name = $2; @@ -511,12 +487,12 @@ elsif ($opt_b) { my $single_ret_list_max_define = "undefined"; my $multi_ret = 0; - if ($call->{ret} ne "void" and + if ($rettype ne "void" and scalar(@{$call->{ret_members}}) > 1) { $multi_ret = 1; } - if ($call->{ret} ne "void") { + if ($rettype ne "void") { foreach my $ret_member (@{$call->{ret_members}}) { if ($multi_ret) { if ($ret_member =~ m/^(unsigned )?(char|short|int|hyper) (\S+)\[\S+\];/) { @@ -715,8 +691,8 @@ elsif ($opt_b) { die "multi-return-value without insert@<offset> annotation: $call->{ret}"; } - if (!@args_list) { - push(@args_list, "conn"); + if (! @args_list) { + push(@args_list, "priv->conn"); } my $struct_name = $call->{ProcName}; @@ -737,36 +713,28 @@ elsif ($opt_b) { } if ($call->{streamflag} ne "none") { - splice(@args_list, $call->{streamoffset}, 0, ("stream->st")); + splice(@args_list, $call->{streamoffset}, 0, ("st")); push(@free_list_on_error, "if (stream) {"); - push(@free_list_on_error, " virStreamAbort(stream->st);"); - push(@free_list_on_error, " remoteFreeClientStream(client, stream);"); + push(@free_list_on_error, " virStreamAbort(st);"); + push(@free_list_on_error, " daemonFreeClientStream(client, stream);"); + push(@free_list_on_error, "} else {"); + push(@free_list_on_error, " virStreamFree(st);"); push(@free_list_on_error, "}"); } # print functions signature - print "\n"; - print "static int\n"; - print "${structprefix}Dispatch$call->{ProcName}(\n"; - print " struct qemud_server *server ATTRIBUTE_UNUSED,\n"; - print " struct qemud_client *client ATTRIBUTE_UNUSED,\n"; - print " virConnectPtr conn,\n"; - print " remote_message_header *hdr ATTRIBUTE_UNUSED,\n"; - print " remote_error *rerr,\n"; - print " $call->{args} *args"; - - if ($call->{args} eq "void") { - print " ATTRIBUTE_UNUSED" - } - - print ",\n"; - print " $call->{ret} *ret"; - - if ($call->{ret} eq "void") { - print " ATTRIBUTE_UNUSED" - } - - print ")\n"; + print "static int $name(\n"; + print " virNetServerPtr server ATTRIBUTE_UNUSED,\n"; + print " virNetServerClientPtr client,\n"; + print " virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED,\n"; + print " virNetMessageErrorPtr rerr"; + if ($argtype ne "void") { + print ",\n $argtype *args"; + } + if ($rettype ne "void") { + print ",\n $rettype *ret"; + } + print ")\n"; # print function body print "{\n"; @@ -775,13 +743,16 @@ elsif ($opt_b) { foreach my $var (@vars_list) { print " $var;\n"; } + print " struct daemonClientPrivate *priv =\n"; + print " virNetServerClientGetPrivateData(client);\n"; if ($call->{streamflag} ne "none") { - print " struct qemud_client_stream *stream = NULL;\n"; + print " virStreamPtr st = NULL;\n"; + print " daemonClientStreamPtr stream = NULL;\n"; } print "\n"; - print " if (!conn) {\n"; + print " if (!priv->conn) {\n"; print " virNetError(VIR_ERR_INTERNAL_ERROR, \"%s\", _(\"connection not open\"));\n"; print " goto cleanup;\n"; print " }\n"; @@ -811,12 +782,12 @@ elsif ($opt_b) { } if ($call->{streamflag} ne "none") { - print " if (!(stream = remoteCreateClientStream(conn, hdr)))\n"; + print " if (!(stream = daemonCreateClientStream(client, st, remoteProgram, hdr)))\n"; print " goto cleanup;\n"; print "\n"; } - if ($call->{ret} eq "void") { + if ($rettype eq "void") { print " if (vir$call->{ProcName}("; print join(', ', @args_list); print ") < 0)\n"; @@ -827,7 +798,7 @@ elsif ($opt_b) { my $proc_name = $call->{ProcName}; if (! @args_list) { - push(@args_list, "conn"); + push(@args_list, "priv->conn"); if ($call->{ProcName} ne "NodeGetFreeMemory") { $prefix = "Connect" @@ -885,7 +856,7 @@ elsif ($opt_b) { } if ($call->{streamflag} ne "none") { - print " if (remoteAddClientStream(client, stream, "; + print " if (daemonAddClientStream(client, stream, "; if ($call->{streamflag} eq "write") { print "0"; @@ -934,8 +905,46 @@ elsif ($opt_b) { } print " return rv;\n"; - print "}\n"; + print "}\n\n\n\n"; } + + + # Finally we write out the huge dispatch table which lists + # the dispatch helper method. the XDR proc for processing + # args and return values, and the size of the args and + # return value structs. All methods are marked as requiring + # authentication. Methods are selectively relaxed in the + # daemon code which registers the program. + + print "virNetServerProgramProc ${structprefix}Procs[] = {\n"; + for ($id = 0 ; $id <= $#calls ; $id++) { + my ($comment, $name, $argtype, $arglen, $argfilter, $retlen, $retfilter); + + if (defined $calls[$id] && !$calls[$id]->{msg}) { + $comment = "/* Method $calls[$id]->{ProcName} => $id */"; + $name = $structprefix . "Dispatch" . $calls[$id]->{ProcName} . "Helper"; + my $argtype = $calls[$id]->{args}; + my $rettype = $calls[$id]->{ret}; + $arglen = $argtype ne "void" ? "sizeof($argtype)" : "0"; + $retlen = $rettype ne "void" ? "sizeof($rettype)" : "0"; + $argfilter = $argtype ne "void" ? "xdr_$argtype" : "xdr_void"; + $retfilter = $rettype ne "void" ? "xdr_$rettype" : "xdr_void"; + } else { + if ($calls[$id]->{msg}) { + $comment = "/* Async event $calls[$id]->{ProcName} => $id */"; + } else { + $comment = "/* Unused $id */"; + } + $name = "NULL"; + $arglen = $retlen = 0; + $argfilter = "xdr_void"; + $retfilter = "xdr_void"; + } + + print "{ $comment\n ${name},\n $arglen,\n (xdrproc_t)$argfilter,\n $retlen,\n (xdrproc_t)$retfilter,\n true \n},\n"; + } + print "};\n"; + print "size_t ${structprefix}NProcs = ARRAY_CARDINALITY(${structprefix}Procs);\n"; } # Bodies for client functions ("remote_client_bodies.h"). @@ -952,6 +961,9 @@ elsif ($opt_k) { # skip procedures not on generate list next if ! exists($generate{$call->{ProcName}}); + my $argtype = $call->{args}; + my $rettype = $call->{ret}; + # handle arguments to the function my @args_list = (); my @vars_list = (); @@ -962,18 +974,18 @@ elsif ($opt_k) { my $priv_name = "privateData"; my $call_args = "&args"; - if ($call->{args} eq "void") { + if ($argtype eq "void") { $call_args = "NULL"; } else { - push(@vars_list, "$call->{args} args"); + push(@vars_list, "$argtype args"); my $is_first_arg = 1; my $has_node_device = 0; # node device is special - if ($call->{args} =~ m/^remote_node_/ and - !($call->{args} =~ m/^remote_node_device_lookup_by_name_/) and - !($call->{args} =~ m/^remote_node_device_create_xml_/)) { + if ($argtype =~ m/^remote_node_/ and + !($argtype =~ m/^remote_node_device_lookup_by_name_/) and + !($argtype =~ m/^remote_node_device_create_xml_/)) { $has_node_device = 1; $priv_name = "devMonPrivateData"; } @@ -1150,15 +1162,15 @@ elsif ($opt_k) { my $single_ret_cleanup = 0; my $multi_ret = 0; - if ($call->{ret} ne "void" and + if ($rettype ne "void" and scalar(@{$call->{ret_members}}) > 1) { $multi_ret = 1; } - if ($call->{ret} eq "void") { + if ($rettype eq "void") { $call_ret = "NULL"; } else { - push(@vars_list, "$call->{ret} ret"); + push(@vars_list, "$rettype ret"); foreach my $ret_member (@{$call->{ret_members}}) { if ($multi_ret) { @@ -1233,7 +1245,7 @@ elsif ($opt_k) { push(@ret_list, "rv = get_nonnull_$name($priv_src, ret.$arg_name);"); } - push(@ret_list, "xdr_free((xdrproc_t)xdr_$call->{ret}, (char *)&ret);"); + push(@ret_list, "xdr_free((xdrproc_t)xdr_$rettype, (char *)&ret);"); $single_ret_var = "vir${type_name}Ptr rv = NULL"; $single_ret_type = "vir${type_name}Ptr"; } @@ -1397,15 +1409,15 @@ elsif ($opt_k) { print "\n"; } - if ($call->{ret} ne "void") { + if ($rettype ne "void") { print "\n"; print " memset(&ret, 0, sizeof ret);\n"; } print "\n"; print " if (call($priv_src, priv, 0, ${procprefix}_PROC_$call->{UC_NAME},\n"; - print " (xdrproc_t)xdr_$call->{args}, (char *)$call_args,\n"; - print " (xdrproc_t)xdr_$call->{ret}, (char *)$call_ret) == -1) {\n"; + print " (xdrproc_t)xdr_$argtype, (char *)$call_args,\n"; + print " (xdrproc_t)xdr_$rettype, (char *)$call_ret) == -1) {\n"; if ($call->{streamflag} ne "none") { print " virNetClientRemoveStream(priv->client, netst);\n"; -- 1.7.4.4

On 06/27/2011 08:24 AM, Daniel P. Berrange wrote:
This guts the libvirtd daemon, removing all its networking and RPC handling code. Instead it calls out to the new virServerPtr APIs for all its RPC & networking work
As a fallout all libvirtd daemon error reporting now takes place via the normal internal error reporting APIs. There is no need to call separate error reporting APIs in RPC code, nor should code use VIR_WARN/VIR_ERROR for reporting fatal problems anymore.
* daemon/qemu_dispatch_*.h, daemon/remote_dispatch_*.h: Remove old generated dispatcher code * daemon/qemu_dispatch.h, daemon/remote_dispatch.h: New dispatch code * daemon/dispatch.c, daemon/dispatch.h: Remove obsoleted code * daemon/remote.c, daemon/remote.h: Rewrite for new dispatch APIs * daemon/libvirtd.c, daemon/libvirtd.h: Remove all networking code * daemon/stream.c, daemon/stream.h: Update for new APIs * daemon/Makefile.am: Link to libvirt-net-rpc-server.la +++ b/daemon/Makefile.am @@ -3,33 +3,21 @@ CLEANFILES =
DAEMON_GENERATED = \ - $(srcdir)/remote_dispatch_prototypes.h \ - $(srcdir)/remote_dispatch_table.h \ - $(srcdir)/remote_dispatch_args.h \ - $(srcdir)/remote_dispatch_ret.h \ - $(srcdir)/remote_dispatch_bodies.h \ - $(srcdir)/qemu_dispatch_prototypes.h \ - $(srcdir)/qemu_dispatch_table.h \ - $(srcdir)/qemu_dispatch_args.h \ - $(srcdir)/qemu_dispatch_ret.h \ - $(srcdir)/qemu_dispatch_bodies.h + $(srcdir)/remote_dispatch.h \ + $(srcdir)/qemu_dispatch.h
Should prove to be interesting, with fewer files.
@@ -211,11 +162,7 @@ policyfile = libvirtd.policy-1 endif endif
-if HAVE_AVAHI -libvirtd_SOURCES += $(AVAHI_SOURCES) -libvirtd_CFLAGS += $(AVAHI_CFLAGS) -libvirtd_LDADD += $(AVAHI_LIBS) -endif +EXTRA_DIST += probes.d libvirtd.stp
Spurious addition; this line is already present elsewhere in Makefile.am.
+++ b/daemon/libvirtd.c @@ -23,31 +23,13 @@
#include <grp.h> -#include <signal.h> -#include <netdb.h> -#include <locale.h>
A bit too prune-happy; compilation failed. And even then, I'm getting a test failure: 31) corrupted config audit_logging ... OK ./daemon-conf: line 98: kill: (22788) - No such process 32) valid config file (sleeping 2 seconds) ... FAILED FAIL: daemon-conf
#include "memory.h" -#include "stream.h" +#include "conf.h" +#include "virnetserver.h" +#include "threads.h" +#include "remote.h" +#include "remote_driver.h" +//#include "stream.h"
Delete this line, rather than commenting it.
+ + if (data->auth_unix_rw == REMOTE_AUTH_POLKIT) + data->unix_sock_rw_perms = strdup("0777"); /* Allow world */ + else + data->unix_sock_rw_perms = strdup("0700"); /* Allow user only */ + data->unix_sock_ro_perms = strdup("0777"); /* Always allow world */
Why are we passing this as formatted strings, instead of as raw octal mode_t numbers?
@@ -3218,69 +1325,91 @@ int main(int argc, char **argv) { break;
case 'p': - pid_file = optarg; + VIR_FREE(pid_file); + if (!(pid_file = strdup(optarg))) + exit(EXIT_FAILURE); break;
case 'f': - remote_config_file = optarg; + if (!(remote_config_file = strdup(optarg))) + exit(EXIT_FAILURE);
Mem leak if -f is used more than once (you need the same VIR_FREE to nuke prior use of -f as you had for prior use of -p).
+/* + * You must hold lock for at least the client + * We don't free stuff here, merely disconnect the client's + * network socket & resources. + * We keep the libvirt connection open until any async + * jobs have finished, then clean it up elsehwere + */ +static void remoteClientFreeFunc(void *data)
s/elsehwere/elsewhere/
static int -remoteDispatchDomainGetVcpupinInfo(struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, +remoteDispatchDomainGetVcpupinInfo(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_domain_get_vcpupin_info_args *args, remote_domain_get_vcpupin_info_ret *ret)
Simple merge conflict due to the Vcpupin->VcpuPin rename.
static int -remoteDispatchAuthList(struct qemud_server *server, - struct qemud_client *client, - virConnectPtr conn ATTRIBUTE_UNUSED, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - void *args ATTRIBUTE_UNUSED, +remoteDispatchAuthList(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_auth_list_ret *ret) { int rv = -1; + int auth = virNetServerClientGetAuth(client); + uid_t callerUid; + pid_t callerPid; + + /* If the client is root then we want to bypass the + * policykit auth to avoid root being denied if + * some piece of polkit isn't present/running + */ + if (auth == VIR_NET_SERVER_SERVICE_AUTH_POLKIT) { + if (virNetServerClientGetLocalIdentity(client, &callerUid, &callerPid) < 0) { + /* Don't do anything on error - it'll be validated at next + * phase of auth anyway */
This is new code; should it be split into a separate patch?
- ssf *= 8; /* tls key size is bytes, sasl wants bits */ - - err = sasl_setprop(client->saslconn, SASL_SSF_EXTERNAL, &ssf); - if (err != SASL_OK) { - VIR_ERROR(_("cannot set SASL external SSF %d (%s)"), - err, sasl_errstring(err, NULL, NULL)); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; + + ssf *= 8; /* key size is bytes, sasl wants bits */
Pre-existing, but I like CHAR_BIT (from <limits.h>) better than the magic number 8.
+ virNetSASLSessionSecProps(sasl, + 56, /* Good enough to require kerberos */ + 100000, /* Arbitrary big number */ + false); /* No anonymous */
Same magic numbers as in patch 1/4.
- VIR_DEBUG("Queue event %d %d", procnr, msg->bufferLength); - qemudClientMessageQueuePush(&client->tx, msg); - qemudUpdateClientEvent(client); + VIR_DEBUG("Queue event %d %Zu", procnr, msg->bufferLength);
%Zu is not portable. Use %zu instead.
/* * @conn: a connection object to associate the stream with - * @hdr: the method call to associate with the stram + * @header: the method call to associate with the stram
s/stram/stream/
+ # Finally we write out the huge dispatch table which lists + # the dispatch helper method. the XDR proc for processing + # args and return values, and the size of the args and + # return value structs. All methods are marked as requiring + # authentication. Methods are selectively relaxed in the + # daemon code which registers the program.
Should that instead be an annotation in the .x file? But we can change that later. Conditional ACK - fix the testsuite failure, address the above problems, and squash this in: diff --git i/.gitignore w/.gitignore index 4fbecfa..be4193d 100644 --- i/.gitignore +++ w/.gitignore @@ -34,7 +34,7 @@ /config.sub /configure /configure.lineno -/daemon/*_dispatch_*.h +/daemon/*_dispatch.h /docs/hvsupport.html.in /gnulib/ /libtool diff --git i/daemon/libvirtd.c w/daemon/libvirtd.c index 214199b..83ea094 100644 --- i/daemon/libvirtd.c +++ w/daemon/libvirtd.c @@ -30,6 +30,7 @@ #include <getopt.h> #include <stdlib.h> #include <grp.h> +#include <locale.h> #include "libvirt_internal.h" #include "virterror_internal.h" -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Mon, Jun 27, 2011 at 04:01:53PM -0600, Eric Blake wrote:
On 06/27/2011 08:24 AM, Daniel P. Berrange wrote:
This guts the libvirtd daemon, removing all its networking and RPC handling code. Instead it calls out to the new virServerPtr APIs for all its RPC & networking work
As a fallout all libvirtd daemon error reporting now takes place via the normal internal error reporting APIs. There is no need to call separate error reporting APIs in RPC code, nor should code use VIR_WARN/VIR_ERROR for reporting fatal problems anymore.
* daemon/qemu_dispatch_*.h, daemon/remote_dispatch_*.h: Remove old generated dispatcher code * daemon/qemu_dispatch.h, daemon/remote_dispatch.h: New dispatch code * daemon/dispatch.c, daemon/dispatch.h: Remove obsoleted code * daemon/remote.c, daemon/remote.h: Rewrite for new dispatch APIs * daemon/libvirtd.c, daemon/libvirtd.h: Remove all networking code * daemon/stream.c, daemon/stream.h: Update for new APIs * daemon/Makefile.am: Link to libvirt-net-rpc-server.la +++ b/daemon/Makefile.am @@ -3,33 +3,21 @@ CLEANFILES =
DAEMON_GENERATED = \ - $(srcdir)/remote_dispatch_prototypes.h \ - $(srcdir)/remote_dispatch_table.h \ - $(srcdir)/remote_dispatch_args.h \ - $(srcdir)/remote_dispatch_ret.h \ - $(srcdir)/remote_dispatch_bodies.h \ - $(srcdir)/qemu_dispatch_prototypes.h \ - $(srcdir)/qemu_dispatch_table.h \ - $(srcdir)/qemu_dispatch_args.h \ - $(srcdir)/qemu_dispatch_ret.h \ - $(srcdir)/qemu_dispatch_bodies.h + $(srcdir)/remote_dispatch.h \ + $(srcdir)/qemu_dispatch.h
Should prove to be interesting, with fewer files.
Basically all the code from those 5 previous files can now be consolidated into the one file, because the code restructuring means we generate the callback tables in a simpler way.
@@ -211,11 +162,7 @@ policyfile = libvirtd.policy-1 endif endif
-if HAVE_AVAHI -libvirtd_SOURCES += $(AVAHI_SOURCES) -libvirtd_CFLAGS += $(AVAHI_CFLAGS) -libvirtd_LDADD += $(AVAHI_LIBS) -endif +EXTRA_DIST += probes.d libvirtd.stp
Spurious addition; this line is already present elsewhere in Makefile.am.
Rebase mistake :-)
+++ b/daemon/libvirtd.c @@ -23,31 +23,13 @@
#include <grp.h> -#include <signal.h> -#include <netdb.h> -#include <locale.h>
A bit too prune-happy; compilation failed. And even then, I'm getting a test failure:
31) corrupted config audit_logging ... OK ./daemon-conf: line 98: kill: (22788) - No such process 32) valid config file (sleeping 2 seconds) ... FAILED FAIL: daemon-conf
I'll loook into that, probably another rebase mistake.
+ + if (data->auth_unix_rw == REMOTE_AUTH_POLKIT) + data->unix_sock_rw_perms = strdup("0777"); /* Allow world */ + else + data->unix_sock_rw_perms = strdup("0700"); /* Allow user only */ + data->unix_sock_ro_perms = strdup("0777"); /* Always allow world */
Why are we passing this as formatted strings, instead of as raw octal mode_t numbers?
That's because when we later override these variables with data from the config file it will also be in string format. We can't use int format in the config file, because that's always decimal and we want to have octal.
static int -remoteDispatchAuthList(struct qemud_server *server, - struct qemud_client *client, - virConnectPtr conn ATTRIBUTE_UNUSED, - remote_message_header *hdr ATTRIBUTE_UNUSED, - remote_error *rerr, - void *args ATTRIBUTE_UNUSED, +remoteDispatchAuthList(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessageHeaderPtr hdr ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, remote_auth_list_ret *ret) { int rv = -1; + int auth = virNetServerClientGetAuth(client); + uid_t callerUid; + pid_t callerPid; + + /* If the client is root then we want to bypass the + * policykit auth to avoid root being denied if + * some piece of polkit isn't present/running + */ + if (auth == VIR_NET_SERVER_SERVICE_AUTH_POLKIT) { + if (virNetServerClientGetLocalIdentity(client, &callerUid, &callerPid) < 0) { + /* Don't do anything on error - it'll be validated at next + * phase of auth anyway */
This is new code; should it be split into a separate patch?
Actually it isn't new code. In the old code, this particular check was hardcoded elsewhere in the daemon codebase, at the point where we accept() the client socket. With the new code structure, we can't do that, so I moved the check to here instead.
+ # Finally we write out the huge dispatch table which lists + # the dispatch helper method. the XDR proc for processing + # args and return values, and the size of the args and + # return value structs. All methods are marked as requiring + # authentication. Methods are selectively relaxed in the + # daemon code which registers the program.
Should that instead be an annotation in the .x file? But we can change that later.
Yeah, we could do that with an annotation these days. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Mon, Jun 27, 2011 at 04:01:53PM -0600, Eric Blake wrote:
On 06/27/2011 08:24 AM, Daniel P. Berrange wrote:
This guts the libvirtd daemon, removing all its networking and RPC handling code. Instead it calls out to the new virServerPtr APIs for all its RPC & networking work
As a fallout all libvirtd daemon error reporting now takes place via the normal internal error reporting APIs. There is no need to call separate error reporting APIs in RPC code, nor should code use VIR_WARN/VIR_ERROR for reporting fatal problems anymore.
* daemon/qemu_dispatch_*.h, daemon/remote_dispatch_*.h: Remove old generated dispatcher code * daemon/qemu_dispatch.h, daemon/remote_dispatch.h: New dispatch code * daemon/dispatch.c, daemon/dispatch.h: Remove obsoleted code * daemon/remote.c, daemon/remote.h: Rewrite for new dispatch APIs * daemon/libvirtd.c, daemon/libvirtd.h: Remove all networking code * daemon/stream.c, daemon/stream.h: Update for new APIs * daemon/Makefile.am: Link to libvirt-net-rpc-server.la +++ b/daemon/Makefile.am @@ -3,33 +3,21 @@ CLEANFILES =
+++ b/daemon/libvirtd.c @@ -23,31 +23,13 @@
#include <grp.h> -#include <signal.h> -#include <netdb.h> -#include <locale.h>
A bit too prune-happy; compilation failed. And even then, I'm getting a test failure:
31) corrupted config audit_logging ... OK ./daemon-conf: line 98: kill: (22788) - No such process 32) valid config file (sleeping 2 seconds) ... FAILED FAIL: daemon-conf
This turned out to be a bogus check I had added to the daemon making it refuse to startup as non-root if you specified a unix socket group. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

libvirtd now uses the generic RPC code for MDNS, so its custom mdns APIs are no longer required * daemon/mdns.c, daemon/mdns.h: Removed obsolete files --- daemon/mdns.c | 508 --------------------------------------------------------- daemon/mdns.h | 96 ----------- 2 files changed, 0 insertions(+), 604 deletions(-) delete mode 100644 daemon/mdns.c delete mode 100644 daemon/mdns.h diff --git a/daemon/mdns.c b/daemon/mdns.c deleted file mode 100644 index ca4a433..0000000 --- a/daemon/mdns.c +++ /dev/null @@ -1,508 +0,0 @@ -/* - * mdns.c: advertise libvirt hypervisor connections - * - * Copyright (C) 2007 Daniel P. Berrange - * - * Derived from Avahi example service provider code. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Daniel P. Berrange <berrange@redhat.com> - */ - -#include <config.h> - -#include <time.h> -#include <stdio.h> -#include <stdlib.h> - -#include <avahi-client/client.h> -#include <avahi-client/publish.h> - -#include <avahi-common/alternative.h> -#include <avahi-common/simple-watch.h> -#include <avahi-common/malloc.h> -#include <avahi-common/error.h> -#include <avahi-common/timeval.h> - -#include "libvirtd.h" -#include "mdns.h" -#include "event_poll.h" -#include "memory.h" - -#define AVAHI_DEBUG(fmt, ...) VIR_DEBUG(fmt, __VA_ARGS__) - -struct libvirtd_mdns_entry { - char *type; - int port; - struct libvirtd_mdns_entry *next; -}; - -struct libvirtd_mdns_group { - struct libvirtd_mdns *mdns; - AvahiEntryGroup *handle; - char *name; - struct libvirtd_mdns_entry *entry; - struct libvirtd_mdns_group *next; -}; - -struct libvirtd_mdns { - AvahiClient *client; - AvahiPoll *poller; - struct libvirtd_mdns_group *group; -}; - -/* Avahi API requires this struct names in the app :-( */ -struct AvahiWatch { - int watch; - int fd; - int revents; - AvahiWatchCallback callback; - void *userdata; -}; - -/* Avahi API requires this struct names in the app :-( */ -struct AvahiTimeout { - int timer; - AvahiTimeoutCallback callback; - void *userdata; -}; - - -static void libvirtd_mdns_create_services(struct libvirtd_mdns_group *group); - -/* Called whenever the entry group state changes */ -static void libvirtd_mdns_group_callback(AvahiEntryGroup *g ATTRIBUTE_UNUSED, AvahiEntryGroupState state, void *userdata) { - struct libvirtd_mdns_group *group = (struct libvirtd_mdns_group *)userdata; - - switch (state) { - case AVAHI_ENTRY_GROUP_ESTABLISHED: - /* The entry group has been established successfully */ - AVAHI_DEBUG("Group '%s' established", group->name); - break; - - case AVAHI_ENTRY_GROUP_COLLISION: - { - char *n; - - /* A service name collision happened. Let's pick a new name */ - n = avahi_alternative_service_name(group->name); - VIR_FREE(group->name); - group->name = n; - - AVAHI_DEBUG("Group name collision, renaming service to '%s'", group->name); - - /* And recreate the services */ - libvirtd_mdns_create_services(group); - } - break; - - case AVAHI_ENTRY_GROUP_FAILURE : - AVAHI_DEBUG("Group failure: %s", avahi_strerror(avahi_client_errno(group->mdns->client))); - - /* Some kind of failure happened while we were registering our services */ - //avahi_simple_poll_quit(simple_poll); - break; - - case AVAHI_ENTRY_GROUP_UNCOMMITED: - case AVAHI_ENTRY_GROUP_REGISTERING: - ; - } -} - -static void libvirtd_mdns_create_services(struct libvirtd_mdns_group *group) { - struct libvirtd_mdns *mdns = group->mdns; - struct libvirtd_mdns_entry *entry; - int ret; - AVAHI_DEBUG("Adding services to '%s'", group->name); - - /* If we've no services to advertise, just reset the group to make - * sure it is emptied of any previously advertised services */ - if (!group->entry) { - if (group->handle) - avahi_entry_group_reset(group->handle); - return; - } - - /* If this is the first time we're called, let's create a new entry group */ - if (!group->handle) { - AVAHI_DEBUG("Creating initial group %s", group->name); - if (!(group->handle = avahi_entry_group_new(mdns->client, libvirtd_mdns_group_callback, group))) { - AVAHI_DEBUG("avahi_entry_group_new() failed: %s", avahi_strerror(avahi_client_errno(mdns->client))); - return; - } - } - - entry = group->entry; - while (entry) { - if ((ret = avahi_entry_group_add_service(group->handle, - AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, - 0, - group->name, - entry->type, - NULL, - NULL, - entry->port, - NULL)) < 0) { - AVAHI_DEBUG("Failed to add %s service on port %d: %s", - entry->type, entry->port, avahi_strerror(ret)); - avahi_entry_group_reset(group->handle); - return; - } - entry = entry->next; - } - - /* Tell the server to register the service */ - if ((ret = avahi_entry_group_commit(group->handle)) < 0) { - avahi_entry_group_reset(group->handle); - AVAHI_DEBUG("Failed to commit entry_group: %s", avahi_strerror(ret)); - return; - } -} - - -static void libvirtd_mdns_client_callback(AvahiClient *c, AvahiClientState state, void *userdata) { - struct libvirtd_mdns *mdns = (struct libvirtd_mdns *)userdata; - struct libvirtd_mdns_group *group; - if (!mdns->client) - mdns->client = c; - - /* Called whenever the client or server state changes */ - switch (state) { - case AVAHI_CLIENT_S_RUNNING: - /* The server has startup successfully and registered its host - * name on the network, so it's time to create our services */ - AVAHI_DEBUG("Client running %p", mdns->client); - group = mdns->group; - while (group) { - libvirtd_mdns_create_services(group); - group = group->next; - } - break; - - case AVAHI_CLIENT_FAILURE: - AVAHI_DEBUG("Client failure: %s", avahi_strerror(avahi_client_errno(c))); - libvirtd_mdns_stop(mdns); - libvirtd_mdns_start(mdns); - break; - - case AVAHI_CLIENT_S_COLLISION: - /* Let's drop our registered services. When the server is back - * in AVAHI_SERVER_RUNNING state we will register them - * again with the new host name. */ - - /* Fallthrough */ - - case AVAHI_CLIENT_S_REGISTERING: - /* The server records are now being established. This - * might be caused by a host name change. We need to wait - * for our own records to register until the host name is - * properly established. */ - AVAHI_DEBUG("Client collision/connecting %p", mdns->client); - group = mdns->group; - while (group) { - if (group->handle) - avahi_entry_group_reset(group->handle); - group = group->next; - } - break; - - case AVAHI_CLIENT_CONNECTING: - AVAHI_DEBUG("Client connecting.... %p", mdns->client); - ; - } -} - - -static void libvirtd_mdns_watch_dispatch(int watch, int fd, int events, void *opaque) -{ - AvahiWatch *w = (AvahiWatch*)opaque; - int fd_events = virEventPollToNativeEvents(events); - AVAHI_DEBUG("Dispatch watch %d FD %d Event %d", watch, fd, fd_events); - w->revents = fd_events; - w->callback(w, fd, fd_events, w->userdata); -} - -static void libvirtd_mdns_watch_dofree(void *w) -{ - VIR_FREE(w); -} - - -static AvahiWatch *libvirtd_mdns_watch_new(const AvahiPoll *api ATTRIBUTE_UNUSED, - int fd, AvahiWatchEvent event, - AvahiWatchCallback cb, void *userdata) { - AvahiWatch *w; - virEventHandleType hEvents; - if (VIR_ALLOC(w) < 0) - return NULL; - - w->fd = fd; - w->revents = 0; - w->callback = cb; - w->userdata = userdata; - - AVAHI_DEBUG("New handle %p FD %d Event %d", w, w->fd, event); - hEvents = virEventPollFromNativeEvents(event); - if ((w->watch = virEventAddHandle(fd, hEvents, - libvirtd_mdns_watch_dispatch, - w, - libvirtd_mdns_watch_dofree)) < 0) { - VIR_FREE(w); - return NULL; - } - - return w; -} - -static void libvirtd_mdns_watch_update(AvahiWatch *w, AvahiWatchEvent event) -{ - AVAHI_DEBUG("Update handle %p FD %d Event %d", w, w->fd, event); - virEventUpdateHandle(w->watch, event); -} - -static AvahiWatchEvent libvirtd_mdns_watch_get_events(AvahiWatch *w) -{ - AVAHI_DEBUG("Get handle events %p %d", w, w->fd); - return w->revents; -} - -static void libvirtd_mdns_watch_free(AvahiWatch *w) -{ - AVAHI_DEBUG("Free handle %p %d", w, w->fd); - virEventRemoveHandle(w->watch); -} - -static void libvirtd_mdns_timeout_dispatch(int timer ATTRIBUTE_UNUSED, void *opaque) -{ - AvahiTimeout *t = (AvahiTimeout*)opaque; - AVAHI_DEBUG("Dispatch timeout %p %d", t, timer); - virEventUpdateTimeout(t->timer, -1); - t->callback(t, t->userdata); -} - -static void libvirtd_mdns_timeout_dofree(void *t) -{ - VIR_FREE(t); -} - -static AvahiTimeout *libvirtd_mdns_timeout_new(const AvahiPoll *api ATTRIBUTE_UNUSED, - const struct timeval *tv, - AvahiTimeoutCallback cb, - void *userdata) -{ - AvahiTimeout *t; - struct timeval now; - long long nowms, thenms, timeout; - AVAHI_DEBUG("Add timeout TV %p", tv); - if (VIR_ALLOC(t) < 0) - return NULL; - - if (gettimeofday(&now, NULL) < 0) { - VIR_FREE(t); - return NULL; - } - - AVAHI_DEBUG("Trigger timed for %d %d %d %d", - (int)now.tv_sec, (int)now.tv_usec, - (int)(tv ? tv->tv_sec : 0), (int)(tv ? tv->tv_usec : 0)); - nowms = (now.tv_sec * 1000ll) + (now.tv_usec / 1000ll); - if (tv) { - thenms = (tv->tv_sec * 1000ll) + (tv->tv_usec/1000ll); - timeout = thenms > nowms ? nowms - thenms : 0; - if (timeout < 0) - timeout = 0; - } else { - timeout = -1; - } - - t->timer = virEventAddTimeout(timeout, - libvirtd_mdns_timeout_dispatch, - t, - libvirtd_mdns_timeout_dofree); - t->callback = cb; - t->userdata = userdata; - - if (t->timer < 0) { - VIR_FREE(t); - return NULL; - } - - return t; -} - -static void libvirtd_mdns_timeout_update(AvahiTimeout *t, const struct timeval *tv) -{ - struct timeval now; - long long nowms, thenms, timeout; - AVAHI_DEBUG("Update timeout %p TV %p", t, tv); - if (gettimeofday(&now, NULL) < 0) { - VIR_FREE(t); - return; - } - - nowms = (now.tv_sec * 1000ll) + (now.tv_usec / 1000ll); - if (tv) { - thenms = ((tv->tv_sec * 1000ll) + (tv->tv_usec/1000ll)); - timeout = thenms > nowms ? nowms - thenms : 0; - if (timeout < 0) - timeout = 0; - } else { - timeout = -1; - } - - virEventUpdateTimeout(t->timer, timeout); -} - -static void libvirtd_mdns_timeout_free(AvahiTimeout *t) -{ - AVAHI_DEBUG("Free timeout %p", t); - virEventRemoveTimeout(t->timer); -} - - -static AvahiPoll *libvirtd_create_poll(void) -{ - AvahiPoll *p; - if (VIR_ALLOC(p) < 0) - return NULL; - - p->userdata = NULL; - - p->watch_new = libvirtd_mdns_watch_new; - p->watch_update = libvirtd_mdns_watch_update; - p->watch_get_events = libvirtd_mdns_watch_get_events; - p->watch_free = libvirtd_mdns_watch_free; - - p->timeout_new = libvirtd_mdns_timeout_new; - p->timeout_update = libvirtd_mdns_timeout_update; - p->timeout_free = libvirtd_mdns_timeout_free; - - return p; -} - -struct libvirtd_mdns *libvirtd_mdns_new(void) -{ - struct libvirtd_mdns *mdns; - if (VIR_ALLOC(mdns) < 0) - return NULL; - - /* Allocate main loop object */ - if (!(mdns->poller = libvirtd_create_poll())) { - VIR_FREE(mdns); - return NULL; - } - - return mdns; -} - -int libvirtd_mdns_start(struct libvirtd_mdns *mdns) -{ - int error; - AVAHI_DEBUG("Starting client %p", mdns); - mdns->client = avahi_client_new(mdns->poller, AVAHI_CLIENT_NO_FAIL, libvirtd_mdns_client_callback, mdns, &error); - - if (!mdns->client) { - AVAHI_DEBUG("Failed to create mDNS client: %s", avahi_strerror(error)); - return -1; - } - - return 0; -} - -struct libvirtd_mdns_group *libvirtd_mdns_add_group(struct libvirtd_mdns *mdns, const char *name) { - struct libvirtd_mdns_group *group; - - AVAHI_DEBUG("Adding group '%s'", name); - if (VIR_ALLOC(group) < 0) - return NULL; - - if (!(group->name = strdup(name))) { - VIR_FREE(group); - return NULL; - } - group->mdns = mdns; - group->next = mdns->group; - mdns->group = group; - return group; -} - -void libvirtd_mdns_remove_group(struct libvirtd_mdns *mdns, struct libvirtd_mdns_group *group) { - struct libvirtd_mdns_group *tmp = mdns->group, *prev = NULL; - - while (tmp) { - if (tmp == group) { - VIR_FREE(group->name); - if (prev) - prev->next = group->next; - else - group->mdns->group = group->next; - VIR_FREE(group); - return; - } - prev = tmp; - tmp = tmp->next; - } -} - -struct libvirtd_mdns_entry *libvirtd_mdns_add_entry(struct libvirtd_mdns_group *group, const char *type, int port) { - struct libvirtd_mdns_entry *entry; - - AVAHI_DEBUG("Adding entry %s %d to group %s", type, port, group->name); - if (VIR_ALLOC(entry) < 0) - return NULL; - - entry->port = port; - if (!(entry->type = strdup(type))) { - VIR_FREE(entry); - return NULL; - } - entry->next = group->entry; - group->entry = entry; - return entry; -} - -void libvirtd_mdns_remove_entry(struct libvirtd_mdns_group *group, struct libvirtd_mdns_entry *entry) { - struct libvirtd_mdns_entry *tmp = group->entry, *prev = NULL; - - while (tmp) { - if (tmp == entry) { - VIR_FREE(entry->type); - if (prev) - prev->next = entry->next; - else - group->entry = entry->next; - return; - } - prev = tmp; - tmp = tmp->next; - } -} - -void libvirtd_mdns_stop(struct libvirtd_mdns *mdns) -{ - struct libvirtd_mdns_group *group = mdns->group; - while (group) { - if (group->handle) { - avahi_entry_group_free(group->handle); - group->handle = NULL; - } - group = group->next; - } - if (mdns->client) - avahi_client_free(mdns->client); - mdns->client = NULL; -} diff --git a/daemon/mdns.h b/daemon/mdns.h deleted file mode 100644 index b096984..0000000 --- a/daemon/mdns.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * mdns.c: advertise libvirt hypervisor connections - * - * Copyright (C) 2007 Daniel P. Berrange - * - * Derived from Avahi example service provider code. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Daniel P. Berrange <berrange@redhat.com> - */ - -#include "internal.h" - -#ifndef __VIRTD_MDNS_H__ -# define __VIRTD_MDNS_H__ - -struct libvirtd_mdns; -struct libvirtd_mdns_group; -struct libvirtd_mdns_entry; - -/** - * Prepares a new mdns manager object for use - */ -struct libvirtd_mdns *libvirtd_mdns_new(void); - -/** - * Starts the mdns client, advertising any groups/entries currently registered - * - * @mdns: manager to start advertising - * - * Starts the mdns client. Services may not be immediately visible, since - * it may asynchronously wait for the mdns service to startup - * - * returns -1 upon failure, 0 upon success. - */ -int libvirtd_mdns_start(struct libvirtd_mdns *mdns); - -/** - * Stops the mdns client, removing any advertisements - * - * @mdns: manager to start advertising - * - */ -void libvirtd_mdns_stop(struct libvirtd_mdns *mdns); - -/** - * Adds a group container for advertisement - * - * @mdns manager to attach the group to - * @name unique human readable service name - * - * returns the group record, or NULL upon failure - */ -struct libvirtd_mdns_group *libvirtd_mdns_add_group(struct libvirtd_mdns *mdns, const char *name); - -/** - * Removes a group container from advertisement - * - * @mdns amanger to detach group from - * @group group to remove - */ -void libvirtd_mdns_remove_group(struct libvirtd_mdns *mdns, struct libvirtd_mdns_group *group); - -/** - * Adds a service entry in a group - * - * @group group to attach the entry to - * @type service type string - * @port tcp port number - * - * returns the service record, or NULL upon failure - */ -struct libvirtd_mdns_entry *libvirtd_mdns_add_entry(struct libvirtd_mdns_group *group, const char *type, int port); - -/** - * Removes a service entry from a group - * - * @group group to detach service entry from - * @entry service entry to remove - */ -void libvirtd_mdns_remove_entry(struct libvirtd_mdns_group *group, struct libvirtd_mdns_entry *entry); - -#endif /* __VIRTD_MDNS_H__ */ -- 1.7.4.4

On 06/27/2011 08:24 AM, Daniel P. Berrange wrote:
libvirtd now uses the generic RPC code for MDNS, so its custom mdns APIs are no longer required
* daemon/mdns.c, daemon/mdns.h: Removed obsolete files --- daemon/mdns.c | 508 --------------------------------------------------------- daemon/mdns.h | 96 ----------- 2 files changed, 0 insertions(+), 604 deletions(-) delete mode 100644 daemon/mdns.c delete mode 100644 daemon/mdns.h
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

The standard remote protocol for libvirtd no longer needs to include definitions of the generic message header/error structs or status codes. This is all defined in the generic RPC protocol * src/remote/remote_protocol.x: Remove all RPC message definitions * src/remote/remote_protocol.h, src/remote/remote_protocol.c: Re-generate * daemon/remote_generate_stubs.pl: Delete obsolete script --- src/remote/remote_protocol.x | 115 ------------------------------------------ 1 files changed, 0 insertions(+), 115 deletions(-) diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index ee08b82..342f392 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -60,15 +60,6 @@ /*----- Data types. -----*/ -/* Maximum total message size (serialised). */ -const REMOTE_MESSAGE_MAX = 262144; - -/* Size of struct remote_message_header (serialized)*/ -const REMOTE_MESSAGE_HEADER_MAX = 24; - -/* Size of message payload */ -const REMOTE_MESSAGE_PAYLOAD_MAX = 262120; - /* Length of long, but not unbounded, strings. * This is an arbitrary limit designed to stop the decoder from trying * to allocate unbounded amounts of memory when fed with a bad message. @@ -2409,109 +2400,3 @@ enum remote_procedure { * <offset> specifies at which offset the stream parameter is inserted * in the function parameter list. */ }; - -/* - * RPC wire format - * - * Each message consists of: - * - * Name | Type | Description - * -----------+-----------------------+------------------ - * Length | int | Total number of bytes in message _including_ length. - * Header | remote_message_header | Control information about procedure call - * Payload | - | Variable payload data per procedure - * - * In header, the 'serial' field varies according to: - * - * - type == REMOTE_CALL - * * serial is set by client, incrementing by 1 each time - * - * - type == REMOTE_REPLY - * * serial matches that from the corresponding REMOTE_CALL - * - * - type == REMOTE_MESSAGE - * * serial is always zero - * - * - type == REMOTE_STREAM - * * serial matches that from the corresponding REMOTE_CALL - * - * and the 'status' field varies according to: - * - * - type == REMOTE_CALL - * * REMOTE_OK always - * - * - type == REMOTE_REPLY - * * REMOTE_OK if RPC finished successfully - * * REMOTE_ERROR if something failed - * - * - type == REMOTE_MESSAGE - * * REMOTE_OK always - * - * - type == REMOTE_STREAM - * * REMOTE_CONTINUE if more data is following - * * REMOTE_OK if stream is complete - * * REMOTE_ERROR if stream had an error - * - * Payload varies according to type and status: - * - * - type == REMOTE_CALL - * XXX_args for procedure - * - * - type == REMOTE_REPLY - * * status == REMOTE_OK - * XXX_ret for procedure - * * status == REMOTE_ERROR - * remote_error Error information - * - * - type == REMOTE_MESSAGE - * * status == REMOTE_OK - * XXX_args for procedure - * * status == REMOTE_ERROR - * remote_error Error information - * - * - type == REMOTE_STREAM - * * status == REMOTE_CONTINUE - * byte[] raw stream data - * * status == REMOTE_ERROR - * remote_error error information - * * status == REMOTE_OK - * <empty> - */ -enum remote_message_type { - /* client -> server. args from a method call */ - REMOTE_CALL = 0, - /* server -> client. reply/error from a method call */ - REMOTE_REPLY = 1, - /* either direction. async notification */ - REMOTE_MESSAGE = 2, - /* either direction. stream data packet */ - REMOTE_STREAM = 3 -}; - -enum remote_message_status { - /* Status is always REMOTE_OK for calls. - * For replies, indicates no error. - */ - REMOTE_OK = 0, - - /* For replies, indicates that an error happened, and a struct - * remote_error follows. - */ - REMOTE_ERROR = 1, - - /* For streams, indicates that more data is still expected - */ - REMOTE_CONTINUE = 2 -}; - -/* 4 byte length word per header */ -const REMOTE_MESSAGE_HEADER_XDR_LEN = 4; - -struct remote_message_header { - unsigned prog; /* REMOTE_PROGRAM */ - unsigned vers; /* REMOTE_PROTOCOL_VERSION */ - int proc; /* REMOTE_PROC_x */ - remote_message_type type; - unsigned serial; /* Serial number of message. */ - remote_message_status status; -}; -- 1.7.4.4

On 06/27/2011 08:24 AM, Daniel P. Berrange wrote:
The standard remote protocol for libvirtd no longer needs to include definitions of the generic message header/error structs or status codes. This is all defined in the generic RPC protocol
* src/remote/remote_protocol.x: Remove all RPC message definitions * src/remote/remote_protocol.h, src/remote/remote_protocol.c: Re-generate * daemon/remote_generate_stubs.pl: Delete obsolete script
Commit message is out-of-date; this file was renamed daemon/remote_generator.pl in commit 999f5b3, then moved to src/rpc/gendispatch.pl in commit b17b4af.
--- src/remote/remote_protocol.x | 115 ------------------------------------------ 1 files changed, 0 insertions(+), 115 deletions(-)
Given the above commit message, should you also be trimming away at src/rpc/gendispatch.pl, or any of the Makefile.am snippets that still create the various *_bodies.h files? ACK with that question resolved, and this squashed in. diff --git i/src/remote_protocol-structs w/src/remote_protocol-structs index c07ba81..25979b4 100644 --- i/src/remote_protocol-structs +++ w/src/remote_protocol-structs @@ -1615,11 +1615,3 @@ struct remote_domain_get_control_info_ret { u_int details; uint64_t stateTime; }; -struct remote_message_header { - u_int prog; - u_int vers; - int proc; - remote_message_type type; - u_int serial; - remote_message_status status; -}; -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 06/27/2011 08:24 AM, Daniel P. Berrange wrote:
This patch set actually switches the libvirtd daemon and remote driver client code to use the new RPC code.
While it has had a fair amount of testing, it has also been rebased many many many times. There are bound to be things in it which break, hopefully not the actual RPC wire data format, but in the client/daemon dispatch code and thread safety, etc ....
Personally, I'm in favor of getting this committed now, and doing a RC2 release candidate. The sooner we get this in, the more test exposure it can get; and if this is delayed, then all the new RPC code already added has no clients and no testing for a full release cycle. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Mon, Jun 27, 2011 at 03:24:35PM +0100, Daniel P. Berrange wrote:
This patch set actually switches the libvirtd daemon and remote driver client code to use the new RPC code.
While it has had a fair amount of testing, it has also been rebased many many many times. There are bound to be things in it which break, hopefully not the actual RPC wire data format, but in the client/daemon dispatch code and thread safety, etc ....
The tradeoff is waiting for next release to not risk breaking 0.9.3 vs. waiting for 0.9.4 to get feedback on the new internal APIs. Honestly I doubt there will be much testing as of 0.9.3 if we don't use the new internal APIs in some way. So I think it makes sense to push those now, even if we are theorically in freeze, waiting one month won't improve the situation I'm afraid. So okay to push, following Eric ACKs Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (3)
-
Daniel P. Berrange
-
Daniel Veillard
-
Eric Blake