libvirt/src/rpc/virnetclientprogram.c
Daniel P. Berrange 5990f227e9 Split virNetClientSend into 2 methods
Stop multiplexing virNetClientSend for two different purposes,
instead add virNetClientSendWithReply and virNetClientSendNoReply

* src/rpc/virnetclient.c, src/rpc/virnetclient.h: Replace
  virNetClientSend with virNetClientSendWithReply and
  virNetClientSendNoReply
* src/rpc/virnetclientprogram.c, src/rpc/virnetclientstream.c:
  Update for new API names
2011-11-16 10:48:46 +00:00

408 lines
12 KiB
C

/*
* virnetclientprogram.c: generic network RPC client program
*
* Copyright (C) 2006-2011 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: Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include <unistd.h>
#include "virnetclientprogram.h"
#include "virnetclient.h"
#include "virnetprotocol.h"
#include "memory.h"
#include "virterror_internal.h"
#include "logging.h"
#include "util.h"
#include "virfile.h"
#define VIR_FROM_THIS VIR_FROM_RPC
#define virNetError(code, ...) \
virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \
__FUNCTION__, __LINE__, __VA_ARGS__)
struct _virNetClientProgram {
int refs;
unsigned program;
unsigned version;
virNetClientProgramEventPtr events;
size_t nevents;
void *eventOpaque;
};
virNetClientProgramPtr virNetClientProgramNew(unsigned program,
unsigned version,
virNetClientProgramEventPtr events,
size_t nevents,
void *eventOpaque)
{
virNetClientProgramPtr prog;
if (VIR_ALLOC(prog) < 0) {
virReportOOMError();
return NULL;
}
prog->refs = 1;
prog->program = program;
prog->version = version;
prog->events = events;
prog->nevents = nevents;
prog->eventOpaque = eventOpaque;
return prog;
}
void virNetClientProgramRef(virNetClientProgramPtr prog)
{
prog->refs++;
}
void virNetClientProgramFree(virNetClientProgramPtr prog)
{
if (!prog)
return;
prog->refs--;
if (prog->refs > 0)
return;
VIR_FREE(prog);
}
unsigned virNetClientProgramGetProgram(virNetClientProgramPtr prog)
{
return prog->program;
}
unsigned virNetClientProgramGetVersion(virNetClientProgramPtr prog)
{
return prog->version;
}
int virNetClientProgramMatches(virNetClientProgramPtr prog,
virNetMessagePtr msg)
{
if (prog->program == msg->header.prog &&
prog->version == msg->header.vers)
return 1;
return 0;
}
static int
virNetClientProgramDispatchError(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
virNetMessagePtr msg)
{
virNetMessageError err;
int ret = -1;
memset(&err, 0, sizeof(err));
if (virNetMessageDecodePayload(msg, (xdrproc_t)xdr_virNetMessageError, &err) < 0)
goto cleanup;
/* Interop for virErrorNumber glitch in 0.8.0, if server is
* 0.7.1 through 0.7.7; see comments in virterror.h. */
switch (err.code) {
case VIR_WAR_NO_NWFILTER:
/* no way to tell old VIR_WAR_NO_SECRET apart from
* VIR_WAR_NO_NWFILTER, but both are very similar
* warnings, so ignore the difference */
break;
case VIR_ERR_INVALID_NWFILTER:
case VIR_ERR_NO_NWFILTER:
case VIR_ERR_BUILD_FIREWALL:
/* server was trying to pass VIR_ERR_INVALID_SECRET,
* VIR_ERR_NO_SECRET, or VIR_ERR_CONFIG_UNSUPPORTED */
if (err.domain != VIR_FROM_NWFILTER)
err.code += 4;
break;
case VIR_WAR_NO_SECRET:
if (err.domain == VIR_FROM_QEMU)
err.code = VIR_ERR_OPERATION_TIMEOUT;
break;
case VIR_ERR_INVALID_SECRET:
if (err.domain == VIR_FROM_XEN)
err.code = VIR_ERR_MIGRATE_PERSIST_FAILED;
break;
default:
/* Nothing to alter. */
break;
}
if ((err.domain == VIR_FROM_REMOTE || err.domain == VIR_FROM_RPC) &&
err.code == VIR_ERR_RPC &&
err.level == VIR_ERR_ERROR &&
err.message &&
STRPREFIX(*err.message, "unknown procedure")) {
virRaiseErrorFull(__FILE__, __FUNCTION__, __LINE__,
err.domain,
VIR_ERR_NO_SUPPORT,
err.level,
err.str1 ? *err.str1 : NULL,
err.str2 ? *err.str2 : NULL,
err.str3 ? *err.str3 : NULL,
err.int1,
err.int2,
"%s", *err.message);
} else {
virRaiseErrorFull(__FILE__, __FUNCTION__, __LINE__,
err.domain,
err.code,
err.level,
err.str1 ? *err.str1 : NULL,
err.str2 ? *err.str2 : NULL,
err.str3 ? *err.str3 : NULL,
err.int1,
err.int2,
"%s", err.message ? *err.message : _("Unknown error"));
}
ret = 0;
cleanup:
xdr_free((xdrproc_t)xdr_virNetMessageError, (void*)&err);
return ret;
}
static virNetClientProgramEventPtr virNetClientProgramGetEvent(virNetClientProgramPtr prog,
int procedure)
{
int i;
for (i = 0 ; i < prog->nevents ; i++) {
if (prog->events[i].proc == procedure)
return &prog->events[i];
}
return NULL;
}
int virNetClientProgramDispatch(virNetClientProgramPtr prog,
virNetClientPtr client,
virNetMessagePtr msg)
{
virNetClientProgramEventPtr event;
char *evdata;
VIR_DEBUG("prog=%d ver=%d type=%d status=%d serial=%d proc=%d",
msg->header.prog, msg->header.vers, msg->header.type,
msg->header.status, msg->header.serial, msg->header.proc);
/* Check version, etc. */
if (msg->header.prog != prog->program) {
VIR_ERROR(_("program mismatch in event (actual %x, expected %x)"),
msg->header.prog, prog->program);
return -1;
}
if (msg->header.vers != prog->version) {
VIR_ERROR(_("version mismatch in event (actual %x, expected %x)"),
msg->header.vers, prog->version);
return -1;
}
if (msg->header.status != VIR_NET_OK) {
VIR_ERROR(_("status mismatch in event (actual %x, expected %x)"),
msg->header.status, VIR_NET_OK);
return -1;
}
if (msg->header.type != VIR_NET_MESSAGE) {
VIR_ERROR(_("type mismatch in event (actual %x, expected %x)"),
msg->header.type, VIR_NET_MESSAGE);
return -1;
}
event = virNetClientProgramGetEvent(prog, msg->header.proc);
if (!event) {
VIR_ERROR(_("No event expected with procedure %x"),
msg->header.proc);
return -1;
}
if (VIR_ALLOC_N(evdata, event->msg_len) < 0) {
virReportOOMError();
return -1;
}
if (virNetMessageDecodePayload(msg, event->msg_filter, evdata) < 0)
goto cleanup;
event->func(prog, client, evdata, prog->eventOpaque);
xdr_free(event->msg_filter, evdata);
cleanup:
VIR_FREE(evdata);
return 0;
}
int virNetClientProgramCall(virNetClientProgramPtr prog,
virNetClientPtr client,
unsigned serial,
int proc,
size_t noutfds,
int *outfds,
size_t *ninfds,
int **infds,
xdrproc_t args_filter, void *args,
xdrproc_t ret_filter, void *ret)
{
virNetMessagePtr msg;
size_t i;
if (infds)
*infds = NULL;
if (ninfds)
*ninfds = 0;
if (!(msg = virNetMessageNew(false)))
return -1;
msg->header.prog = prog->program;
msg->header.vers = prog->version;
msg->header.status = VIR_NET_OK;
msg->header.type = noutfds ? VIR_NET_CALL_WITH_FDS : VIR_NET_CALL;
msg->header.serial = serial;
msg->header.proc = proc;
msg->nfds = noutfds;
if (VIR_ALLOC_N(msg->fds, msg->nfds) < 0) {
virReportOOMError();
goto error;
}
for (i = 0 ; i < msg->nfds ; i++)
msg->fds[i] = -1;
for (i = 0 ; i < msg->nfds ; i++) {
if ((msg->fds[i] = dup(outfds[i])) < 0) {
virReportSystemError(errno,
_("Cannot duplicate FD %d"),
outfds[i]);
goto error;
}
if (virSetInherit(msg->fds[i], false) < 0) {
virReportSystemError(errno,
_("Cannot set close-on-exec %d"),
msg->fds[i]);
goto error;
}
}
if (virNetMessageEncodeHeader(msg) < 0)
goto error;
if (msg->nfds &&
virNetMessageEncodeNumFDs(msg) < 0)
goto error;
if (virNetMessageEncodePayload(msg, args_filter, args) < 0)
goto error;
if (virNetClientSendWithReply(client, msg) < 0)
goto error;
/* None of these 3 should ever happen here, because
* virNetClientSend should have validated the reply,
* but it doesn't hurt to check again.
*/
if (msg->header.type != VIR_NET_REPLY &&
msg->header.type != VIR_NET_REPLY_WITH_FDS) {
virNetError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected message type %d"), msg->header.type);
goto error;
}
if (msg->header.proc != proc) {
virNetError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected message proc %d != %d"),
msg->header.proc, proc);
goto error;
}
if (msg->header.serial != serial) {
virNetError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected message serial %d != %d"),
msg->header.serial, serial);
goto error;
}
switch (msg->header.status) {
case VIR_NET_OK:
if (infds && ninfds) {
*ninfds = msg->nfds;
if (VIR_ALLOC_N(*infds, *ninfds) < 0) {
virReportOOMError();
goto error;
}
for (i = 0 ; i < *ninfds ; i++)
*infds[i] = -1;
for (i = 0 ; i < *ninfds ; i++) {
if ((*infds[i] = dup(msg->fds[i])) < 0) {
virReportSystemError(errno,
_("Cannot duplicate FD %d"),
msg->fds[i]);
goto error;
}
if (virSetInherit(*infds[i], false) < 0) {
virReportSystemError(errno,
_("Cannot set close-on-exec %d"),
*infds[i]);
goto error;
}
}
}
if (virNetMessageDecodePayload(msg, ret_filter, ret) < 0)
goto error;
break;
case VIR_NET_ERROR:
virNetClientProgramDispatchError(prog, msg);
goto error;
default:
virNetError(VIR_ERR_RPC,
_("Unexpected message status %d"), msg->header.status);
goto error;
}
virNetMessageFree(msg);
return 0;
error:
virNetMessageFree(msg);
if (infds && ninfds) {
for (i = 0 ; i < *ninfds ; i++)
VIR_FORCE_CLOSE(*infds[i]);
}
return -1;
}