2010-12-01 16:35:50 +00:00
|
|
|
/*
|
|
|
|
* 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
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2010-12-01 16:35:50 +00:00
|
|
|
*
|
|
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2011-10-21 10:48:03 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2010-12-01 16:35:50 +00:00
|
|
|
#include "virnetclientprogram.h"
|
|
|
|
#include "virnetclient.h"
|
|
|
|
#include "virnetprotocol.h"
|
|
|
|
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2012-12-13 17:44:57 +00:00
|
|
|
#include "virutil.h"
|
2011-10-21 10:48:03 +00:00
|
|
|
#include "virfile.h"
|
2012-12-13 15:49:48 +00:00
|
|
|
#include "virthread.h"
|
2010-12-01 16:35:50 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_RPC
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("rpc.netclientprogram");
|
|
|
|
|
2010-12-01 16:35:50 +00:00
|
|
|
struct _virNetClientProgram {
|
2018-04-13 11:51:23 +00:00
|
|
|
virObject parent;
|
2010-12-01 16:35:50 +00:00
|
|
|
|
|
|
|
unsigned program;
|
|
|
|
unsigned version;
|
|
|
|
virNetClientProgramEventPtr events;
|
|
|
|
size_t nevents;
|
|
|
|
void *eventOpaque;
|
|
|
|
};
|
|
|
|
|
2012-07-30 09:14:56 +00:00
|
|
|
static virClassPtr virNetClientProgramClass;
|
|
|
|
static void virNetClientProgramDispose(void *obj);
|
|
|
|
|
|
|
|
static int virNetClientProgramOnceInit(void)
|
|
|
|
{
|
2018-04-17 15:42:33 +00:00
|
|
|
if (!VIR_CLASS_NEW(virNetClientProgram, virClassForObject()))
|
2012-07-30 09:14:56 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virNetClientProgram)
|
|
|
|
|
|
|
|
|
2010-12-01 16:35:50 +00:00
|
|
|
virNetClientProgramPtr virNetClientProgramNew(unsigned program,
|
|
|
|
unsigned version,
|
|
|
|
virNetClientProgramEventPtr events,
|
|
|
|
size_t nevents,
|
|
|
|
void *eventOpaque)
|
|
|
|
{
|
|
|
|
virNetClientProgramPtr prog;
|
|
|
|
|
2012-07-30 09:14:56 +00:00
|
|
|
if (virNetClientProgramInitialize() < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!(prog = virObjectNew(virNetClientProgramClass)))
|
2010-12-01 16:35:50 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
prog->program = program;
|
|
|
|
prog->version = version;
|
|
|
|
prog->events = events;
|
|
|
|
prog->nevents = nevents;
|
|
|
|
prog->eventOpaque = eventOpaque;
|
|
|
|
|
|
|
|
return prog;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-07-30 09:14:56 +00:00
|
|
|
void virNetClientProgramDispose(void *obj ATTRIBUTE_UNUSED)
|
2010-12-01 16:35:50 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-09-14 18:41:17 +00:00
|
|
|
if ((err.domain == VIR_FROM_REMOTE || err.domain == VIR_FROM_RPC) &&
|
2010-12-01 16:35:50 +00:00
|
|
|
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;
|
|
|
|
|
2014-03-25 06:52:31 +00:00
|
|
|
cleanup:
|
2010-12-01 16:35:50 +00:00
|
|
|
xdr_free((xdrproc_t)xdr_virNetMessageError, (void*)&err);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static virNetClientProgramEventPtr virNetClientProgramGetEvent(virNetClientProgramPtr prog,
|
|
|
|
int procedure)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/rpc/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2010-12-01 16:35:50 +00:00
|
|
|
|
2013-05-21 07:59:54 +00:00
|
|
|
for (i = 0; i < prog->nevents; i++) {
|
2010-12-01 16:35:50 +00:00
|
|
|
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) {
|
2017-09-25 10:43:33 +00:00
|
|
|
VIR_ERROR(_("program mismatch in event (actual 0x%x, expected 0x%x)"),
|
2010-12-01 16:35:50 +00:00
|
|
|
msg->header.prog, prog->program);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->header.vers != prog->version) {
|
2017-09-25 10:43:33 +00:00
|
|
|
VIR_ERROR(_("version mismatch in event (actual 0x%x, expected 0x%x)"),
|
2010-12-01 16:35:50 +00:00
|
|
|
msg->header.vers, prog->version);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->header.status != VIR_NET_OK) {
|
2017-09-25 10:43:33 +00:00
|
|
|
VIR_ERROR(_("status mismatch in event (actual 0x%x, expected 0x%x)"),
|
2010-12-01 16:35:50 +00:00
|
|
|
msg->header.status, VIR_NET_OK);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->header.type != VIR_NET_MESSAGE) {
|
2017-09-25 10:43:33 +00:00
|
|
|
VIR_ERROR(_("type mismatch in event (actual 0x%x, expected 0x%x)"),
|
2010-12-01 16:35:50 +00:00
|
|
|
msg->header.type, VIR_NET_MESSAGE);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
event = virNetClientProgramGetEvent(prog, msg->header.proc);
|
|
|
|
|
|
|
|
if (!event) {
|
2017-09-25 10:43:33 +00:00
|
|
|
VIR_ERROR(_("No event expected with procedure 0x%x"),
|
2010-12-01 16:35:50 +00:00
|
|
|
msg->header.proc);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-07-04 10:15:05 +00:00
|
|
|
if (VIR_ALLOC_N(evdata, event->msg_len) < 0)
|
2010-12-01 16:35:50 +00:00
|
|
|
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);
|
|
|
|
|
2014-03-25 06:52:31 +00:00
|
|
|
cleanup:
|
2010-12-01 16:35:50 +00:00
|
|
|
VIR_FREE(evdata);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int virNetClientProgramCall(virNetClientProgramPtr prog,
|
|
|
|
virNetClientPtr client,
|
|
|
|
unsigned serial,
|
|
|
|
int proc,
|
2011-10-21 10:48:03 +00:00
|
|
|
size_t noutfds,
|
|
|
|
int *outfds,
|
|
|
|
size_t *ninfds,
|
|
|
|
int **infds,
|
2010-12-01 16:35:50 +00:00
|
|
|
xdrproc_t args_filter, void *args,
|
|
|
|
xdrproc_t ret_filter, void *ret)
|
|
|
|
{
|
|
|
|
virNetMessagePtr msg;
|
2011-10-21 10:48:03 +00:00
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (infds)
|
|
|
|
*infds = NULL;
|
|
|
|
if (ninfds)
|
|
|
|
*ninfds = 0;
|
2010-12-01 16:35:50 +00:00
|
|
|
|
Fix tracking of RPC messages wrt streams
Commit 2c85644b0b51fbe5b6244e6773531af29933a727 attempted to
fix a problem with tracking RPC messages from streams by doing
- if (msg->header.type == VIR_NET_REPLY) {
+ if (msg->header.type == VIR_NET_REPLY ||
+ (msg->header.type == VIR_NET_STREAM &&
+ msg->header.status != VIR_NET_CONTINUE)) {
client->nrequests--;
In other words any stream packet, with status NET_OK or NET_ERROR
would cause nrequests to be decremented. This is great if the
packet from from a synchronous virStreamFinish or virStreamAbort
API call, but wildly wrong if from a server initiated abort.
The latter resulted in 'nrequests' being decremented below zero.
This then causes all I/O for that client to be stopped.
Instead of trying to infer whether we need to decrement the
nrequests field, from the message type/status, introduce an
explicit 'bool tracked' field to mark whether the virNetMessagePtr
object is subject to tracking.
Also add a virNetMessageClear function to allow a message
contents to be cleared out, without adversely impacting the
'tracked' field as a naive memset() would do
* src/rpc/virnetmessage.c, src/rpc/virnetmessage.h: Add
a 'bool tracked' field and virNetMessageClear() API
* daemon/remote.c, daemon/stream.c, src/rpc/virnetclientprogram.c,
src/rpc/virnetclientstream.c, src/rpc/virnetserverclient.c,
src/rpc/virnetserverprogram.c: Switch over to use
virNetMessageClear() and pass in the 'bool tracked' value
when creating messages.
2011-08-31 16:42:58 +00:00
|
|
|
if (!(msg = virNetMessageNew(false)))
|
2010-12-01 16:35:50 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
msg->header.prog = prog->program;
|
|
|
|
msg->header.vers = prog->version;
|
|
|
|
msg->header.status = VIR_NET_OK;
|
2011-10-21 10:48:03 +00:00
|
|
|
msg->header.type = noutfds ? VIR_NET_CALL_WITH_FDS : VIR_NET_CALL;
|
2010-12-01 16:35:50 +00:00
|
|
|
msg->header.serial = serial;
|
|
|
|
msg->header.proc = proc;
|
2017-06-07 08:46:39 +00:00
|
|
|
if (VIR_ALLOC_N(msg->fds, noutfds) < 0)
|
2011-10-21 10:48:03 +00:00
|
|
|
goto error;
|
2017-06-07 08:46:39 +00:00
|
|
|
msg->nfds = noutfds;
|
2013-05-21 07:59:54 +00:00
|
|
|
for (i = 0; i < msg->nfds; i++)
|
2011-10-21 10:48:03 +00:00
|
|
|
msg->fds[i] = -1;
|
2013-05-21 07:59:54 +00:00
|
|
|
for (i = 0; i < msg->nfds; i++) {
|
2011-10-21 10:48:03 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2010-12-01 16:35:50 +00:00
|
|
|
|
|
|
|
if (virNetMessageEncodeHeader(msg) < 0)
|
|
|
|
goto error;
|
|
|
|
|
2011-10-21 10:48:03 +00:00
|
|
|
if (msg->nfds &&
|
|
|
|
virNetMessageEncodeNumFDs(msg) < 0)
|
|
|
|
goto error;
|
|
|
|
|
2010-12-01 16:35:50 +00:00
|
|
|
if (virNetMessageEncodePayload(msg, args_filter, args) < 0)
|
|
|
|
goto error;
|
|
|
|
|
2011-11-11 15:42:46 +00:00
|
|
|
if (virNetClientSendWithReply(client, msg) < 0)
|
2010-12-01 16:35:50 +00:00
|
|
|
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.
|
|
|
|
*/
|
2011-10-21 10:48:03 +00:00
|
|
|
if (msg->header.type != VIR_NET_REPLY &&
|
|
|
|
msg->header.type != VIR_NET_REPLY_WITH_FDS) {
|
2012-07-18 10:41:47 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unexpected message type %d"), msg->header.type);
|
2010-12-01 16:35:50 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (msg->header.proc != proc) {
|
2012-07-18 10:41:47 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unexpected message proc %d != %d"),
|
|
|
|
msg->header.proc, proc);
|
2010-12-01 16:35:50 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (msg->header.serial != serial) {
|
2012-07-18 10:41:47 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unexpected message serial %d != %d"),
|
|
|
|
msg->header.serial, serial);
|
2010-12-01 16:35:50 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (msg->header.status) {
|
|
|
|
case VIR_NET_OK:
|
2011-10-21 10:48:03 +00:00
|
|
|
if (infds && ninfds) {
|
|
|
|
*ninfds = msg->nfds;
|
2013-07-04 10:15:05 +00:00
|
|
|
if (VIR_ALLOC_N(*infds, *ninfds) < 0)
|
2011-10-21 10:48:03 +00:00
|
|
|
goto error;
|
2013-05-21 07:59:54 +00:00
|
|
|
for (i = 0; i < *ninfds; i++)
|
2012-12-21 16:49:12 +00:00
|
|
|
(*infds)[i] = -1;
|
2013-05-21 07:59:54 +00:00
|
|
|
for (i = 0; i < *ninfds; i++) {
|
2012-12-21 16:49:12 +00:00
|
|
|
if (((*infds)[i] = dup(msg->fds[i])) < 0) {
|
2011-10-21 10:48:03 +00:00
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Cannot duplicate FD %d"),
|
|
|
|
msg->fds[i]);
|
|
|
|
goto error;
|
|
|
|
}
|
2012-12-21 16:49:12 +00:00
|
|
|
if (virSetInherit((*infds)[i], false) < 0) {
|
2011-10-21 10:48:03 +00:00
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Cannot set close-on-exec %d"),
|
2012-12-21 16:49:12 +00:00
|
|
|
(*infds)[i]);
|
2011-10-21 10:48:03 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2010-12-01 16:35:50 +00:00
|
|
|
if (virNetMessageDecodePayload(msg, ret_filter, ret) < 0)
|
|
|
|
goto error;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_NET_ERROR:
|
|
|
|
virNetClientProgramDispatchError(prog, msg);
|
|
|
|
goto error;
|
|
|
|
|
2018-02-14 09:43:59 +00:00
|
|
|
case VIR_NET_CONTINUE:
|
2010-12-01 16:35:50 +00:00
|
|
|
default:
|
2012-07-18 10:41:47 +00:00
|
|
|
virReportError(VIR_ERR_RPC,
|
|
|
|
_("Unexpected message status %d"), msg->header.status);
|
2010-12-01 16:35:50 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2011-07-08 11:35:36 +00:00
|
|
|
virNetMessageFree(msg);
|
2010-12-01 16:35:50 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2014-03-25 06:52:31 +00:00
|
|
|
error:
|
2011-07-08 11:35:36 +00:00
|
|
|
virNetMessageFree(msg);
|
2011-10-21 10:48:03 +00:00
|
|
|
if (infds && ninfds) {
|
2013-05-21 07:59:54 +00:00
|
|
|
for (i = 0; i < *ninfds; i++)
|
2012-12-21 16:49:12 +00:00
|
|
|
VIR_FORCE_CLOSE((*infds)[i]);
|
2011-10-21 10:48:03 +00:00
|
|
|
}
|
2010-12-01 16:35:50 +00:00
|
|
|
return -1;
|
|
|
|
}
|