summary refs log tree commit diff
path: root/nscd
diff options
context:
space:
mode:
Diffstat (limited to 'nscd')
-rw-r--r--nscd/Makefile44
-rw-r--r--nscd/connections.c245
2 files changed, 198 insertions, 91 deletions
diff --git a/nscd/Makefile b/nscd/Makefile
index 95fd1ea610..4ccb0c2324 100644
--- a/nscd/Makefile
+++ b/nscd/Makefile
@@ -70,28 +70,28 @@ CFLAGS-nscd_getai.c = -fexceptions
 CFLAGS-nscd_initgroups.c = -fexceptions
 
 ifeq (yesyes,$(have-fpie)$(build-shared))
-CFLAGS-nscd.c = -fpie
-CFLAGS-connections.c = -fpie
-CFLAGS-pwdcache.c = -fpie
-CFLAGS-getpwnam_r.c = -fpie
-CFLAGS-getpwuid_r.c = -fpie
-CFLAGS-grpcache.c = -fpie
-CFLAGS-getgrnam_r.c = -fpie
-CFLAGS-getgrgid_r.c = -fpie
-CFLAGS-hstcache.c = -fpie
-CFLAGS-gethstbyad_r.c = -fpie
-CFLAGS-gethstbynm2_r.c = -fpie
-CFLAGS-dbg_log.c = -fpie
-CFLAGS-nscd_conf.c = -fpie
-CFLAGS-nscd_stat.c = -fpie
-CFLAGS-cache.c = -fpie
-CFLAGS-xmalloc.c = -fpie
-CFLAGS-xstrdup.c = -fpie
-CFLAGS-mem.c = -fpie
-CFLAGS-nscd_setup_thread.c = -fpie
-CFLAGS-aicache.c = -fpie
-CFLAGS-selinux.c = -fpie
-CFLAGS-initgrcache.c = -fpie
+CFLAGS-nscd.c += -fpie
+CFLAGS-connections.c += -fpie
+CFLAGS-pwdcache.c += -fpie
+CFLAGS-getpwnam_r.c += -fpie
+CFLAGS-getpwuid_r.c += -fpie
+CFLAGS-grpcache.c += -fpie
+CFLAGS-getgrnam_r.c += -fpie
+CFLAGS-getgrgid_r.c += -fpie
+CFLAGS-hstcache.c += -fpie
+CFLAGS-gethstbyad_r.c += -fpie
+CFLAGS-gethstbynm2_r.c += -fpie
+CFLAGS-dbg_log.c += -fpie
+CFLAGS-nscd_conf.c += -fpie
+CFLAGS-nscd_stat.c += -fpie
+CFLAGS-cache.c += -fpie
+CFLAGS-xmalloc.c += -fpie
+CFLAGS-xstrdup.c += -fpie
+CFLAGS-mem.c += -fpie
+CFLAGS-nscd_setup_thread.c += -fpie
+CFLAGS-aicache.c += -fpie
+CFLAGS-selinux.c += -fpie
+CFLAGS-initgrcache.c += -fpie
 
 $(objpfx)nscd: $(addprefix $(objpfx),$(nscd-modules:=.o))
 	$(LINK.o) -pie -Wl,-O1 \
diff --git a/nscd/connections.c b/nscd/connections.c
index b658fddb25..8b167aab71 100644
--- a/nscd/connections.c
+++ b/nscd/connections.c
@@ -32,6 +32,9 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <arpa/inet.h>
+#ifdef HAVE_EPOLL
+# include <sys/epoll.h>
+#endif
 #include <sys/mman.h>
 #include <sys/param.h>
 #include <sys/poll.h>
@@ -1017,34 +1020,56 @@ handle_request: request received (Version = %d)"), req.version);
 }
 
 
+static unsigned int nconns;
+
 static void
-__attribute__ ((__noreturn__))
-main_loop (void)
+fd_ready (int fd)
 {
-  /* Determine how much room for descriptors we should initially
-     allocate.  This might need to change later if we cap the number
-     with MAXCONN.  */
-  const long int nfds = sysconf (_SC_OPEN_MAX);
-  unsigned int nconns;
-#define MINCONN 32
-#define MAXCONN 16384
-  if (nfds == -1 || nfds > MAXCONN)
-    nconns = MAXCONN;
-  else if (nfds < MINCONN)
-    nconns = MINCONN;
+  pthread_mutex_lock (&readylist_lock);
+
+  /* Find an empty entry in FDLIST.  */
+  size_t inner;
+  for (inner = 0; inner < nconns; ++inner)
+    if (fdlist[inner].next == NULL)
+      break;
+  assert (inner < nconns);
+
+  fdlist[inner].fd = fd;
+
+  if (readylist == NULL)
+    readylist = fdlist[inner].next = &fdlist[inner];
   else
-    nconns = nfds;
+    {
+      fdlist[inner].next = readylist->next;
+      readylist = readylist->next = &fdlist[inner];
+    }
+
+  bool do_signal = true;
+  if (__builtin_expect (nready == 0, 0))
+    {
+      ++client_queued;
+      do_signal = false;
+    }
+
+  pthread_mutex_unlock (&readylist_lock);
+
+  /* Tell one of the worker threads there is work to do.  */
+  if (do_signal)
+    pthread_cond_signal (&readylist_cond);
+}
 
