static void
virNetServerCheckLimits(virNetServer *srv)
{
+ size_t i;
+
+ for (i = 0; i < srv->nservices; i++) {
+ if (virNetServerServiceTimerActive(srv->services[i])) {
+ VIR_DEBUG("Skipping client-related limits evaluation");
+ return;
+ }
+ }
+
VIR_DEBUG("Checking client-related limits to re-enable or temporarily "
"suspend services: nclients=%zu nclients_max=%zu "
"nclients_unauth=%zu nclients_unauth_max=%zu",
int auth;
bool readonly;
size_t nrequests_client_max;
+ int timer;
+ bool timerActive;
virNetTLSContext *tls;
{
virNetServerService *svc = opaque;
virNetSocket *clientsock = NULL;
-
- if (virNetSocketAccept(sock, &clientsock) < 0)
+ int rc;
+
+ rc = virNetSocketAccept(sock, &clientsock);
+ if (rc < 0) {
+ if (rc == -2) {
+ /* Could not accept new client due to EMFILE. Suspend listening on
+ * the socket and set up a timer to enable it later. Hopefully,
+ * some FDs will be closed meanwhile. */
+ VIR_DEBUG("Temporarily suspending listening on svc=%p because accept() on sock=%p failed (errno=%d)",
+ svc, sock, errno);
+
+ virNetServerServiceToggle(svc, false);
+
+ svc->timerActive = true;
+ /* Retry in 5 seconds. */
+ virEventUpdateTimeout(svc->timer, 5 * 1000);
+ }
goto cleanup;
+ }
if (!clientsock) /* Connection already went away */
goto cleanup;
}
+static void
+virNetServerServiceTimerFunc(int timer,
+ void *opaque)
+{
+ virNetServerService *svc = opaque;
+
+ VIR_DEBUG("Resuming listening on service svc=%p after previous suspend", svc);
+
+ virNetServerServiceToggle(svc, true);
+
+ virEventUpdateTimeout(timer, -1);
+ svc->timerActive = false;
+}
+
+
static virNetServerService *
virNetServerServiceNewSocket(virNetSocket **socks,
size_t nsocks,
svc->nrequests_client_max = nrequests_client_max;
svc->tls = virObjectRef(tls);
+ virObjectRef(svc);
+ svc->timer = virEventAddTimeout(-1, virNetServerServiceTimerFunc,
+ svc, virObjectFreeCallback);
+ if (svc->timer < 0) {
+ virObjectUnref(svc);
+ goto error;
+ }
+
for (i = 0; i < svc->nsocks; i++) {
if (virNetSocketListen(svc->socks[i], max_queued_clients) < 0)
goto error;
virNetServerService *svc = obj;
size_t i;
+ if (svc->timer >= 0)
+ virEventRemoveTimeout(svc->timer);
+
for (i = 0; i < svc->nsocks; i++)
virObjectUnref(svc->socks[i]);
g_free(svc->socks);
virNetSocketClose(svc->socks[i]);
}
}
+
+
+bool
+virNetServerServiceTimerActive(virNetServerService *svc)
+{
+ return svc->timerActive;
+}
return 0;
}
+
+/**
+ * virNetSocketAccept:
+ * @sock: socket to accept connection on
+ * @clientsock: returned client socket
+ *
+ * For given socket @sock accept incoming connection and create
+ * @clientsock representation of the new accepted connection.
+ *
+ * Returns: 0 on success,
+ * -2 if accepting failed due to EMFILE error,
+ * -1 otherwise.
+ */
int virNetSocketAccept(virNetSocket *sock, virNetSocket **clientsock)
{
int fd = -1;
errno == EAGAIN) {
ret = 0;
goto cleanup;
+ } else if (errno == EMFILE) {
+ ret = -2;
}
virReportSystemError(errno, "%s",