libvirt/src/rpc/virnetserverservice.h
Michal Privoznik f3ab818984 rpc: Temporarily stop accept()-ing new clients on EMFILE
This commit is related to 5de203f879 which I pushed a few days
ago. While that commit prioritized closing clients socket over
the rest of I/O process, this one goes one step further and
temporarily suspends processing new connection requests.

A brief recapitulation of the problem:

1) assume that libvirt is at the top of RLIMIT_NOFILE (that is no
   new FDs can be opened).

2) we have a client trying to connect to a UNIX/TCP socket

Because of 2) our event loop sees POLLIN on the socket and thus
calls virNetServerServiceAccept(). But since no new FDs can be
opened (because of 1)) the request is not handled and we will get
the same event on next iteration. The poll() will exit
immediately because there is an event on the socket.  Thus we end
up in an endless loop.

To break the loop and stop burning CPU cycles we can stop
listening for events on the socket and set up a timer tho enable
listening again after some time (I chose 5 seconds because of no
obvious reason).

There's another area where we play with temporarily suspending
accept() of new clients - when a client disconnects and we check
max_clients against number of current clients. Problem here is
that max_clients can be orders of magnitude larger than
RLIMIT_NOFILE but more importantly, what this code considers
client disconnect is not equal to closing client's FD.
A client disconnecting means that the corresponding client
structure is removed from the internal list of clients. Closing
of the client's FD is done from event loop - asynchronously.

To avoid this part stepping on the toes of my fix, let's make the
code NOP if socket timer (as described above) is active.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2021-10-20 16:25:22 +02:00

83 lines
3.7 KiB
C

/*
* virnetserverservice.h: generic network RPC server service
*
* Copyright (C) 2006-2011, 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/>.
*/
#pragma once
#include "virnetserverprogram.h"
#include "virobject.h"
typedef enum {
VIR_NET_SERVER_SERVICE_AUTH_NONE = 0,
VIR_NET_SERVER_SERVICE_AUTH_SASL,
VIR_NET_SERVER_SERVICE_AUTH_POLKIT,
} virNetServerServiceAuthMethods;
typedef int (*virNetServerServiceDispatchFunc)(virNetServerService *svc,
virNetSocket *sock,
void *opaque);
virNetServerService *virNetServerServiceNewTCP(const char *nodename,
const char *service,
int family,
int auth,
virNetTLSContext *tls,
bool readonly,
size_t max_queued_clients,
size_t nrequests_client_max);
virNetServerService *virNetServerServiceNewUNIX(const char *path,
mode_t mask,
gid_t grp,
int auth,
virNetTLSContext *tls,
bool readonly,
size_t max_queued_clients,
size_t nrequests_client_max);
virNetServerService *virNetServerServiceNewFDs(int *fd,
size_t nfds,
bool unlinkUNIX,
int auth,
virNetTLSContext *tls,
bool readonly,
size_t max_queued_clients,
size_t nrequests_client_max);
virNetServerService *virNetServerServiceNewPostExecRestart(virJSONValue *object);
virJSONValue *virNetServerServicePreExecRestart(virNetServerService *service);
int virNetServerServiceGetPort(virNetServerService *svc);
int virNetServerServiceGetAuth(virNetServerService *svc);
bool virNetServerServiceIsReadonly(virNetServerService *svc);
size_t virNetServerServiceGetMaxRequests(virNetServerService *svc);
virNetTLSContext *virNetServerServiceGetTLSContext(virNetServerService *svc);
void virNetServerServiceSetDispatcher(virNetServerService *svc,
virNetServerServiceDispatchFunc func,
void *opaque);
void virNetServerServiceToggle(virNetServerService *svc,
bool enabled);
void virNetServerServiceClose(virNetServerService *svc);
bool virNetServerServiceTimerActive(virNetServerService *svc);