diff --git a/.gitignore b/.gitignore index 4fbecfa3e9..be4193d615 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,7 @@ /config.sub /configure /configure.lineno -/daemon/*_dispatch_*.h +/daemon/*_dispatch.h /docs/hvsupport.html.in /gnulib/ /libtool diff --git a/configure.ac b/configure.ac index a59e8e178f..cd22afbd99 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 ad14c901e2..8ed29b89e2 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,12 +162,6 @@ policyfile = libvirtd.policy-1 endif endif -if HAVE_AVAHI -libvirtd_SOURCES += $(AVAHI_SOURCES) -libvirtd_CFLAGS += $(AVAHI_CFLAGS) -libvirtd_LDADD += $(AVAHI_LIBS) -endif - if WITH_DTRACE libvirtd_LDADD += probes.o nodist_libvirtd_SOURCES = probes.h diff --git a/daemon/dispatch.c b/daemon/dispatch.c deleted file mode 100644 index 010be1ef50..0000000000 --- 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 - * Author: Daniel P. Berrange - */ - -#include - -#include -#include -#include - -#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 : ""); - 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 f24f4946fe..0000000000 --- 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 - * Author: Daniel P. Berrange - */ - -#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 5f291ec6f8..62f089dd4c 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -23,30 +23,13 @@ #include -#include -#include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include -#include +#include #include -#include -#include #include #include "libvirt_internal.h" @@ -56,20 +39,20 @@ #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 "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 -# include - -# 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 */ +virNetSASLContextPtr saslCtxt = NULL; +virNetServerProgramPtr remoteProgram = NULL; +virNetServerProgramPtr qemuProgram = NULL; -/* 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; +struct daemonConfig { + char *host_uuid; -static char *unix_sock_dir = NULL; + int listen_tls; + int listen_tcp; + char *listen_addr; + char *tls_port; + char *tcp_port; -#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; + char *unix_sock_ro_perms; + char *unix_sock_rw_perms; + char *unix_sock_group; + char *unix_sock_dir; -static int mdns_adv = 1; -static char *mdns_name = NULL; + int auth_unix_rw; + int auth_unix_ro; + int auth_tcp; + int auth_tls; -static int tls_no_verify_certificate = 0; -static char **tls_allowed_dn_list = NULL; + int mdns_adv; + char *mdns_name; -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 tls_no_verify_certificate; + char **tls_allowed_dn_list; + char **sasl_allowed_username_list; -static gnutls_certificate_credentials_t x509_cred; -static gnutls_dh_params_t dh_params; + char *key_file; + char *cert_file; + char *ca_file; + char *crl_file; -static int min_workers = 5; -static int max_workers = 20; -static int max_clients = 20; + int min_workers; + int max_workers; + int max_clients; -/* 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 max_requests; + int max_client_requests; -static int audit_level = 1; -static int audit_logging = 0; + int log_level; + char *log_filters; + char *log_outputs; + int log_buffer_size; -#define DH_BITS 1024 - -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) +static int daemonForkIntoBackground(const char *argv0) { - 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) -{ - 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) +daemonPidFilePath(bool privileged, + char **pidfile) { - struct addrinfo *ai; - struct addrinfo hints; - memset (&hints, 0, sizeof hints); - hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; - hints.ai_socktype = SOCK_STREAM; + if (privileged) { + if (!(*pidfile = strdup(LOCALSTATEDIR "/run/libvirtd.pid"))) + goto no_memory; + } else { + char *userdir = NULL; - int e = getaddrinfo (node, service, &hints, &ai); - if (e != 0) { - VIR_ERROR(_("getaddrinfo: %s"), gai_strerror (e)); - return -1; - } + if (!(userdir = virGetUserDirectory(geteuid()))) + goto error; - 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; - } + if (virAsprintf(pidfile, "%s/.libvirt/libvirtd.pid", userdir) < 0) + goto no_memory; - 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) -{ - 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 (!(sock->addrstr = virSocketFormatAddrFull(&sock->addr, true, ";"))) - goto cleanup; - - if (virSetCloseExec(sock->fd) < 0 || - virSetNonBlock(sock->fd) < 0) - goto cleanup; - - 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]); - return -1; -} - -static int qemudInitPaths(struct qemud_server *server, - char **sockname, - char **roSockname) -{ - 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))) - 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))) - 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) - goto no_memory; - } else { - if (virAsprintf(&sock_dir_prefix, "%s/.libvirt", - base_dir_prefix) < 0) - goto no_memory; - } - } - - 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 (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; - } - - ret = 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 int +daemonUnixSocketPaths(struct daemonConfig *config, + bool privileged, + char **sockfile, + char **rosockfile) +{ + if (config->unix_sock_dir) { + if (virAsprintf(sockfile, "%s/libvirt-sock", config->unix_sock_dir) < 0) + goto no_memory; + if (privileged && + virAsprintf(rosockfile, "%s/libvirt-sock-ro", config->unix_sock_dir) < 0) + goto no_memory; + } else { + if (privileged) { + if (!(*sockfile = strdup(LOCALSTATEDIR "/run/libvirt/libvirt-sock"))) + goto no_memory; + if (!(*rosockfile = strdup(LOCALSTATEDIR "/run/libvirt/libvirt-sock-ro"))) + goto no_memory; + } else { + char *userdir = NULL; + + if (!(userdir = virGetUserDirectory(geteuid()))) + goto error; + + if (virAsprintf(sockfile, "@%s/.libvirt/libvirt-sock", userdir) < 0) { + VIR_FREE(userdir); + goto no_memory; + } + + VIR_FREE(userdir); + } + } + return 0; + +no_memory: + virReportOOMError(); +error: + return -1; +} + + +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,40 +448,123 @@ 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 (qemudListenUnix(server, sockname, 0, auth_unix_rw) < 0) - goto cleanup; - sockname = NULL; + if (config->unix_sock_group) { + if (virGetGroupID(config->unix_sock_group, &unix_sock_gid) < 0) + return -1; + } - if (roSockname != NULL && qemudListenUnix(server, roSockname, 1, auth_unix_ro) < 0) - goto cleanup; - roSockname = 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 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; + } + + 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; + + if (virNetServerAddService(srv, svc, NULL) < 0) + goto error; + if (svcRO && + virNetServerAddService(srv, svcRO, NULL) < 0) + goto error; + + if (ipsock) { + if (config->listen_tcp) { + if (!(svcTCP = virNetServerServiceNewTCP(config->listen_addr, + config->tcp_port, + config->auth_tcp, + false, + NULL))) + goto error; + + if (virNetServerAddService(srv, svcTCP, + config->mdns_adv ? "_libvirt._tcp" : NULL) < 0) + goto error; + } + + if (config->listen_tls) { + virNetTLSContextPtr ctxt = NULL; + + 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 (!(svcTLS = + virNetServerServiceNewTCP(config->listen_addr, + config->tls_port, + config->auth_tls, + false, + ctxt))) { + virNetTLSContextFree(ctxt); + goto error; + } + if (virNetServerAddService(srv, svcTLS, + config->mdns_adv && + !config->listen_tcp ? "_libvirt._tcp" : NULL) < 0) + goto error; + + virNetTLSContextFree(ctxt); } } -#endif + + 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; + } #if HAVE_POLKIT0 if (auth_unix_rw == REMOTE_AUTH_POLKIT || @@ -1000,1539 +580,32 @@ static int qemudNetworkInit(struct qemud_server *server) { VIR_ERROR(_("Failed to connect to system bus for PolicyKit auth: %s"), derr.message); dbus_error_free(&derr); - goto cleanup; + goto error; } dbus_connection_set_exit_on_disconnect(server->sysbus, FALSE); } #endif - 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 (gnutls_x509_crt_init (&cert) < 0) { - VIR_ERROR(_("Unable to initialize certificate")); - goto authfail; - } - - 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 (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 (!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 (gnutls_x509_crt_get_expiration_time (cert) < now) { - VIR_ERROR(_("The client certificate has expired")); - gnutls_x509_crt_deinit (cert); - goto authdeny; - } - - 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; - } - } - - 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); - 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_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) - 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; - } - } -#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) +static int daemonShutdownCheck(virNetServerPtr srv ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) { - 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; + if (virStateActive()) + return 0; + + return 1; } -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) -{ - 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); - - VIR_FREE(server); -} /* Allocate an array of malloc'd strings from the config file, filename * (used only in diagnostics), using handle "conf". Upon error, return -1 @@ -2632,13 +705,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 +720,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 +740,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 +754,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 +761,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 +782,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 +803,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 +834,191 @@ qemudSetLogging(struct qemud_server *server, virConfPtr conf, if ((verbose) && (virLogGetDefaultPriority() > VIR_LOG_INFO)) virLogSetDefaultPriority(VIR_LOG_INFO); - ret = 0; + return 0; -free_and_fail: - VIR_FREE(log_filters); - VIR_FREE(log_outputs); - VIR_FREE(log_file); - return(ret); - -out_of_memory: +no_memory: virReportOOMError(); - goto free_and_fail; +error: + return -1; } -/* - * Stop logging - */ -static void -qemudStopLogging(void) + +static int +daemonConfigFilePath(bool privileged, char **configfile) { - virLogShutdown(); + 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); + } + + return 0; + +no_memory: + virReportOOMError(); +error: + return -1; } +static void +daemonConfigFree(struct daemonConfig *data); + +static struct daemonConfig* +daemonConfigNew(bool privileged) +{ + 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 +1026,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 +1061,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 +1082,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 +1126,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; - - 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); - - /* - * 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; - } - - server->sigread = sigpipe[0]; - server->sigwrite = sigpipe[1]; - sigwrite = sigpipe[1]; - return 0; +} -error: - VIR_FORCE_CLOSE(sigpipe[0]); - VIR_FORCE_CLOSE(sigpipe[1]); - return -1; +static void daemonRunStateInit(void *opaque) +{ + virNetServerPtr srv = opaque; + + /* 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; + } + + /* 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; } /* Print command-line usage. */ static void -usage (void) +daemonUsage(const char *argv0, bool privileged) { fprintf (stderr, _("\n\ @@ -3117,38 +1196,55 @@ Options:\n\ | --version Display version information.\n\ -p | --pid-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 +1253,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 +1283,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 +1321,92 @@ 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; + VIR_FREE(remote_config_file); + 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 +1418,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) { + 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; + } + + if (timeout != -1) + virNetServerAutoShutdown(srv, + timeout, + daemonShutdownCheck, + NULL); + + if ((daemonSetupSignals(srv)) < 0) { ret = VIR_DAEMON_ERR_SIGNAL; - goto error; + goto cleanup; } - /* Read the config file (if it exists). */ - if (remoteReadConfigFile (server, remote_config_file) < 0) { - ret = VIR_DAEMON_ERR_CONFIG; - goto error; - } - - 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 +1511,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 +1530,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; + /* Initialize drivers & then start accepting new clients from network */ + if (daemonStateInit(srv) < 0) { + ret = VIR_DAEMON_ERR_INIT; + goto cleanup; } - /* 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; - } - - /* 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 +1558,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 ea00d5c421..6c604fc1fb 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -27,13 +27,6 @@ # include -# include -# include -# include "gnutls_1_0_compat.h" -# if HAVE_SASL -# include -# endif - # if HAVE_POLKIT0 # include # 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 -# 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; + daemonClientStreamPtr streams; }; -# 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; -}; - -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 04161c495b..47e47e4483 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -22,26 +22,6 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #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" -#define VIR_FROM_THIS VIR_FROM_REMOTE +#include "remote_protocol.h" +#include "qemu_protocol.h" + + +#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" +#include "remote_dispatch.h" +#include "qemu_dispatch.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; - } - - 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 elsewhere + */ +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_vcpu_pin_info_args *args, remote_domain_get_vcpu_pin_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) { virNodeCPUStatsPtr 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) { virNodeMemoryStatsPtr 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; + if (virNetServerClientHasTLSSession(client)) { + int 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 ((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; - } + virNetSASLSessionSecProps(sasl, + 56, /* Good enough to require kerberos */ + 100000, /* Arbitrary big number */ + false); /* No anonymous */ - 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; + if (!(ret->mechlist = virNetSASLSessionListMechanisms(sasl))) goto authfail; - } + VIR_DEBUG("Available mechanisms for client: '%s'", ret->mechlist); - 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; - goto authfail; - } - - 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; - } - 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; - 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; - - /* We have a SSF !*/ - return 0; -} - /* * 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; +remoteSASLFinish(virNetServerClientPtr client) +{ + const char *identity; + struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); + int ssf; - 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); + /* TLS or UNIX domain sockets trivially OK */ + if (!virNetServerClientIsSecure(client)) { + if ((ssf = virNetSASLSessionGetKeySize(priv->sasl)) < 0) + goto error; - 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; + 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; + } } - /* If the list is not set, allow any DN. */ - wildcards = server->saslUsernameWhitelist; - if (!wildcards) - return 0; /* No ACL, allow all */ + if (!(identity = virNetSASLSessionGetIdentity(priv->sasl))) + return -2; - while (*wildcards) { - if (fnmatch(*wildcards, client->saslUsername, 0) == 0) - return 0; /* Allowed */ - wildcards++; - } + if (!virNetSASLContextCheckIdentity(saslCtxt, identity)) + return -2; - /* Denied */ - VIR_ERROR(_("SASL client %s not allowed in whitelist"), client->saslUsername); - remoteDispatchAuthError(rerr); - sasl_dispose(&client->saslconn); - client->saslconn = NULL; - return -2; + if (virNetServerClientSetIdentity(client, identity) < 0) + goto error; + + + 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)); + + virNetSASLSessionFree(priv->sasl); + priv->sasl = NULL; + + return 0; + +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); + 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; } -#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); - 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); + if (virNetMessageEncodePayload(msg, proc, data) < 0) + goto cleanup; - /* Skip over the header we just wrote */ - if (xdr_setpos(&xdr, msg->bufferOffset) == 0) - goto xdr_cleanup; + VIR_DEBUG("Queue event %d %zu", procnr, msg->bufferLength); + virNetServerClientSendMessage(client, msg); - 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; - - VIR_DEBUG("Queue event %d %d", procnr, msg->bufferLength); - qemudClientMessageQueuePush(&client->tx, msg); - qemudUpdateClientEvent(client); - - 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 1eb83865cb..c9bf5d7029 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -24,62 +24,20 @@ #ifndef __LIBVIRTD_REMOTE_H__ # define __LIBVIRTD_REMOTE_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); +# include "remote_protocol.h" +# include "rpc/virnetserverprogram.h" +# include "rpc/virnetserverclient.h" +extern virNetServerProgramProc remoteProcs[]; +extern size_t remoteNProcs; +extern virNetServerProgramErrorHander remoteErr; -/** - * 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 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 48085da18f..e4fcf90a16 100644 --- a/daemon/stream.c +++ b/daemon/stream.c @@ -24,32 +24,57 @@ #include #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) @@ -60,24 +85,43 @@ remoteStreamUpdateEvents(struct qemud_client_stream *stream) virStreamEventUpdateCallback(stream->st, newEvents); } +/* + * Invoked when an outgoing data packet message has been fully sent. + * This simply re-enables TX of further data. + * + * The idea is to stop the daemon growing without bound due to + * fast stream, but slow client + */ +static void +daemonStreamMessageFinished(virNetMessagePtr msg, + void *opaque) +{ + daemonClientStream *stream = opaque; + VIR_DEBUG("stream=%p proc=%d serial=%d", + stream, msg->header.proc, msg->header.serial); + + stream->tx = 1; + daemonStreamUpdateEvents(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 = priv->streams; + while (stream) { + if (stream->st == st) + break; + stream = stream->next; + } if (!stream) { VIR_WARN("event for client=%p stream st=%p, but missing stream state", client, st); @@ -85,12 +129,12 @@ remoteStreamEvent(virStreamPtr st, int events, void *opaque) goto cleanup; } - VIR_DEBUG("st=%p events=%d", st, events); + VIR_DEBUG("st=%p events=%d EOF=%d closed=%d", st, events, stream->recvEOF, stream->closed); 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,40 +142,84 @@ 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; } } + /* If we have a completion/abort message, always process it */ + if (stream->rx) { + virNetMessagePtr msg = stream->rx; + switch (msg->header.status) { + case VIR_NET_CONTINUE: + /* nada */ + break; + case VIR_NET_OK: + virNetMessageQueueServe(&stream->rx); + if (daemonStreamHandleFinish(client, stream, msg) < 0) { + virNetMessageFree(msg); + daemonRemoveClientStream(client, stream); + virNetServerClientClose(client); + goto cleanup; + } + break; + case VIR_NET_ERROR: + default: + virNetMessageQueueServe(&stream->rx); + if (daemonStreamHandleAbort(client, stream, msg) < 0) { + virNetMessageFree(msg); + daemonRemoveClientStream(client, stream); + virNetServerClientClose(client); + goto cleanup; + } + break; + } + } + 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 +232,72 @@ cleanup: * -1 on fatal client error */ static int -remoteStreamFilter(struct qemud_client *client, - struct qemud_client_message *msg, void *opaque) +daemonStreamFilter(virNetServerClientPtr client, + virNetMessagePtr msg, + void *opaque) { - struct qemud_client_stream *stream = opaque; + daemonClientStream *stream = opaque; + int ret = 0; - 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); + virMutexLock(&stream->priv->lock); - /* 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; + if (msg->header.type != VIR_NET_STREAM) + goto cleanup; - case REMOTE_CONTINUE: - qemudClientMessageQueuePush(&stream->rx, msg); - remoteStreamUpdateEvents(stream); - break; + if (!virNetServerProgramMatches(stream->prog, msg)) + goto cleanup; - case REMOTE_ERROR: - default: - ret = remoteStreamHandleAbort(client, stream, msg); - if (ret == 0) - qemudClientMessageRelease(client, msg); - break; - } + if (msg->header.proc != stream->procedure || + msg->header.serial != stream->serial) + goto cleanup; - if (ret < 0) - return -1; - } - return 1; - } - return 0; + VIR_DEBUG("Incoming client=%p, rx=%p, serial=%d, proc=%d, status=%d", + client, 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 stream * * 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("client=%p, proc=%d, serial=%d, st=%p", + client, header->proc, header->serial, st); if (VIR_ALLOC(stream) < 0) { virReportOOMError(); return NULL; } - stream->procedure = hdr->proc; - stream->serial = hdr->serial; + stream->priv = priv; + stream->prog = prog; + stream->procedure = header->proc; + stream->serial = header->serial; + stream->filterID = -1; + stream->st = st; - stream->st = virStreamNew(conn, VIR_STREAM_NONBLOCK); - if (!stream->st) { - VIR_FREE(stream); - return NULL; - } - - stream->filter.query = remoteStreamFilter; - stream->filter.opaque = stream; + virNetServerProgramRef(prog); return stream; } @@ -238,25 +308,37 @@ 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); + VIR_DEBUG("client=%p, proc=%d, serial=%d", + client, 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,63 +346,45 @@ 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 transmit) +int daemonAddClientStream(virNetServerClientPtr client, + daemonClientStream *stream, + bool transmit) { - struct qemud_client_stream *tmp = client->streams; + VIR_DEBUG("client=%p, proc=%d, serial=%d, st=%p, transmit=%d", + client, stream->procedure, stream->serial, stream->st, transmit); + daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client); - VIR_DEBUG("client=%p proc=%d serial=%d", client, stream->procedure, stream->serial); - - if (virStreamEventAddCallback(stream->st, 0, - remoteStreamEvent, client, NULL) < 0) + if (stream->filterID != -1) { + VIR_WARN("Filter already added to client %p", client); return -1; - - if (tmp) { - while (tmp->next) - tmp = tmp->next; - tmp->next = stream; - } else { - client->streams = stream; } - stream->filter.next = client->filters; - client->filters = &stream->filter; + if (virStreamEventAddCallback(stream->st, 0, + daemonStreamEvent, client, NULL) < 0) + return -1; + + if ((stream->filterID = virNetServerClientAddFilter(client, + daemonStreamFilter, + stream)) < 0) { + virStreamEventRemoveCallback(stream->st); + return -1; + } if (transmit) stream->tx = 1; - remoteStreamUpdateEvents(stream); + virMutexLock(&priv->lock); + stream->next = priv->streams; + priv->streams = stream; + + daemonStreamUpdateEvents(stream); + + virMutexUnlock(&priv->lock); return 0; } -/* - * @client: a locked client object - * @procedure: procedure associated with the stream - * @serial: serial number associated with the stream - * - * Finds a existing active stream - * - * Returns a stream object matching the procedure+serial number, or NULL - */ -struct qemud_client_stream * -remoteFindClientStream(struct qemud_client *client, - virStreamPtr st) -{ - struct qemud_client_stream *stream = client->streams; - - while (stream) { - if (stream->st == st) - return stream; - stream = stream->next; - } - - return NULL; -} - - /* * @client: a locked client object * @stream: an inactive, closed stream object @@ -330,26 +394,19 @@ 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); + VIR_DEBUG("client=%p, proc=%d, serial=%d, st=%p", + client, stream->procedure, stream->serial, stream->st); + daemonClientPrivatePtr priv = virNetServerClientGetPrivateData(client); + daemonClientStream *curr = priv->streams; + daemonClientStream *prev = NULL; - 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; - } + if (stream->filterID != -1) { + virNetServerClientRemoveFilter(client, + stream->filterID); + stream->filterID = -1; } if (!stream->closed) { @@ -362,9 +419,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 +436,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("client=%p, stream=%p, proc=%d, serial=%d, len=%zu, offset=%zu", + client, stream, msg->header.proc, msg->header.serial, + msg->bufferLength, msg->bufferOffset); ret = virStreamSend(stream->st, msg->buffer + msg->bufferOffset, @@ -402,14 +456,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 +484,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); + VIR_DEBUG("client=%p, stream=%p, proc=%d, serial=%d", + client, 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 +529,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("client=%p, stream=%p, proc=%d, serial=%d", + client, stream, msg->header.proc, msg->header.serial); + virNetMessageError rerr; - VIR_DEBUG("stream=%p proc=%d serial=%d", - stream, msg->hdr.proc, msg->hdr.serial); - - 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 +570,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("client=%p, stream=%p", client, stream); - 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,14 +621,14 @@ 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); + VIR_DEBUG("client=%p, stream=%p", client, stream); /* Shouldn't ever be called unless we're marked able to * transmit, but doesn't hurt to check */ @@ -572,47 +644,41 @@ 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; - ret = remoteSerializeStreamError(client, &rerr, stream->procedure, stream->serial); + memset(&rerr, 0, sizeof(rerr)); + + 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; + + if (msg) { + msg->cb = daemonStreamMessageFinished; + msg->opaque = stream; + virNetServerClientRef(client); + ret = virNetServerProgramSendStreamData(remoteProgram, + client, + msg, + stream->procedure, + stream->serial, + buffer, ret); + } } VIR_FREE(buffer); return ret; } - - -/* - * Invoked when an outgoing data packet message has been fully sent. - * This simply re-enables TX of further data. - * - * The idea is to stop the daemon growing without bound due to - * fast stream, but slow client - */ -void -remoteStreamMessageFinished(struct qemud_client *client, - struct qemud_client_message *msg) -{ - struct qemud_client_stream *stream = client->streams; - - while (stream) { - if (msg->hdr.proc == stream->procedure && - msg->hdr.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); - - if (stream) { - stream->tx = 1; - remoteStreamUpdateEvents(stream); - } -} diff --git a/daemon/stream.h b/daemon/stream.h index 767a76382a..626ae60914 100644 --- a/daemon/stream.h +++ b/daemon/stream.h @@ -28,27 +28,21 @@ -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 transmit); - -struct qemud_client_stream * -remoteFindClientStream(struct qemud_client *client, - virStreamPtr stream); +int daemonAddClientStream(virNetServerClientPtr client, + daemonClientStream *stream, + bool transmit); int -remoteRemoveClientStream(struct qemud_client *client, - struct qemud_client_stream *stream); - -void -remoteStreamMessageFinished(struct qemud_client *client, - struct qemud_client_message *msg); +daemonRemoveClientStream(virNetServerClientPtr client, + daemonClientStream *stream); #endif /* __LIBVIRTD_STREAM_H__ */ diff --git a/po/POTFILES.in b/po/POTFILES.in index 6b07386120..a81fc55230 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 d6264b9d90..027560c0a8 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@ 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,15 @@ elsif ($opt_b) { } if ($call->{streamflag} ne "none") { - print " if (!(stream = remoteCreateClientStream(conn, hdr)))\n"; + print " if (!(st = virStreamNew(priv->conn, VIR_STREAM_NONBLOCK)))\n"; + print " goto cleanup;\n"; + print "\n"; + print " if (!(stream = daemonCreateClientStream(client, st, remoteProgram, hdr)))\n"; print " 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 +801,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,12 +859,12 @@ 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"; + print "false"; } else { - print "1"; + print "true"; } print ") < 0)\n"; @@ -934,8 +908,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 +964,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 +977,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 +1165,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 +1248,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 +1412,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"; diff --git a/src/rpc/virnetmessage.c b/src/rpc/virnetmessage.c index 1cd3ab3656..62cdbc39c3 100644 --- a/src/rpc/virnetmessage.c +++ b/src/rpc/virnetmessage.c @@ -51,6 +51,9 @@ void virNetMessageFree(virNetMessagePtr msg) if (!msg) return; + if (msg->cb) + msg->cb(msg, msg->opaque); + VIR_DEBUG("msg=%p", msg); VIR_FREE(msg); diff --git a/src/rpc/virnetmessage.h b/src/rpc/virnetmessage.h index 4860998a98..921511259b 100644 --- a/src/rpc/virnetmessage.h +++ b/src/rpc/virnetmessage.h @@ -29,6 +29,8 @@ typedef struct virNetMessageError *virNetMessageErrorPtr; typedef struct _virNetMessage virNetMessage; typedef virNetMessage *virNetMessagePtr; +typedef void (*virNetMessageFreeCallback)(virNetMessagePtr msg, void *opaque); + /* Never allocate this (huge) buffer on the stack. Always * use virNetMessageNew() to allocate on the heap */ @@ -39,6 +41,9 @@ struct _virNetMessage { virNetMessageHeader header; + virNetMessageFreeCallback cb; + void *opaque; + virNetMessagePtr next; };