about summary refs log tree commit diff
path: root/nscd
diff options
context:
space:
mode:
Diffstat (limited to 'nscd')
-rw-r--r--nscd/connections.c118
-rw-r--r--nscd/nscd-client.h18
-rw-r--r--nscd/nscd_gethst_r.c23
-rw-r--r--nscd/nscd_helper.c10
4 files changed, 162 insertions, 7 deletions
diff --git a/nscd/connections.c b/nscd/connections.c
index 2b5c7ef53c..1b8a9bdba7 100644
--- a/nscd/connections.c
+++ b/nscd/connections.c
@@ -24,6 +24,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <grp.h>
+#include <ifaddrs.h>
 #include <libintl.h>
 #include <pthread.h>
 #include <pwd.h>
@@ -32,6 +33,9 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <arpa/inet.h>
+#ifdef HAVE_NETLINK
+# include <netlink/netlink.h>
+#endif
 #ifdef HAVE_EPOLL
 # include <sys/epoll.h>
 #endif
@@ -247,6 +251,11 @@ static int sock;
 int inotify_fd = -1;
 #endif
 
+#ifdef HAVE_NETLINK
+/* Descriptor for netlink status updates.  */
+static int nl_status_fd = -1;
+#endif
+
 #ifndef __ASSUME_SOCK_CLOEXEC
 /* Negative if SOCK_CLOEXEC is not supported, positive if it is, zero
    before be know the result.  */
@@ -903,6 +912,65 @@ cannot set socket to close on exec: %s; disabling paranoia mode"),
       exit (1);
     }
 
+#ifdef HAVE_NETLINK
+  if (dbs[hstdb].enabled)
+    {
+      /* Try to open netlink socket to monitor network setting changes.  */
+      nl_status_fd = socket (AF_NETLINK,
+			     SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
+			     NETLINK_ROUTE);
+      if (nl_status_fd != -1)
+	{
+	  struct sockaddr_nl snl;
+	  memset (&snl, '\0', sizeof (snl));
+	  snl.nl_family = AF_NETLINK;
+	  /* XXX Is this the best set to use?  */
+	  snl.nl_groups = (RTMGRP_IPV4_IFADDR | RTMGRP_TC | RTMGRP_IPV4_MROUTE
+			   | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_RULE
+			   | RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_MROUTE
+			   | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFINFO
+			   | RTMGRP_IPV6_PREFIX);
+
+	  if (bind (nl_status_fd, (struct sockaddr *) &snl, sizeof (snl)) != 0)
+	    {
+	      close (nl_status_fd);
+	      nl_status_fd = -1;
+	    }
+	  else
+	    {
+	      /* Start the timestamp process.  */
+	      dbs[hstdb].head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP]
+		= __bump_nl_timestamp ();
+
+# ifndef __ASSUME_SOCK_CLOEXEC
+	      if (have_sock_cloexec < 0)
+		{
+		  /* We don't want to get stuck on accept.  */
+		  int fl = fcntl (nl_status_fd, F_GETFL);
+		  if (fl == -1
+		      || fcntl (nl_status_fd, F_SETFL, fl | O_NONBLOCK) == -1)
+		    {
+		      dbg_log (_("\
+cannot change socket to nonblocking mode: %s"),
+			       strerror (errno));
+		      exit (1);
+		    }
+
+		  /* The descriptor needs to be closed on exec.  */
+		  if (paranoia
+		      && fcntl (nl_status_fd, F_SETFD, FD_CLOEXEC) == -1)
+		    {
+		      dbg_log (_("cannot set socket to close on exec: %s"),
+			       strerror (errno));
+		      exit (1);
+		    }
+		}
+# endif
+	    }
+	}
+    }
+#endif
+
   /* Change to unprivileged uid/gid/groups if specified in config file */
   if (server_user != NULL)
     finish_drop_privileges ();
@@ -1826,6 +1894,18 @@ main_loop_poll (void)
     }
 #endif
 
+#ifdef HAVE_NETLINK
+  size_t idx_nl_status_fd = 0;
+  if (nl_status_fd != -1)
+    {
+      idx_nl_status_fd = nused;
+      conns[nused].fd = nl_status_fd;
+      conns[nused].events = POLLRDNORM;
+      ++nused;
+      firstfree = nused;
+    }
+#endif
+
   while (1)
     {
       /* Wait for any event.  We wait at most a couple of seconds so
@@ -1968,6 +2048,20 @@ disabled inotify after read error %d"),
 	    }
 #endif
 
+#ifdef HAVE_NETLINK
+	  if (idx_nl_status_fd != 0 && conns[idx_nl_status_fd].revents != 0)
+	    {
+	      char buf[4096];
+	      /* Read all the data.  We do not interpret it here.  */
+	      while (TEMP_FAILURE_RETRY (read (nl_status_fd, buf,
+					       sizeof (buf))) != -1)
+		;
+
+	      dbs[hstdb].head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP]
+		= __bump_nl_timestamp ();
+	    }
+#endif
+
 	  for (size_t cnt = first; cnt < nused && n > 0; ++cnt)
 	    if (conns[cnt].revents != 0)
 	      {
@@ -2046,6 +2140,17 @@ main_loop_epoll (int efd)
     }
 # endif
 
+# ifdef HAVE_NETLINK
+  if (nl_status_fd != -1)
+    {
+      ev.events = EPOLLRDNORM;
+      ev.data.fd = nl_status_fd;
+      if (epoll_ctl (efd, EPOLL_CTL_ADD, nl_status_fd, &ev) == -1)
+	/* We cannot use epoll.  */
+	return;
+    }
+# endif
+
   while (1)
     {
       struct epoll_event revs[100];
@@ -2162,6 +2267,18 @@ main_loop_epoll (int efd)
 		}
 	  }
 # endif
