mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-10-30 01:43:23 +00:00
10f94828ea
So far we are repeating the following lines over and over: if (!(virSomeObjectClass = virClassNew(virClassForObject(), "virSomeObject", sizeof(virSomeObject), virSomeObjectDispose))) return -1; While this works, it is impossible to do some checking. Firstly, the class name (the 2nd argument) doesn't match the name in the code in all cases (the 3rd argument). Secondly, the current style is needlessly verbose. This commit turns example into following: if (!(VIR_CLASS_NEW(virSomeObject, virClassForObject))) return -1; Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
403 lines
12 KiB
C
403 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, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include "virnetclientprogram.h"
|
|
#include "virnetclient.h"
|
|
#include "virnetprotocol.h"
|
|
|
|
#include "viralloc.h"
|
|
#include "virerror.h"
|
|
#include "virlog.h"
|
|
#include "virutil.h"
|
|
#include "virfile.h"
|
|
#include "virthread.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_RPC
|
|
|
|
VIR_LOG_INIT("rpc.netclientprogram");
|
|
|
|
struct _virNetClientProgram {
|
|
virObject parent;
|
|
|
|
unsigned program;
|
|
unsigned version;
|
|
virNetClientProgramEventPtr events;
|
|
size_t nevents;
|
|
void *eventOpaque;
|
|
};
|
|
|
|
static virClassPtr virNetClientProgramClass;
|
|
static void virNetClientProgramDispose(void *obj);
|
|
|
|
static int virNetClientProgramOnceInit(void)
|
|
{
|
|
if (!VIR_CLASS_NEW(virNetClientProgram, virClassForObject()))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virNetClientProgram)
|
|
|
|
|
|
virNetClientProgramPtr virNetClientProgramNew(unsigned program,
|
|
unsigned version,
|
|
virNetClientProgramEventPtr events,
|
|
size_t nevents,
|
|
void *eventOpaque)
|
|
{
|
|
virNetClientProgramPtr prog;
|
|
|
|
if (virNetClientProgramInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(prog = virObjectNew(virNetClientProgramClass)))
|
|
return NULL;
|
|
|
|
prog->program = program;
|
|
prog->version = version;
|
|
prog->events = events;
|
|
prog->nevents = nevents;
|
|
prog->eventOpaque = eventOpaque;
|
|
|
|
return prog;
|
|
}
|
|
|
|
|
|
void virNetClientProgramDispose(void *obj ATTRIBUTE_UNUSED)
|
|
{
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
size_t 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 0x%x, expected 0x%x)"),
|
|
msg->header.prog, prog->program);
|
|
return -1;
|
|
}
|
|
|
|
if (msg->header.vers != prog->version) {
|
|
VIR_ERROR(_("version mismatch in event (actual 0x%x, expected 0x%x)"),
|
|
msg->header.vers, prog->version);
|
|
return -1;
|
|
}
|
|
|
|
if (msg->header.status != VIR_NET_OK) {
|
|
VIR_ERROR(_("status mismatch in event (actual 0x%x, expected 0x%x)"),
|
|
msg->header.status, VIR_NET_OK);
|
|
return -1;
|
|
}
|
|
|
|
if (msg->header.type != VIR_NET_MESSAGE) {
|
|
VIR_ERROR(_("type mismatch in event (actual 0x%x, expected 0x%x)"),
|
|
msg->header.type, VIR_NET_MESSAGE);
|
|
return -1;
|
|
}
|
|
|
|
event = virNetClientProgramGetEvent(prog, msg->header.proc);
|
|
|
|
if (!event) {
|
|
VIR_ERROR(_("No event expected with procedure 0x%x"),
|
|
msg->header.proc);
|
|
return -1;
|
|
}
|
|
|
|
if (VIR_ALLOC_N(evdata, event->msg_len) < 0)
|
|
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;
|
|
if (VIR_ALLOC_N(msg->fds, noutfds) < 0)
|
|
goto error;
|
|
msg->nfds = noutfds;
|
|
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) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Unexpected message type %d"), msg->header.type);
|
|
goto error;
|
|
}
|
|
if (msg->header.proc != proc) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Unexpected message proc %d != %d"),
|
|
msg->header.proc, proc);
|
|
goto error;
|
|
}
|
|
if (msg->header.serial != serial) {
|
|
virReportError(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)
|
|
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;
|
|
|
|
case VIR_NET_CONTINUE:
|
|
default:
|
|
virReportError(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;
|
|
}
|