+
+/* Time a connection was accepted.  */
+static time_t *starttime;
+
+
+static void
+__attribute__ ((__noreturn__))
+main_loop_poll (void)
+{
   struct pollfd *conns = (struct pollfd *) xmalloc (nconns
 						    * sizeof (conns[0]));
 
-  /* We need two mirroring arrays filled with the times the connection
-     was accepted and a place to pass descriptors on to the worker
-     threads.  We cannot put this in the same data structure as the
-     CONNS data since CONNS is passed as an array to poll().  */
-  time_t *starttime = (time_t *) xmalloc (nconns * sizeof (starttime[0]));
-  fdlist = (struct fdlist *) xcalloc (nconns, sizeof (fdlist[0]));
-
   conns[0].fd = sock;
   conns[0].events = POLLRDNORM;
   size_t nused = 1;
@@ -1074,17 +1099,9 @@ main_loop (void)
 	      /* We have a new incoming connection.  Accept the connection.  */
 	      int fd = TEMP_FAILURE_RETRY (accept (sock, NULL, NULL));
 
-	      if (fd >= 0)
+	      /* use the descriptor if we have not reached the limit.  */
+	      if (fd >= 0 && firstfree < nconns)
 		{
-		  /* We have a new file descriptor.  Keep it around
-		     and wait until data becomes available.  */
-		  if (firstfree == nconns)
-		    {
-		      // XXX Maybe extend array.  For now, reject
-		      close (fd);
-		      goto reject_out;
-		    }
-
 		  conns[firstfree].fd = fd;
 		  conns[firstfree].events = POLLRDNORM;
 		  starttime[firstfree] = now;
@@ -1096,44 +1113,13 @@ main_loop (void)
 		  while (firstfree < nused && conns[firstfree].fd != -1);
 		}
 
-	    reject_out:
 	      --n;
 	    }
 
 	  for (size_t cnt = 1; cnt < nused && n > 0; ++cnt)
 	    if (conns[cnt].revents != 0)
 	      {
-		pthread_mutex_lock (&readylist_lock);
-
-		/* Find an empty entry in FDLIST.  */
-		size_t inner;
-		for (inner = 0; inner < nconns; ++inner)
-		  if (fdlist[inner].next == NULL)
-		    break;
-		assert (inner < nconns);
-
-		fdlist[inner].fd = conns[cnt].fd;
-
-		if (readylist == NULL)
-		  readylist = fdlist[inner].next = &fdlist[inner];
-		else
-		  {
-		    fdlist[inner].next = readylist->next;
-		    readylist = readylist->next = &fdlist[inner];
-		  }
-
-		bool do_signal = true;
-		if (__builtin_expect (nready == 0, 0))
-		  {
-		    ++client_queued;
-		    do_signal = false;
-		  }
-
-		pthread_mutex_unlock (&readylist_lock);
-
-		/* Tell one of the worker threads there is work to do.  */
-		if (do_signal)
-		  pthread_cond_signal (&readylist_cond);
+		fd_ready (conns[cnt].fd);
 
 		/* Clean up the CONNS array.  */
 		conns[cnt].fd = -1;
@@ -1150,15 +1136,16 @@ main_loop (void)
 
       /* Now find entries which have timed out.  */
       assert (nused > 0);
-      for (size_t cnt = nused - 1; cnt > 0; --cnt)
-	{
-	  /* We make the timeout length depend on the number of file
-	     descriptors currently used.  */
+
+      /* We make the timeout length depend on the number of file
+	 descriptors currently used.  */
 #define ACCEPT_TIMEOUT \
   (MAX_ACCEPT_TIMEOUT							      \
    - ((MAX_ACCEPT_TIMEOUT - MIN_ACCEPT_TIMEOUT) * nused) / nconns)
+      time_t laststart = now - ACCEPT_TIMEOUT;
 
-	  time_t laststart = now - ACCEPT_TIMEOUT;
+      for (size_t cnt = nused - 1; cnt > 0; --cnt)
+	{
 	  if (conns[cnt].fd != -1 && starttime[cnt] < laststart)
 	    {
 	      /* Remove the entry, it timed out.  */
@@ -1177,6 +1164,99 @@ main_loop (void)
 }
 
 
+#ifdef HAVE_EPOLL
+static void
+main_loop_epoll (int efd)
+{
+  struct epoll_event ev = { 0, };
+  int nused = 1;
+  size_t highest = 0;
+
+  /* Add the socket.  */
+  ev.events = EPOLLRDNORM;
+  ev.data.fd = sock;
+  if (epoll_ctl (efd, EPOLL_CTL_ADD, sock, &ev) == -1)
+    /* We cannot use epoll.  */
+    return;
+
+  while (1)
+    {
+      struct epoll_event revs[100];
+# define nrevs (sizeof (revs) / sizeof (revs[0]))
+
+      int n = epoll_wait (efd, revs, nrevs, MAIN_THREAD_TIMEOUT);
+
+      time_t now = time (NULL);
+
+      for (int cnt = 0; cnt < n; ++cnt)
+	if (revs[cnt].data.fd == sock)
+	  {
+	    /* A new connection.  */
+	    int fd = TEMP_FAILURE_RETRY (accept (sock, NULL, NULL));
+
+	    if (fd >= 0)
+	      {
+		/* Try to add the  new descriptor.  */
+		ev.data.fd = fd;
+		if (fd >= nconns
+		    || epoll_ctl (efd, EPOLL_CTL_ADD, fd, &ev) == -1)
+		  /* The descriptor is too large or something went
+		     wrong.  Close the descriptor.  */
+		  close (fd);
+		else
+		  {
+		    /* Remember when we accepted the connection.  */
+		    starttime[fd] = now;
+
+		    if (fd > highest)
+		      highest = fd;
+
+		    ++nused;
+		  }
+	      }
+	  }
+	else
+	  {
+	    /* Remove the descriptor from the epoll descriptor.  */
+	    struct epoll_event ev = { 0, };
+	    (void) epoll_ctl (efd, EPOLL_CTL_DEL, revs[cnt].data.fd, &ev);
+
+	    /* Get a worked to handle the request.  */
+	    fd_ready (revs[cnt].data.fd);
+
+	    /* Reset the time.  */
+	    starttime[revs[cnt].data.fd] = 0;
+	    if (revs[cnt].data.fd == highest)
+	      do
+		--highest;
+	      while (highest > 0 && starttime[highest] == 0);
+
+	    --nused;
+	  }
+
+      /*  Now look for descriptors for accepted connections which have
+	  no reply in too long of a time.  */
+      time_t laststart = now - ACCEPT_TIMEOUT;
+      for (int cnt = highest; cnt > STDERR_FILENO; --cnt)
+	if (cnt != sock && starttime[cnt] != 0 && starttime[cnt] < laststart)
+	  {
+	    /* We are waiting for this one for too long.  Close it.  */
+	    struct epoll_event ev = {0, };
+	    (void) epoll_ctl (efd, EPOLL_CTL_DEL, cnt, &ev);
+
+	    (void) close (cnt);
+
+	    starttime[cnt] = 0;
+	    if (cnt == highest)
+	      --highest;
+	  }
+	else if (cnt != sock && starttime[cnt] == 0 && cnt == highest)
+	  --highest;
+    }
+}
+#endif
+
+
 /* Start all the threads we want.  The initial process is thread no. 1.  */
 void
 start_threads (void)
@@ -1216,9 +1296,36 @@ start_threads (void)
 
   pthread_attr_destroy (&attr);
 
+  /* Determine how much room for descriptors we should initially
+     allocate.  This might need to change later if we cap the number
+     with MAXCONN.  */
+  const long int nfds = sysconf (_SC_OPEN_MAX);
+#define MINCONN 32
+#define MAXCONN 16384
+  if (nfds == -1 || nfds > MAXCONN)
+    nconns = MAXCONN;
+  else if (nfds < MINCONN)
+    nconns = MINCONN;
+  else
+    nconns = nfds;
+
+  /* We need memory to pass descriptors on to the worker threads.  */
+  fdlist = (struct fdlist *) xcalloc (nconns, sizeof (fdlist[0]));
+  /* Array to keep track when connection was accepted.  */
+  starttime = (time_t *) xcalloc (nconns, sizeof (starttime[0]));
+
   /* In the main thread we execute the loop which handles incoming
      connections.  */
-  main_loop ();
+#ifdef HAVE_EPOLL
+  int efd = epoll_create (100);
+  if (efd != -1)
+    {
+      main_loop_epoll (efd);
+      close (efd);
+    }
+#endif
+
+  main_loop_poll ();
 }