+# ifdef HAVE_NETLINK
+	else if (revs[cnt].data.fd == nl_status_fd)
+	  {
+	    char buf[4096];
+	    /* Read all the data.  We do not interpret it here.  */
+	    while (TEMP_FAILURE_RETRY (read (nl_status_fd, buf,
+					     sizeof (buf))) != -1)
+	      ;
+
+	    __bump_nl_timestamp ();
+	  }
+# endif
 	else
 	  {
 	    /* Remove the descriptor from the epoll descriptor.  */
@@ -2185,6 +2302,7 @@ main_loop_epoll (int efd)
       time_t laststart = now - ACCEPT_TIMEOUT;
       assert (starttime[sock] == 0);
       assert (inotify_fd == -1 || starttime[inotify_fd] == 0);
+      assert (nl_status_fd == -1 || starttime[nl_status_fd] == 0);
       for (int cnt = highest; cnt > STDERR_FILENO; --cnt)
 	if (starttime[cnt] != 0 && starttime[cnt] < laststart)
 	  {
diff --git a/nscd/nscd-client.h b/nscd/nscd-client.h
index caad26a9cc..b5cd2d2c1e 100644
--- a/nscd/nscd-client.h
+++ b/nscd/nscd-client.h
@@ -260,12 +260,17 @@ struct hashentry
 
 
 /* Current persistent database version.  */
-#define DB_VERSION	1
+#define DB_VERSION	2
 
 /* Maximum time allowed between updates of the timestamp.  */
 #define MAPPING_TIMEOUT (5 * 60)
 
 
+/* Used indices for the EXTRA_DATA element of 'database_pers_head'.
+   Each database has its own indices.  */
+#define NSCD_HST_IDX_CONF_TIMESTAMP	0
+
+
 /* Header of persistent database file.  */
 struct database_pers_head
 {
@@ -274,6 +279,8 @@ struct database_pers_head
   volatile int32_t gc_cycle;
   volatile int32_t nscd_certainly_running;
   volatile nscd_time_t timestamp;
+  /* Room for extensions.  */
+  volatile uint32_t extra_data[4];
 
   nscd_ssize_t module;
   nscd_ssize_t data_size;
@@ -322,6 +329,12 @@ extern int __nscd_open_socket (const char *key, size_t keylen,
 			       request_type type, void *response,
 			       size_t responselen) attribute_hidden;
 
+/* Try to get a file descriptor for the shared meory segment
+   containing the database.  */
+extern struct mapped_database *__nscd_get_mapping (request_type type,
+						   const char *key,
+						   struct mapped_database **mappedp) attribute_hidden;
+
 /* Get reference of mapping.  */
 extern struct mapped_database *__nscd_get_map_ref (request_type type,
 						   const char *name,
@@ -371,4 +384,7 @@ extern ssize_t writeall (int fd, const void *buf, size_t len)
 extern ssize_t sendfileall (int tofd, int fromfd, off_t off, size_t len)
   attribute_hidden;
 
+/* Get netlink timestamp counter from mapped area or zero.  */
+extern uint32_t __nscd_get_nl_timestamp (void);
+
 #endif /* nscd.h */
diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c
index 70631fa961..6ee142d71b 100644
--- a/nscd/nscd_gethst_r.c
+++ b/nscd/nscd_gethst_r.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1998-2005, 2006, 2007, 2008, 2009
+/* Copyright (C) 1998-2005, 2006, 2007, 2008, 2009, 2011
    Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
@@ -98,6 +98,27 @@ libc_freeres_fn (hst_map_free)
 }
 
 
+uint32_t
+__nscd_get_nl_timestamp (void)
+{
+  if (__nss_not_use_nscd_hosts != 0)
+    return 0;
+
+  struct mapped_database *map = __hst_map_handle.mapped;
+
+  if (map == NULL
+      || (map != NO_MAPPING
+	  && map->head->nscd_certainly_running == 0
+	  && map->head->timestamp + MAPPING_TIMEOUT < time (NULL)))
+    map = __nscd_get_mapping (GETFDHST, "hosts", &__hst_map_handle.mapped);
+
+  if (map == NO_MAPPING)
+    return 0;
+
+  return map->head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP];
+}
+
+
 int __nss_have_localdomain attribute_hidden;
 
 static int
diff --git a/nscd/nscd_helper.c b/nscd/nscd_helper.c
index fe63f9a7fe..365b599892 100644
--- a/nscd/nscd_helper.c
+++ b/nscd/nscd_helper.c
@@ -277,9 +277,9 @@ __nscd_unmap (struct mapped_database *mapped)
 
 /* Try to get a file descriptor for the shared meory segment
    containing the database.  */
-static struct mapped_database *
-get_mapping (request_type type, const char *key,
-	     struct mapped_database **mappedp)
+struct mapped_database *
+__nscd_get_mapping (request_type type, const char *key,
+		    struct mapped_database **mappedp)
 {
   struct mapped_database *result = NO_MAPPING;
 #ifdef SCM_RIGHTS
@@ -449,8 +449,8 @@ __nscd_get_map_ref (request_type type, const char *name,
 	  || (cur->head->nscd_certainly_running == 0
 	      && cur->head->timestamp + MAPPING_TIMEOUT < time (NULL))
 	  || cur->head->data_size > cur->datasize)
-	cur = get_mapping (type, name,
-			   (struct mapped_database **) &mapptr->mapped);
+	cur = __nscd_get_mapping (type, name,
+				  (struct mapped_database **) &mapptr->mapped);
 
       if (__builtin_expect (cur != NO_MAPPING, 1))
 	{