libvirt/qemud/dispatch.c
Daniel P. Berrange a147ef3837 Split generic RPC message dispatch code out from remote protocol API handlers
* po/POTFILES.in: Add qemud/dispatch.c
* qemud/dispatch.c, qemud/dispatch.h: Generic code handling dispatch of
  RPC messages.
* qemud/Makefile.am: Add dispatch.c to build
* qemud/qemud.c: Include dispatch.h
* qemud/qemud.h: Remove remoteDispatchClientRequest, remoteRelayDomainEvent
  now in dispatch.h
* qemud/remote.c: Remove remoteDispatchClientRequest, remoteRelayDomainEvent
  now in dispatch.c, and dispatch_args, dispatch_ret, dispatch_fn & dispatch_data
  now in remote.h
* qemud/remote.h: Add typedefs for dispatch_args, dispatch_ret,
  dispatch_fn, dispath_data. Add remoteGetDispatchData() API
2009-07-16 16:09:41 +01:00

303 lines
8.8 KiB
C

/*
* dispatch.h: RPC message dispatching infrastructure
*
* Copyright (C) 2007, 2008, 2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Richard W.M. Jones <rjones@redhat.com>
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include "dispatch.h"
#include "remote.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 remoteDispatchOOMError (remote_error *rerr)
{
remoteDispatchStringError(rerr,
VIR_ERR_NO_MEMORY,
NULL);
}
void remoteDispatchConnError (remote_error *rerr,
virConnectPtr conn)
{
virErrorPtr verr;
if (conn)
verr = virConnGetLastError(conn);
else
verr = virGetLastError();
if (verr)
remoteDispatchCopyError(rerr, verr);
else
remoteDispatchGenericError(rerr);
}
/*
* @server: the unlocked server object
* @client: the locked client object
* @msg: the complete incoming message packet
*
* This function gets called from qemud when it pulls a incoming
* remote protocol messsage off the dispatch queue for processing.
*
*
* 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)
{
XDR xdr;
remote_message_header req, rep;
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);
/* Parse the header. */
xdrmem_create (&xdr,
msg->buffer + REMOTE_MESSAGE_HEADER_XDR_LEN,
msg->bufferLength - REMOTE_MESSAGE_HEADER_XDR_LEN,
XDR_DECODE);
if (!xdr_remote_message_header (&xdr, &req))
goto fatal_error;
/* Check version, etc. */
if (req.prog != REMOTE_PROGRAM) {
remoteDispatchFormatError (&rerr,
_("program mismatch (actual %x, expected %x)"),
req.prog, REMOTE_PROGRAM);
goto rpc_error;
}
if (req.vers != REMOTE_PROTOCOL_VERSION) {
remoteDispatchFormatError (&rerr,
_("version mismatch (actual %x, expected %x)"),
req.vers, REMOTE_PROTOCOL_VERSION);
goto rpc_error;
}
if (req.direction != REMOTE_CALL) {
remoteDispatchFormatError (&rerr, _("direction (%d) != REMOTE_CALL"),
(int) req.direction);
goto rpc_error;
}
if (req.status != REMOTE_OK) {
remoteDispatchFormatError (&rerr, _("status (%d) != REMOTE_OK"),
(int) req.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 (req.proc != REMOTE_PROC_AUTH_LIST &&
req.proc != REMOTE_PROC_AUTH_SASL_INIT &&
req.proc != REMOTE_PROC_AUTH_SASL_START &&
req.proc != REMOTE_PROC_AUTH_SASL_STEP &&
req.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;
}
}
data = remoteGetDispatchData(req.proc);
if (!data) {
remoteDispatchFormatError (&rerr, _("unknown procedure: %d"),
req.proc);
goto rpc_error;
}
/* De-serialize args off the wire */
if (!((data->args_filter)(&xdr, &args))) {
remoteDispatchFormatError (&rerr, "%s", _("parse args failed"));
goto rpc_error;
}
/* 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, &rerr, &args, &ret);
virMutexLock(&server->lock);
virMutexLock(&client->lock);
virMutexUnlock(&server->lock);
xdr_free (data->args_filter, (char*)&args);
rpc_error:
xdr_destroy (&xdr);
/* Return header. */
rep.prog = req.prog;
rep.vers = req.vers;
rep.proc = req.proc;
rep.direction = REMOTE_REPLY;
rep.serial = req.serial;
rep.status = rv < 0 ? REMOTE_ERROR : REMOTE_OK;
/* Serialise the return header. */
xdrmem_create (&xdr, msg->buffer, sizeof msg->buffer, XDR_ENCODE);
len = 0; /* We'll come back and write this later. */
if (!xdr_u_int (&xdr, &len)) {
if (rv == 0) xdr_free (data->ret_filter, (char*)&ret);
goto fatal_error;
}
if (!xdr_remote_message_header (&xdr, &rep)) {
if (rv == 0) xdr_free (data->ret_filter, (char*)&ret);
goto fatal_error;
}
/* If OK, serialise return structure, if error serialise error. */
if (rv >= 0) {
if (!((data->ret_filter) (&xdr, &ret)))
goto fatal_error;
xdr_free (data->ret_filter, (char*)&ret);
} else /* error */ {
/* Error was NULL so synthesize an error. */
if (rerr.code == 0)
remoteDispatchGenericError(&rerr);
if (!xdr_remote_error (&xdr, &rerr))
goto fatal_error;
xdr_free((xdrproc_t)xdr_remote_error, (char *)&rerr);
}
/* Write the length word. */
len = xdr_getpos (&xdr);
if (xdr_setpos (&xdr, 0) == 0)
goto fatal_error;
if (!xdr_u_int (&xdr, &len))
goto fatal_error;
xdr_destroy (&xdr);
msg->bufferLength = len;
msg->bufferOffset = 0;
return 0;
fatal_error:
/* Seriously bad stuff happened, so we'll kill off this client
and not send back any RPC error */
xdr_destroy (&xdr);
return -1;
}