mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-08 14:29:56 +00:00
e387f4c1f1
Coverity complained about the following: (3) Event ptr_arith: Performing pointer arithmetic on "cur_fd" in expression "cur_fd++". 130 return virNetServerServiceNewFD(*cur_fd++, The complaint is that pointer arithmetic taking place instead of the expected auto increment of the variable... Adding some well placed parentheses ensures our order of operation.
523 lines
15 KiB
C
523 lines
15 KiB
C
/*
|
|
* virnetserverservice.c: generic network RPC server service
|
|
*
|
|
* Copyright (C) 2006-2012, 2014 Red Hat, Inc.
|
|
* Copyright (C) 2006 Daniel P. Berrange
|
|
*
|
|
* 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 "virnetserverservice.h"
|
|
|
|
#include <unistd.h>
|
|
|
|
#include "viralloc.h"
|
|
#include "virerror.h"
|
|
#include "virthread.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_RPC
|
|
|
|
struct _virNetServerService {
|
|
virObject object;
|
|
|
|
size_t nsocks;
|
|
virNetSocketPtr *socks;
|
|
|
|
int auth;
|
|
bool readonly;
|
|
size_t nrequests_client_max;
|
|
|
|
#if WITH_GNUTLS
|
|
virNetTLSContextPtr tls;
|
|
#endif
|
|
|
|
virNetServerServiceDispatchFunc dispatchFunc;
|
|
void *dispatchOpaque;
|
|
};
|
|
|
|
|
|
static virClassPtr virNetServerServiceClass;
|
|
static void virNetServerServiceDispose(void *obj);
|
|
|
|
static int virNetServerServiceOnceInit(void)
|
|
{
|
|
if (!(virNetServerServiceClass = virClassNew(virClassForObject(),
|
|
"virNetServerService",
|
|
sizeof(virNetServerService),
|
|
virNetServerServiceDispose)))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virNetServerService)
|
|
|
|
|
|
static void virNetServerServiceAccept(virNetSocketPtr sock,
|
|
int events ATTRIBUTE_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virNetServerServicePtr svc = opaque;
|
|
virNetSocketPtr clientsock = NULL;
|
|
|
|
if (virNetSocketAccept(sock, &clientsock) < 0)
|
|
goto cleanup;
|
|
|
|
if (!clientsock) /* Connection already went away */
|
|
goto cleanup;
|
|
|
|
if (!svc->dispatchFunc)
|
|
goto cleanup;
|
|
|
|
svc->dispatchFunc(svc, clientsock, svc->dispatchOpaque);
|
|
|
|
cleanup:
|
|
virObjectUnref(clientsock);
|
|
}
|
|
|
|
|
|
virNetServerServicePtr
|
|
virNetServerServiceNewFDOrUNIX(const char *path,
|
|
mode_t mask,
|
|
gid_t grp,
|
|
int auth,
|
|
#if WITH_GNUTLS
|
|
virNetTLSContextPtr tls,
|
|
#endif
|
|
bool readonly,
|
|
size_t max_queued_clients,
|
|
size_t nrequests_client_max,
|
|
unsigned int nfds,
|
|
unsigned int *cur_fd)
|
|
{
|
|
if (*cur_fd - STDERR_FILENO > nfds) {
|
|
/*
|
|
* There are no more file descriptors to use, so we have to
|
|
* fallback to UNIX socket.
|
|
*/
|
|
return virNetServerServiceNewUNIX(path,
|
|
mask,
|
|
grp,
|
|
auth,
|
|
#if WITH_GNUTLS
|
|
tls,
|
|
#endif
|
|
readonly,
|
|
max_queued_clients,
|
|
nrequests_client_max);
|
|
|
|
} else {
|
|
/*
|
|
* There's still enough file descriptors. In this case we'll
|
|
* use the current one and increment it afterwards. Take care
|
|
* with order of operation for pointer arithmetic and auto
|
|
* increment on cur_fd - the parentheses are necessary.
|
|
*/
|
|
return virNetServerServiceNewFD((*cur_fd)++,
|
|
auth,
|
|
#if WITH_GNUTLS
|
|
tls,
|
|
#endif
|
|
readonly,
|
|
max_queued_clients,
|
|
nrequests_client_max);
|
|
}
|
|
}
|
|
|
|
|
|
virNetServerServicePtr virNetServerServiceNewTCP(const char *nodename,
|
|
const char *service,
|
|
int auth,
|
|
#if WITH_GNUTLS
|
|
virNetTLSContextPtr tls,
|
|
#endif
|
|
bool readonly,
|
|
size_t max_queued_clients,
|
|
size_t nrequests_client_max)
|
|
{
|
|
virNetServerServicePtr svc;
|
|
size_t i;
|
|
|
|
if (virNetServerServiceInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(svc = virObjectNew(virNetServerServiceClass)))
|
|
return NULL;
|
|
|
|
svc->auth = auth;
|
|
svc->readonly = readonly;
|
|
svc->nrequests_client_max = nrequests_client_max;
|
|
#if WITH_GNUTLS
|
|
svc->tls = virObjectRef(tls);
|
|
#endif
|
|
|
|
if (virNetSocketNewListenTCP(nodename,
|
|
service,
|
|
&svc->socks,
|
|
&svc->nsocks) < 0)
|
|
goto error;
|
|
|
|
for (i = 0; i < svc->nsocks; i++) {
|
|
if (virNetSocketListen(svc->socks[i], max_queued_clients) < 0)
|
|
goto error;
|
|
|
|
/* IO callback is initially disabled, until we're ready
|
|
* to deal with incoming clients */
|
|
virObjectRef(svc);
|
|
if (virNetSocketAddIOCallback(svc->socks[i],
|
|
0,
|
|
virNetServerServiceAccept,
|
|
svc,
|
|
virObjectFreeCallback) < 0) {
|
|
virObjectUnref(svc);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
|
|
return svc;
|
|
|
|
error:
|
|
virObjectUnref(svc);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
virNetServerServicePtr virNetServerServiceNewUNIX(const char *path,
|
|
mode_t mask,
|
|
gid_t grp,
|
|
int auth,
|
|
#if WITH_GNUTLS
|
|
virNetTLSContextPtr tls,
|
|
#endif
|
|
bool readonly,
|
|
size_t max_queued_clients,
|
|
size_t nrequests_client_max)
|
|
{
|
|
virNetServerServicePtr svc;
|
|
size_t i;
|
|
|
|
if (virNetServerServiceInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(svc = virObjectNew(virNetServerServiceClass)))
|
|
return NULL;
|
|
|
|
svc->auth = auth;
|
|
svc->readonly = readonly;
|
|
svc->nrequests_client_max = nrequests_client_max;
|
|
#if WITH_GNUTLS
|
|
svc->tls = virObjectRef(tls);
|
|
#endif
|
|
|
|
svc->nsocks = 1;
|
|
if (VIR_ALLOC_N(svc->socks, svc->nsocks) < 0)
|
|
goto error;
|
|
|
|
if (virNetSocketNewListenUNIX(path,
|
|
mask,
|
|
-1,
|
|
grp,
|
|
&svc->socks[0]) < 0)
|
|
goto error;
|
|
|
|
for (i = 0; i < svc->nsocks; i++) {
|
|
if (virNetSocketListen(svc->socks[i], max_queued_clients) < 0)
|
|
goto error;
|
|
|
|
/* IO callback is initially disabled, until we're ready
|
|
* to deal with incoming clients */
|
|
virObjectRef(svc);
|
|
if (virNetSocketAddIOCallback(svc->socks[i],
|
|
0,
|
|
virNetServerServiceAccept,
|
|
svc,
|
|
virObjectFreeCallback) < 0) {
|
|
virObjectUnref(svc);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
|
|
return svc;
|
|
|
|
error:
|
|
virObjectUnref(svc);
|
|
return NULL;
|
|
}
|
|
|
|
virNetServerServicePtr virNetServerServiceNewFD(int fd,
|
|
int auth,
|
|
#if WITH_GNUTLS
|
|
virNetTLSContextPtr tls,
|
|
#endif
|
|
bool readonly,
|
|
size_t max_queued_clients,
|
|
size_t nrequests_client_max)
|
|
{
|
|
virNetServerServicePtr svc;
|
|
size_t i;
|
|
|
|
if (virNetServerServiceInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(svc = virObjectNew(virNetServerServiceClass)))
|
|
return NULL;
|
|
|
|
svc->auth = auth;
|
|
svc->readonly = readonly;
|
|
svc->nrequests_client_max = nrequests_client_max;
|
|
#if WITH_GNUTLS
|
|
svc->tls = virObjectRef(tls);
|
|
#endif
|
|
|
|
svc->nsocks = 1;
|
|
if (VIR_ALLOC_N(svc->socks, svc->nsocks) < 0)
|
|
goto error;
|
|
|
|
if (virNetSocketNewListenFD(fd,
|
|
&svc->socks[0]) < 0)
|
|
goto error;
|
|
|
|
for (i = 0; i < svc->nsocks; i++) {
|
|
if (virNetSocketListen(svc->socks[i], max_queued_clients) < 0)
|
|
goto error;
|
|
|
|
/* IO callback is initially disabled, until we're ready
|
|
* to deal with incoming clients */
|
|
if (virNetSocketAddIOCallback(svc->socks[i],
|
|
0,
|
|
virNetServerServiceAccept,
|
|
svc,
|
|
virObjectFreeCallback) < 0)
|
|
goto error;
|
|
}
|
|
|
|
|
|
return svc;
|
|
|
|
error:
|
|
virObjectUnref(svc);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
virNetServerServicePtr virNetServerServiceNewPostExecRestart(virJSONValuePtr object)
|
|
{
|
|
virNetServerServicePtr svc;
|
|
virJSONValuePtr socks;
|
|
size_t i;
|
|
int n;
|
|
unsigned int max;
|
|
|
|
if (virNetServerServiceInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(svc = virObjectNew(virNetServerServiceClass)))
|
|
return NULL;
|
|
|
|
if (virJSONValueObjectGetNumberInt(object, "auth", &svc->auth) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Missing auth field in JSON state document"));
|
|
goto error;
|
|
}
|
|
if (virJSONValueObjectGetBoolean(object, "readonly", &svc->readonly) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Missing readonly field in JSON state document"));
|
|
goto error;
|
|
}
|
|
if (virJSONValueObjectGetNumberUint(object, "nrequests_client_max",
|
|
&max) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Missing nrequests_client_max field in JSON state document"));
|
|
goto error;
|
|
}
|
|
svc->nrequests_client_max = max;
|
|
|
|
if (!(socks = virJSONValueObjectGet(object, "socks"))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Missing socks field in JSON state document"));
|
|
goto error;
|
|
}
|
|
|
|
if ((n = virJSONValueArraySize(socks)) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("socks field in JSON was not an array"));
|
|
goto error;
|
|
}
|
|
|
|
svc->nsocks = n;
|
|
if (VIR_ALLOC_N(svc->socks, svc->nsocks) < 0)
|
|
goto error;
|
|
|
|
for (i = 0; i < svc->nsocks; i++) {
|
|
virJSONValuePtr child = virJSONValueArrayGet(socks, i);
|
|
virNetSocketPtr sock;
|
|
|
|
if (!(sock = virNetSocketNewPostExecRestart(child))) {
|
|
virObjectUnref(sock);
|
|
goto error;
|
|
}
|
|
|
|
svc->socks[i] = sock;
|
|
|
|
/* IO callback is initially disabled, until we're ready
|
|
* to deal with incoming clients */
|
|
virObjectRef(svc);
|
|
if (virNetSocketAddIOCallback(sock,
|
|
0,
|
|
virNetServerServiceAccept,
|
|
svc,
|
|
virObjectFreeCallback) < 0) {
|
|
virObjectUnref(svc);
|
|
virObjectUnref(sock);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
return svc;
|
|
|
|
error:
|
|
virObjectUnref(svc);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
virJSONValuePtr virNetServerServicePreExecRestart(virNetServerServicePtr svc)
|
|
{
|
|
virJSONValuePtr object = virJSONValueNewObject();
|
|
virJSONValuePtr socks;
|
|
size_t i;
|
|
|
|
if (!object)
|
|
return NULL;
|
|
|
|
if (virJSONValueObjectAppendNumberInt(object, "auth", svc->auth) < 0)
|
|
goto error;
|
|
if (virJSONValueObjectAppendBoolean(object, "readonly", svc->readonly) < 0)
|
|
goto error;
|
|
if (virJSONValueObjectAppendNumberUint(object, "nrequests_client_max", svc->nrequests_client_max) < 0)
|
|
goto error;
|
|
|
|
if (!(socks = virJSONValueNewArray()))
|
|
goto error;
|
|
|
|
if (virJSONValueObjectAppend(object, "socks", socks) < 0) {
|
|
virJSONValueFree(socks);
|
|
goto error;
|
|
}
|
|
|
|
for (i = 0; i < svc->nsocks; i++) {
|
|
virJSONValuePtr child;
|
|
if (!(child = virNetSocketPreExecRestart(svc->socks[i])))
|
|
goto error;
|
|
|
|
if (virJSONValueArrayAppend(socks, child) < 0) {
|
|
virJSONValueFree(child);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
return object;
|
|
|
|
error:
|
|
virJSONValueFree(object);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int virNetServerServiceGetPort(virNetServerServicePtr svc)
|
|
{
|
|
/* We're assuming if there are multiple sockets
|
|
* for IPv4 & 6, then they are all on same port */
|
|
return virNetSocketGetPort(svc->socks[0]);
|
|
}
|
|
|
|
|
|
int virNetServerServiceGetAuth(virNetServerServicePtr svc)
|
|
{
|
|
return svc->auth;
|
|
}
|
|
|
|
|
|
bool virNetServerServiceIsReadonly(virNetServerServicePtr svc)
|
|
{
|
|
return svc->readonly;
|
|
}
|
|
|
|
|
|
size_t virNetServerServiceGetMaxRequests(virNetServerServicePtr svc)
|
|
{
|
|
return svc->nrequests_client_max;
|
|
}
|
|
|
|
#if WITH_GNUTLS
|
|
virNetTLSContextPtr virNetServerServiceGetTLSContext(virNetServerServicePtr svc)
|
|
{
|
|
return svc->tls;
|
|
}
|
|
#endif
|
|
|
|
void virNetServerServiceSetDispatcher(virNetServerServicePtr svc,
|
|
virNetServerServiceDispatchFunc func,
|
|
void *opaque)
|
|
{
|
|
svc->dispatchFunc = func;
|
|
svc->dispatchOpaque = opaque;
|
|
}
|
|
|
|
|
|
void virNetServerServiceDispose(void *obj)
|
|
{
|
|
virNetServerServicePtr svc = obj;
|
|
size_t i;
|
|
|
|
for (i = 0; i < svc->nsocks; i++)
|
|
virObjectUnref(svc->socks[i]);
|
|
VIR_FREE(svc->socks);
|
|
|
|
#if WITH_GNUTLS
|
|
virObjectUnref(svc->tls);
|
|
#endif
|
|
}
|
|
|
|
void virNetServerServiceToggle(virNetServerServicePtr svc,
|
|
bool enabled)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < svc->nsocks; i++)
|
|
virNetSocketUpdateIOCallback(svc->socks[i],
|
|
enabled ?
|
|
VIR_EVENT_HANDLE_READABLE :
|
|
0);
|
|
}
|
|
|
|
void virNetServerServiceClose(virNetServerServicePtr svc)
|
|
{
|
|
size_t i;
|
|
|
|
if (!svc)
|
|
return;
|
|
|
|
for (i = 0; i < svc->nsocks; i++) {
|
|
virNetSocketClose(svc->socks[i]);
|
|
}
|
|
}
|