This is the mail archive of the glibc-cvs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

GNU C Library master sources branch, master, updated. glibc-2.14-325-g684ae51


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, master has been updated
       via  684ae515993269277448150a1ca70db3b94aa5bd (commit)
      from  21fd49a9ef9c21cd2c87d0facf9f53bedb5cea20 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://sources.redhat.com/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=684ae515993269277448150a1ca70db3b94aa5bd

commit 684ae515993269277448150a1ca70db3b94aa5bd
Author: Ulrich Drepper <drepper@gmail.com>
Date:   Fri Oct 7 10:06:31 2011 -0400

    Implement caching of nscd

diff --git a/ChangeLog b/ChangeLog
index edd2728..08e46b6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,45 @@
 2011-10-07  Ulrich Drepper  <drepper@gmail.com>
 
+	* inet/getnetgrent_r.c: Hook up nscd.
+	* nscd/Makefile (routines): Add nscd_netgroup.
+	(nscd-modules): Add netgroupcache.
+	(CFLAGS-netgroupcache.c): Define.
+	* nscd/cache.c (readdfcts): Add entries for GETNETGRENT and INNETGR.
+	(cache_search): Add const to second parameter.
+	* nscd/connections.c (serv2str): Add entries for GETNETGRENT and
+	INNETGR.
+	(dbs): Add netgrdb entry.
+	(reqinfo): Add entries for GETNETGRENT, INNETGR, and GETFDNETGR.
+	(verify_persistent_db): Handle netgrdb.
+	(handle_request): Handle GETNETGRENT, INNETGR, and GETFDNETGR.
+	* nscd/nscd-client.h (request_type): Add GETNETGRENT, INNETGR, and
+	GETFDNETGR.
+	(netgroup_response_header): Define.
+	(innetgroup_response_header): Define.
+	(datahead): Add netgroup_response_header and innetgroup_response_header
+	elements.
+	* nscd/nscd.conf: Add entries for netgroup cache.
+	* nscd/nscd.h (dbtype): Add netgrdb.
+	(_PATH_NSCD_NETGROUP_DB): Define.
+	(netgroup_iov_disabled): Declare.
+	(xmalloc, xcalloc, xrealloc): Move declarations here.
+	(cache_search): Adjust prototype.
+	Add netgroup-related prototypes.
+	* nscd/nscd_conf.c (dbnames): Add netgrdb entry.
+	* nscd/nscd_proto.h (__nss_not_use_nscd_netgroup): Declare.
+	(__nscd_innetgr): Declare.
+	* nscd/selinux.c (perms): Use access_vector_t as element type and
+	add netgroup-related initializers.
+	* nscd/netgroupcache.c: New file.
+	* nscd/nscd_netgroup.c: New file.
+	* nss/Versions [libc] (GLIBC_PRIVATE): Export __nss_lookup.
+	* nss/getent.c (netgroup_keys): Use setnetgrent only for one parameter.
+	For four parameters use innetgr.
+	* nss/nss_files/files-init.c: Add definition and callback for netgr.
+	* nss/nsswitch.c (__nss_lookup): Add libc_hidden_def.
+	(__nss_disable_nscd): Set __nss_not_use_nscd_netgroup.
+	* nss/nsswitch.h (__nss_lookup): Add libc_hidden_proto.
+
 	* nscd/connections.c (register_traced_file): Don't register file
 	for disabled databases.
 
diff --git a/NEWS b/NEWS
index 8e22f43..e9bf1ff 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU C Library NEWS -- history of user-visible changes.  2011-9-11
+GNU C Library NEWS -- history of user-visible changes.  2011-10-7
 Copyright (C) 1992-2009, 2010, 2011 Free Software Foundation, Inc.
 See the end for copying conditions.
 
@@ -38,6 +38,9 @@ Version 2.15
 
 * Checking versions of FD_SET, FD_CLR, and FD_ISSET added.
   Implemented by Ulrich Drepper.
+
+* nscd now also caches the netgroup database.
+  Implemented by Ulrich Drepper.
 
 Version 2.14
 
diff --git a/inet/getnetgrent_r.c b/inet/getnetgrent_r.c
index 6aaba7b..8790387 100644
--- a/inet/getnetgrent_r.c
+++ b/inet/getnetgrent_r.c
@@ -28,6 +28,7 @@
 #include "netgroup.h"
 #include "nsswitch.h"
 #include <sysdep.h>
+#include <nscd/nscd_proto.h>
 
 
 /* Protect above variable against multiple uses at the same time.  */
@@ -101,7 +102,7 @@ endnetgrent_hook (struct __netgrent *datap)
 {
   enum nss_status (*endfct) (struct __netgrent *);
 
-  if (datap->nip == NULL)
+  if (datap->nip == NULL || datap->nip == (service_user *) -1l)
     return;
 
   endfct = __nss_lookup_function (datap->nip, "endnetgrent");
@@ -189,8 +190,21 @@ setnetgrent (const char *group)
 
   __libc_lock_lock (lock);
 
+  if (__nss_not_use_nscd_netgroup > 0
+      && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
+    __nss_not_use_nscd_netgroup = 0;
+
+  if (!__nss_not_use_nscd_netgroup
+      && !__nss_database_custom[NSS_DBSIDX_netgroup])
+    {
+      result = __nscd_setnetgrent (group, &dataset);
+      if (result >= 0)
+	goto out;
+    }
+
   result = internal_setnetgrent (group, &dataset);
 
+ out:
   __libc_lock_unlock (lock);
 
   return result;
@@ -226,6 +240,26 @@ int internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
 			    char *buffer, size_t buflen, int *errnop);
 libc_hidden_proto (internal_getnetgrent_r)
 
+
+static enum nss_status
+nscd_getnetgrent (struct __netgrent *datap, char *buffer, size_t buflen,
+		  int *errnop)
+{
+  if (datap->cursor >= datap->data + datap->data_size)
+    return NSS_STATUS_UNAVAIL;
+
+  datap->type = triple_val;
+  datap->val.triple.host = datap->cursor;
+  datap->cursor = (char *) rawmemchr (datap->cursor, '\0') + 1;
+  datap->val.triple.user = datap->cursor;
+  datap->cursor = (char *) rawmemchr (datap->cursor, '\0') + 1;
+  datap->val.triple.domain = datap->cursor;
+  datap->cursor = (char *) rawmemchr (datap->cursor, '\0') + 1;
+
+  return NSS_STATUS_SUCCESS;
+}
+
+
 int
 internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
 			  struct __netgrent *datap,
@@ -239,9 +273,18 @@ internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
   /* Run through available functions, starting with the same function last
      run.  We will repeat each function as long as it succeeds, and then go
      on to the next service action.  */
-  int no_more = (datap->nip == NULL
-		 || (fct = __nss_lookup_function (datap->nip, "getnetgrent_r"))
-		    == NULL);
+  int no_more = datap->nip == NULL;
+  if (! no_more)
+    {
+      if (datap->nip == (service_user *) -1l)
+	fct = nscd_getnetgrent;
+      else
+	{
+	  fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
+	  no_more = fct == NULL;
+	}
+    }
+
   while (! no_more)
     {
       status = DL_CALL_FCT (*fct, (datap, buffer, buflen, &errno));
@@ -342,6 +385,18 @@ int
 innetgr (const char *netgroup, const char *host, const char *user,
 	 const char *domain)
 {
+  if (__nss_not_use_nscd_netgroup > 0
+      && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
+    __nss_not_use_nscd_netgroup = 0;
+
+  if (!__nss_not_use_nscd_netgroup
+      && !__nss_database_custom[NSS_DBSIDX_netgroup])
+    {
+      int result = __nscd_innetgr (netgroup, host, user, domain);
+      if (result >= 0)
+	return result;
+    }
+
   union
   {
     enum nss_status (*f) (const char *, struct __netgrent *);
@@ -453,7 +508,7 @@ innetgr (const char *netgroup, const char *host, const char *user,
 	  entry.needed_groups = tmp->next;
 	  tmp->next = entry.known_groups;
 	  entry.known_groups = tmp;
-	  current_group = entry.known_groups->name;
+	  current_group = tmp->name;
 	  continue;
 	}
 
diff --git a/nscd/Makefile b/nscd/Makefile
index 097e6f0..be0afed 100644
--- a/nscd/Makefile
+++ b/nscd/Makefile
@@ -22,7 +22,7 @@
 subdir	:= nscd
 
 routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r nscd_getai \
-	    nscd_initgroups nscd_getserv_r
+	    nscd_initgroups nscd_getserv_r nscd_netgroup
 aux	:= nscd_helper
 
 include ../Makeconfig
@@ -34,7 +34,8 @@ nscd-modules := nscd connections pwdcache getpwnam_r getpwuid_r grpcache \
 		getgrnam_r getgrgid_r hstcache gethstbyad_r gethstbynm3_r \
 		getsrvbynm_r getsrvbypt_r servicescache \
 		dbg_log nscd_conf nscd_stat cache mem nscd_setup_thread \
-		xmalloc xstrdup aicache initgrcache gai res_hconf
+		xmalloc xstrdup aicache initgrcache gai res_hconf \
+		netgroupcache
 
 ifeq ($(have-thread-library),yes)
 
@@ -121,6 +122,7 @@ CFLAGS-servicescache.c += $(nscd-cflags)
 CFLAGS-getsrvbynm_r.c += $(nscd-cflags)
 CFLAGS-getsrvbypt_r.c += $(nscd-cflags)
 CFLAGS-res_hconf.c += $(nscd-cflags)
+CFLAGS-netgroupcache.c += $(nscd-cflags)
 
 ifeq (yesyes,$(have-fpie)$(build-shared))
 LDFLAGS-nscd = -Wl,-z,now
diff --git a/nscd/cache.c b/nscd/cache.c
index 58f0bcc..507ca78 100644
--- a/nscd/cache.c
+++ b/nscd/cache.c
@@ -60,7 +60,9 @@ static time_t (*const readdfcts[LASTREQ]) (struct database_dyn *,
   [GETAI] = readdhstai,
   [INITGROUPS] = readdinitgroups,
   [GETSERVBYNAME] = readdservbyname,
-  [GETSERVBYPORT] = readdservbyport
+  [GETSERVBYPORT] = readdservbyport,
+  [GETNETGRENT] = readdgetnetgrent,
+  [INNETGR] = readdinnetgr
 };
 
 
@@ -70,7 +72,7 @@ static time_t (*const readdfcts[LASTREQ]) (struct database_dyn *,
 
    This function must be called with the read-lock held.  */
 struct datahead *
-cache_search (request_type type, void *key, size_t len,
+cache_search (request_type type, const void *key, size_t len,
 	      struct database_dyn *table, uid_t owner)
 {
   unsigned long int hash = __nis_hash (key, len) % table->head->module;
diff --git a/nscd/connections.c b/nscd/connections.c
index 7d3ff2e..2b5c7ef 100644
--- a/nscd/connections.c
+++ b/nscd/connections.c
@@ -57,11 +57,6 @@
 #endif
 
 
-/* Wrapper functions with error checking for standard functions.  */
-extern void *xmalloc (size_t n);
-extern void *xcalloc (size_t n, size_t s);
-extern void *xrealloc (void *o, size_t n);
-
 /* Support to run nscd as an unprivileged user */
 const char *server_user;
 static uid_t server_uid;
@@ -100,7 +95,10 @@ const char *const serv2str[LASTREQ] =
   [INITGROUPS] = "INITGROUPS",
   [GETSERVBYNAME] = "GETSERVBYNAME",
   [GETSERVBYPORT] = "GETSERVBYPORT",
-  [GETFDSERV] = "GETFDSERV"
+  [GETFDSERV] = "GETFDSERV",
+  [GETNETGRENT] = "GETNETGRENT",
+  [INNETGR] = "INNETGR",
+  [GETFDNETGR] = "GETFDNETGR"
 };
 
 /* The control data structures for the services.  */
@@ -181,6 +179,25 @@ struct database_dyn dbs[lastdb] =
     .wr_fd = -1,
     .ro_fd = -1,
     .mmap_used = false
+  },
+  [netgrdb] = {
+    .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
+    .prune_lock = PTHREAD_MUTEX_INITIALIZER,
+    .prune_run_lock = PTHREAD_MUTEX_INITIALIZER,
+    .enabled = 0,
+    .check_file = 1,
+    .persistent = 0,
+    .propagate = 0,		/* Not used.  */
+    .shared = 0,
+    .max_db_size = DEFAULT_MAX_DB_SIZE,
+    .suggested_module = DEFAULT_SUGGESTED_MODULE,
+    .db_filename = _PATH_NSCD_NETGROUP_DB,
+    .disabled_iov = &netgroup_iov_disabled,
+    .postimeout = 28800,
+    .negtimeout = 20,
+    .wr_fd = -1,
+    .ro_fd = -1,
+    .mmap_used = false
   }
 };
 
@@ -210,7 +227,10 @@ static struct
   [INITGROUPS] = { true, &dbs[grpdb] },
   [GETSERVBYNAME] = { true, &dbs[servdb] },
   [GETSERVBYPORT] = { true, &dbs[servdb] },
-  [GETFDSERV] = { false, &dbs[servdb] }
+  [GETFDSERV] = { false, &dbs[servdb] },
+  [GETNETGRENT] = { true, &dbs[netgrdb] },
+  [INNETGR] = { true, &dbs[netgrdb] },
+  [GETFDNETGR] = { false, &dbs[netgrdb] }
 };
 
 
@@ -355,7 +375,8 @@ check_use (const char *data, nscd_ssize_t first_free, uint8_t *usemap,
 static int
 verify_persistent_db (void *mem, struct database_pers_head *readhead, int dbnr)
 {
-  assert (dbnr == pwddb || dbnr == grpdb || dbnr == hstdb || dbnr == servdb);
+  assert (dbnr == pwddb || dbnr == grpdb || dbnr == hstdb || dbnr == servdb
+	  || dbnr == netgrdb);
 
   time_t now = time (NULL);
 
@@ -1230,6 +1251,14 @@ request from '%s' [%ld] not handled due to missing permission"),
       addservbyport (db, fd, req, key, uid);
       break;
 
+    case GETNETGRENT:
+      addgetnetgrent (db, fd, req, key, uid);
+      break;
+
+    case INNETGR:
+      addinnetgr (db, fd, req, key, uid);
+      break;
+
     case GETSTAT:
     case SHUTDOWN:
     case INVALIDATE:
@@ -1276,6 +1305,7 @@ request from '%s' [%ld] not handled due to missing permission"),
     case GETFDGR:
     case GETFDHST:
     case GETFDSERV:
+    case GETFDNETGR:
 #ifdef SCM_RIGHTS
       send_ro_fd (reqinfo[req->type].db, key, fd);
 #endif
diff --git a/nscd/netgroupcache.c b/nscd/netgroupcache.c
new file mode 100644
index 0000000..a406ade
--- /dev/null
+++ b/nscd/netgroupcache.c
@@ -0,0 +1,669 @@
+/* Cache handling for netgroup lookup.
+   Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include <alloca.h>
+#include <assert.h>
+#include <errno.h>
+#include <libintl.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "../inet/netgroup.h"
+#include "nscd.h"
+#include "dbg_log.h"
+#ifdef HAVE_SENDFILE
+# include <kernel-features.h>
+#endif
+
+
+/* This is the standard reply in case the service is disabled.  */
+static const netgroup_response_header disabled =
+{
+  .version = NSCD_VERSION,
+  .found = -1,
+  .nresults = 0,
+  .result_len = 0
+};
+
+/* This is the struct describing how to write this record.  */
+const struct iovec netgroup_iov_disabled =
+{
+  .iov_base = (void *) &disabled,
+  .iov_len = sizeof (disabled)
+};
+
+
+/* This is the standard reply in case we haven't found the dataset.  */
+static const netgroup_response_header notfound =
+{
+  .version = NSCD_VERSION,
+  .found = 0,
+  .nresults = 0,
+  .result_len = 0
+};
+
+
+struct dataset
+{
+  struct datahead head;
+  netgroup_response_header resp;
+  char strdata[0];
+};
+
+
+static time_t
+addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
+		 const char *key, uid_t uid, struct hashentry *he,
+		 struct datahead *dh, struct dataset **resultp)
+{
+  if (__builtin_expect (debug_level > 0, 0))
+    {
+      if (he == NULL)
+	dbg_log (_("Haven't found \"%s\" in netgroup cache!"), key);
+      else
+	dbg_log (_("Reloading \"%s\" in netgroup cache!"), key);
+    }
+
+  static service_user *netgroup_database;
+  time_t timeout;
+  struct dataset *dataset;
+  bool cacheable = false;
+  ssize_t total;
+
+  char *key_copy = NULL;
+  struct __netgrent data;
+  size_t buflen = MAX (1024, sizeof (*dataset) + req->key_len);
+  size_t buffilled = sizeof (*dataset);
+  char *buffer = NULL;
+  size_t nentries = 0;
+  bool use_malloc = false;
+  size_t group_len = strlen (key) + 1;
+  union
+  {
+    struct name_list elem;
+    char mem[sizeof (struct name_list) + group_len];
+  } first_needed;
+
+  if (netgroup_database == NULL
+      && __nss_database_lookup ("netgroup", NULL, NULL, &netgroup_database))
+    {
+      /* No such service.  */
+      total = sizeof (notfound);
+      timeout = time (NULL) + db->negtimeout;
+
+      if (fd != -1)
+	TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
+
+      dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len, 1);
+      /* If we cannot permanently store the result, so be it.  */
+      if (dataset != NULL)
+	{
+	  dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
+	  dataset->head.recsize = total;
+	  dataset->head.notfound = true;
+	  dataset->head.nreloads = 0;
+	  dataset->head.usable = true;
+
+	  /* Compute the timeout time.  */
+	  timeout = dataset->head.timeout = time (NULL) + db->negtimeout;
+	  dataset->head.ttl = db->negtimeout;
+
+	  /* This is the reply.  */
+	  memcpy (&dataset->resp, &notfound, total);
+
+	  /* Copy the key data.  */
+	  memcpy (dataset->strdata, key, req->key_len);
+
+	  cacheable = true;
+	}
+
+      goto writeout;
+    }
+
+  memset (&data, '\0', sizeof (data));
+  buffer = alloca (buflen);
+  first_needed.elem.next = &first_needed.elem;
+  memcpy (first_needed.elem.name, key, group_len);
+  data.needed_groups = &first_needed.elem;
+
+  while (data.needed_groups != NULL)
+    {
+      /* Add the next group to the list of those which are known.  */
+      struct name_list *this_group = data.needed_groups->next;
+      if (this_group == data.needed_groups)
+	data.needed_groups = NULL;
+      else
+	data.needed_groups->next = this_group->next;
+      this_group->next = data.known_groups;
+      data.known_groups = this_group;
+
+      union
+      {
+	enum nss_status (*f) (const char *, struct __netgrent *);
+	void *ptr;
+      } setfct;
+
+      service_user *nip = netgroup_database;
+      int no_more = __nss_lookup (&nip, "setnetgrent", NULL, &setfct.ptr);
+      while (!no_more)
+	{
+	  enum nss_status status
+	    = DL_CALL_FCT (*setfct.f, (data.known_groups->name, &data));
+
+	  if (status == NSS_STATUS_SUCCESS)
+	    {
+	      union
+	      {
+		enum nss_status (*f) (struct __netgrent *, char *, size_t,
+				      int *);
+		void *ptr;
+	      } getfct;
+	      getfct.ptr = __nss_lookup_function (nip, "getnetgrent_r");
+	      if (getfct.f != NULL)
+		while (1)
+		  {
+		    int e;
+		    status = getfct.f (&data, buffer + buffilled,
+				       buflen - buffilled, &e);
+		    if (status == NSS_STATUS_RETURN)
+		      /* This was the last one for this group.  Look
+			 at next group if available.  */
+		      break;
+		    if (status == NSS_STATUS_SUCCESS)
+		      {
+			if (data.type == triple_val)
+			  {
+			    const char *nhost = data.val.triple.host;
+			    const char *nuser = data.val.triple.user;
+			    const char *ndomain = data.val.triple.domain;
+
+			    if (data.val.triple.host > data.val.triple.user
+				|| data.val.triple.user > data.val.triple.domain)
+			      {
+				const char *last = MAX (nhost,
+							MAX (nuser, ndomain));
+				size_t bufused = (last + strlen (last) + 1
+						  - buffer);
+
+				/* We have to make temporary copies.  */
+				size_t hostlen = strlen (nhost) + 1;
+				size_t userlen = strlen (nuser) + 1;
+				size_t domainlen = strlen (ndomain) + 1;
+				size_t needed = hostlen + userlen + domainlen;
+
+				if (buflen - req->key_len - bufused < needed)
+				  {
+				    size_t newsize = MAX (2 * buflen,
+							  buflen + 2 * needed);
+				    if (use_malloc || newsize > 1024 * 1024)
+				      {
+					buflen = newsize;
+					char *newbuf = xrealloc (use_malloc
+								 ? buffer
+								 : NULL,
+								 buflen);
+
+					buffer = newbuf;
+					use_malloc = true;
+				      }
+				    else
+				      extend_alloca (buffer, buflen, newsize);
+				  }
+
+				nhost = memcpy (buffer + bufused,
+						nhost, hostlen);
+				nuser = memcpy ((char *) nhost + hostlen,
+						nuser, userlen);
+				ndomain = memcpy ((char *) nuser + userlen,
+						  ndomain, domainlen);
+			      }
+
+			    char *wp = buffer + buffilled;
+			    wp = stpcpy (wp, nhost) + 1;
+			    wp = stpcpy (wp, nuser) + 1;
+			    wp = stpcpy (wp, ndomain) + 1;
+			    buffilled = wp - buffer;
+			    ++nentries;
+			  }
+			else
+			  {
+			    /* Check that the group has not been
+			       requested before.  */
+			    struct name_list *runp = data.needed_groups;
+			    if (runp != NULL)
+			      while (1)
+				{
+				  if (strcmp (runp->name, data.val.group) == 0)
+				    break;
+
+				  runp = runp->next;
+				  if (runp == data.needed_groups)
+				    {
+				      runp = NULL;
+				      break;
+				    }
+				}
+
+			    if (runp == NULL)
+			      {
+				runp = data.known_groups;
+				while (runp != NULL)
+				  if (strcmp (runp->name, data.val.group) == 0)
+				    break;
+				  else
+				    runp = runp->next;
+				}
+
+			    if (runp == NULL)
+			      {
+				/* A new group is requested.  */
+				size_t namelen = strlen (data.val.group) + 1;
+				struct name_list *newg = alloca (sizeof (*newg)
+								 + namelen);
+				memcpy (newg->name, data.val.group, namelen);
+				if (data.needed_groups == NULL)
+				  data.needed_groups = newg->next = newg;
+				else
+				  {
+				    newg->next = data.needed_groups->next;
+				    data.needed_groups->next = newg;
+				    data.needed_groups = newg;
+				  }
+			      }
+			  }
+		      }
+		    else if (status == NSS_STATUS_UNAVAIL && e == ERANGE)
+		      {
+			size_t newsize = 2 * buflen;
+			if (use_malloc || newsize > 1024 * 1024)
+			  {
+			    buflen = newsize;
+			    char *newbuf = xrealloc (use_malloc
+						     ? buffer : NULL, buflen);
+
+			    buffer = newbuf;
+			    use_malloc = true;
+			  }
+			else
+			  extend_alloca (buffer, buflen, newsize);
+		      }
+		  }
+
+	      enum nss_status (*endfct) (struct __netgrent *);
+	      endfct = __nss_lookup_function (nip, "endnetgrent");
+	      if (endfct != NULL)
+		(void) DL_CALL_FCT (*endfct, (&data));
+
+	      break;
+	    }
+
+	  no_more = __nss_next2 (&nip, "setnetgrent", NULL, &setfct.ptr,
+				 status, 0);
+	}
+    }
+
+  total = buffilled;
+
+  /* Fill in the dataset.  */
+  dataset = (struct dataset *) buffer;
+  dataset->head.allocsize = total + req->key_len;
+  dataset->head.recsize = total - offsetof (struct dataset, resp);
+  dataset->head.notfound = false;
+  dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
+  dataset->head.usable = true;
+  dataset->head.ttl = db->postimeout;
+  timeout = dataset->head.timeout = time (NULL) + dataset->head.ttl;
+
+  dataset->resp.version = NSCD_VERSION;
+  dataset->resp.found = 1;
+  dataset->resp.nresults = nentries;
+  dataset->resp.result_len = buffilled - sizeof (*dataset);
+
+  assert (buflen - buffilled >= req->key_len);
+  key_copy = memcpy (buffer + buffilled, key, req->key_len);
+  buffilled += req->key_len;
+
+  /* Now we can determine whether on refill we have to create a new
+     record or not.  */
+  if (he != NULL)
+    {
+      assert (fd == -1);
+
+      if (dataset->head.allocsize == dh->allocsize
+	  && dataset->head.recsize == dh->recsize
+	  && memcmp (&dataset->resp, dh->data,
+		     dh->allocsize - offsetof (struct dataset, resp)) == 0)
+	{
+	  /* The data has not changed.  We will just bump the timeout
+	     value.  Note that the new record has been allocated on
+	     the stack and need not be freed.  */
+	  dh->timeout = dataset->head.timeout;
+	  dh->ttl = dataset->head.ttl;
+	  ++dh->nreloads;
+	  dataset = (struct dataset *) dh;
+
+	  goto out;
+	}
+    }
+
+  {
+    struct dataset *newp
+      = (struct dataset *) mempool_alloc (db, total + req->key_len, 1);
+    if (__builtin_expect (newp != NULL, 1))
+      {
+	/* Adjust pointer into the memory block.  */
+	key_copy = (char *) newp + (key_copy - buffer);
+
+	dataset = memcpy (newp, dataset, total + req->key_len);
+	cacheable = true;
+
+	if (he != NULL)
+	  /* Mark the old record as obsolete.  */
+	  dh->usable = false;
+      }
+  }
+
+  if (he == NULL && fd != -1)
+    {
+      /* We write the dataset before inserting it to the database
+	 since while inserting this thread might block and so would
+	 unnecessarily let the receiver wait.  */
+    writeout:
+#ifdef HAVE_SENDFILE
+      if (__builtin_expect (db->mmap_used, 1) && cacheable)
+	{
+	  assert (db->wr_fd != -1);
+	  assert ((char *) &dataset->resp > (char *) db->data);
+	  assert ((char *) dataset - (char *) db->head + total
+		  <= (sizeof (struct database_pers_head)
+		      + db->head->module * sizeof (ref_t)
+		      + db->head->data_size));
+# ifndef __ASSUME_SENDFILE
+	  ssize_t written =
+# endif
+	    sendfileall (fd, db->wr_fd, (char *) &dataset->resp
+			 - (char *) db->head, dataset->head.recsize);
+# ifndef __ASSUME_SENDFILE
+	  if (written == -1 && errno == ENOSYS)
+	    goto use_write;
+# endif
+	}
+      else
+	{
+# ifndef __ASSUME_SENDFILE
+	use_write:
+# endif
+#endif
+	  writeall (fd, &dataset->resp, dataset->head.recsize);
+	}
+    }
+
+  if (cacheable)
+    {
+      /* If necessary, we also propagate the data to disk.  */
+      if (db->persistent)
+	{
+	  // XXX async OK?
+	  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+	  msync ((void *) pval,
+		 ((uintptr_t) dataset & pagesize_m1) + total + req->key_len,
+		 MS_ASYNC);
+	}
+
+      (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
+			true, db, uid, he == NULL);
+
+      pthread_rwlock_unlock (&db->lock);
+
+      /* Mark the old entry as obsolete.  */
+      if (dh != NULL)
+	dh->usable = false;
+    }
+
+ out:
+  if (use_malloc)
+    free (buffer);
+
+  *resultp = dataset;
+
+  return timeout;
+}
+
+
+static time_t
+addinnetgrX (struct database_dyn *db, int fd, request_header *req,
+	     char *key, uid_t uid, struct hashentry *he,
+	     struct datahead *dh)
+{
+  const char *group = key;
+  key = (char *) rawmemchr (key, '\0') + 1;
+  size_t group_len = key - group - 1;
+  const char *host = *key++ ? key : NULL;
+  if (host != NULL)
+    key = (char *) rawmemchr (key, '\0') + 1;
+  const char *user = *key++ ? key : NULL;
+  if (user != NULL)
+    key = (char *) rawmemchr (key, '\0') + 1;
+  const char *domain = *key++ ? key : NULL;
+
+  if (__builtin_expect (debug_level > 0, 0))
+    {
+      if (he == NULL)
+	dbg_log (_("Haven't found \"%s (%s,%s,%s)\" in netgroup cache!"),
+		 group, host ?: "", user ?: "", domain ?: "");
+      else
+	dbg_log (_("Reloading \"%s (%s,%s,%s)\" in netgroup cache!"),
+		 group, host ?: "", user ?: "", domain ?: "");
+    }
+
+  struct dataset *result = (struct dataset *) cache_search (GETNETGRENT,
+							    group, group_len,
+							    db, uid);
+  time_t timeout;
+  if (result != NULL)
+    timeout = result->head.timeout;
+  else
+    {
+      request_header req_get =
+	{
+	  .type = GETNETGRENT,
+	  .key_len = group_len
+	};
+      timeout = addgetnetgrentX (db, -1, &req_get, group, uid, NULL, NULL,
+				 &result);
+    }
+
+  struct indataset
+  {
+    struct datahead head;
+    innetgroup_response_header resp;
+  } *dataset
+      = (struct indataset *) mempool_alloc (db,
+					    sizeof (*dataset) + req->key_len,
+					    1);
+  struct indataset dataset_mem;
+  bool cacheable = true;
+  if (__builtin_expect (dataset == NULL, 0))
+    {
+      cacheable = false;
+      dataset = &dataset_mem;
+    }
+
+  dataset->head.allocsize = sizeof (*dataset) + req->key_len;
+  dataset->head.recsize = sizeof (innetgroup_response_header);
+  dataset->head.notfound = result->head.notfound;
+  dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
+  dataset->head.usable = true;
+  dataset->head.ttl = result->head.ttl;
+  dataset->head.timeout = timeout;
+
+  dataset->resp.version = NSCD_VERSION;
+  dataset->resp.found = result->resp.found;
+  /* Until we find a matching entry the result is 0.  */
+  dataset->resp.result = 0;
+
+  char *key_copy = memcpy ((char *) (dataset + 1), group, req->key_len);
+
+  if (dataset->resp.found)
+    {
+      const char *triplets = (const char *) (&result->resp + 1);
+
+      for (nscd_ssize_t i = result->resp.nresults; i > 0; --i)
+	{
+	  bool success = true;
+
+	  if (host != NULL)
+	    success = strcmp (host, triplets) == 0;
+	  triplets = (const char *) rawmemchr (triplets, '\0') + 1;
+
+	  if (success && user != NULL)
+	    success = strcmp (user, triplets) == 0;
+	  triplets = (const char *) rawmemchr (triplets, '\0') + 1;
+
+	  if (success && (domain == NULL || strcmp (domain, triplets) == 0))
+	    {
+	      dataset->resp.result = 1;
+	      break;
+	    }
+	  triplets = (const char *) rawmemchr (triplets, '\0') + 1;
+	}
+    }
+
+  if (he != NULL && dh->data[0].innetgroupdata.result == dataset->resp.result)
+    {
+      /* The data has not changed.  We will just bump the timeout
+	 value.  Note that the new record has been allocated on
+	 the stack and need not be freed.  */
+      dh->timeout = timeout;
+      dh->ttl = dataset->head.ttl;
+      ++dh->nreloads;
+      return timeout;
+    }
+
+  if (he == NULL)
+    {
+      /* We write the dataset before inserting it to the database
+	 since while inserting this thread might block and so would
+	 unnecessarily let the receiver wait.  */
+       assert (fd != -1);
+
+#ifdef HAVE_SENDFILE
+      if (__builtin_expect (db->mmap_used, 1) && cacheable)
+	{
+	  assert (db->wr_fd != -1);
+	  assert ((char *) &dataset->resp > (char *) db->data);
+	  assert ((char *) dataset - (char *) db->head + sizeof (*dataset)
+		  <= (sizeof (struct database_pers_head)
+		      + db->head->module * sizeof (ref_t)
+		      + db->head->data_size));
+# ifndef __ASSUME_SENDFILE
+	  ssize_t written =
+# endif
+	    sendfileall (fd, db->wr_fd,
+			 (char *) &dataset->resp - (char *) db->head,
+			 sizeof (innetgroup_response_header));
+# ifndef __ASSUME_SENDFILE
+	  if (written == -1 && errno == ENOSYS)
+	    goto use_write;
+# endif
+	}
+      else
+	{
+# ifndef __ASSUME_SENDFILE
+	use_write:
+# endif
+#endif
+	  writeall (fd, &dataset->resp, sizeof (innetgroup_response_header));
+	}
+    }
+
+  if (cacheable)
+    {
+      /* If necessary, we also propagate the data to disk.  */
+      if (db->persistent)
+	{
+	  // XXX async OK?
+	  uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
+	  msync ((void *) pval,
+		 ((uintptr_t) dataset & pagesize_m1) + sizeof (*dataset)
+		 + req->key_len,
+		 MS_ASYNC);
+	}
+
+      (void) cache_add (req->type, key_copy, req->key_len, &dataset->head,
+			true, db, uid, he == NULL);
+
+      pthread_rwlock_unlock (&db->lock);
+
+      /* Mark the old entry as obsolete.  */
+      if (dh != NULL)
+	dh->usable = false;
+    }
+
+  return timeout;
+}
+
+
+void
+addgetnetgrent (struct database_dyn *db, int fd, request_header *req,
+		void *key, uid_t uid)
+{
+  struct dataset *ignore;
+
+  addgetnetgrentX (db, fd, req, key, uid, NULL, NULL, &ignore);
+}
+
+
+time_t
+readdgetnetgrent (struct database_dyn *db, struct hashentry *he,
+		  struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = GETNETGRENT,
+      .key_len = he->len
+    };
+  struct dataset *ignore;
+
+  return addgetnetgrentX (db, -1, &req, db->data + he->key, he->owner, he, dh,
+			  &ignore);
+}
+
+
+void
+addinnetgr (struct database_dyn *db, int fd, request_header *req,
+	    void *key, uid_t uid)
+{
+  addinnetgrX (db, fd, req, key, uid, NULL, NULL);
+}
+
+
+time_t
+readdinnetgr (struct database_dyn *db, struct hashentry *he,
+	      struct datahead *dh)
+{
+  request_header req =
+    {
+      .type = INNETGR,
+      .key_len = he->len
+    };
+
+  return addinnetgrX (db, -1, &req, db->data + he->key, he->owner, he, dh);
+}
diff --git a/nscd/nscd-client.h b/nscd/nscd-client.h
index 482b052..caad26a 100644
--- a/nscd/nscd-client.h
+++ b/nscd/nscd-client.h
@@ -70,6 +70,9 @@ typedef enum
   GETSERVBYNAME,
   GETSERVBYPORT,
   GETFDSERV,
+  GETNETGRENT,
+  INNETGR,
+  GETFDNETGR,
   LASTREQ
 } request_type;
 
@@ -171,6 +174,24 @@ typedef struct
 } serv_response_header;
 
 
+/* Structure send in reply to netgroup query.  Note that this struct is
+   sent also if the service is disabled or there is no record found.  */
+typedef struct
+{
+  int32_t version;
+  int32_t found;
+  nscd_ssize_t nresults;
+  nscd_ssize_t result_len;
+} netgroup_response_header;
+
+typedef struct
+{
+  int32_t version;
+  int32_t found;
+  int32_t result;
+} innetgroup_response_header;
+
+
 /* Type for offsets in data part of database.  */
 typedef uint32_t ref_t;
 /* Value for invalid/no reference.  */
@@ -210,6 +231,8 @@ struct datahead
     ai_response_header aidata;
     initgr_response_header initgrdata;
     serv_response_header servdata;
+    netgroup_response_header netgroupdata;
+    innetgroup_response_header innetgroupdata;
     nscd_ssize_t align1;
     nscd_time_t align2;
   } data[0];
diff --git a/nscd/nscd.conf b/nscd/nscd.conf
index ada88e6..39b8759 100644
--- a/nscd/nscd.conf
+++ b/nscd/nscd.conf
@@ -77,3 +77,12 @@
 	persistent		services	yes
 	shared			services	yes
 	max-db-size		services	33554432
+
+	enable-cache		netgroup	yes
+	positive-time-to-live	netgroup	28800
+	negative-time-to-live	netgroup	20
+	suggested-size		netgroup	211
+	check-files		netgroup	yes
+	persistent		netgroup	yes
+	shared			netgroup	yes
+	max-db-size		netgroup	33554432
diff --git a/nscd/nscd.h b/nscd/nscd.h
index c15e88b..fdaf01b 100644
--- a/nscd/nscd.h
+++ b/nscd/nscd.h
@@ -38,6 +38,7 @@ typedef enum
   grpdb,
   hstdb,
   servdb,
+  netgrdb,
   lastdb
 } dbtype;
 
@@ -116,6 +117,7 @@ struct database_dyn
 #define _PATH_NSCD_GROUP_DB	"/var/db/nscd/group"
 #define _PATH_NSCD_HOSTS_DB	"/var/db/nscd/hosts"
 #define _PATH_NSCD_SERVICES_DB	"/var/db/nscd/services"
+#define _PATH_NSCD_NETGROUP_DB	"/var/db/nscd/netgroup"
 
 /* Path used when not using persistent storage.  */
 #define _PATH_NSCD_XYZ_DB_TMP	"/var/run/nscd/dbXXXXXX"
@@ -149,6 +151,7 @@ extern const struct iovec pwd_iov_disabled;
 extern const struct iovec grp_iov_disabled;
 extern const struct iovec hst_iov_disabled;
 extern const struct iovec serv_iov_disabled;
+extern const struct iovec netgroup_iov_disabled;
 
 
 /* Initial number of threads to run.  */
@@ -197,6 +200,11 @@ extern gid_t old_gid;
 
 /* Prototypes for global functions.  */
 
+/* Wrapper functions with error checking for standard functions.  */
+extern void *xmalloc (size_t n);
+extern void *xcalloc (size_t n, size_t s);
+extern void *xrealloc (void *o, size_t n);
+
 /* nscd.c */
 extern void termination_handler (int signum) __attribute__ ((__noreturn__));
 extern int nscd_open_socket (void);
@@ -216,8 +224,8 @@ extern void send_stats (int fd, struct database_dyn dbs[lastdb]);
 extern int receive_print_stats (void) __attribute__ ((__noreturn__));
 
 /* cache.c */
-extern struct datahead *cache_search (request_type, void *key, size_t len,
-				      struct database_dyn *table,
+extern struct datahead *cache_search (request_type, const void *key,
+				      size_t len, struct database_dyn *table,
 				      uid_t owner);
 extern int cache_add (int type, const void *key, size_t len,
 		      struct datahead *packet, bool first,
@@ -286,6 +294,16 @@ extern void addservbyport (struct database_dyn *db, int fd,
 extern time_t readdservbyport (struct database_dyn *db, struct hashentry *he,
 			       struct datahead *dh);
 
+/* netgroupcache.c */
+extern void addinnetgr (struct database_dyn *db, int fd, request_header *req,
+			void *key, uid_t uid);
+extern time_t readdinnetgr (struct database_dyn *db, struct hashentry *he,
+			    struct datahead *dh);
+extern void addgetnetgrent (struct database_dyn *db, int fd,
+			    request_header *req, void *key, uid_t uid);
+extern time_t readdgetnetgrent (struct database_dyn *db, struct hashentry *he,
+				struct datahead *dh);
+
 /* mem.c */
 extern void *mempool_alloc (struct database_dyn *db, size_t len,
 			    int data_alloc);
diff --git a/nscd/nscd_conf.c b/nscd/nscd_conf.c
index 3b6cbb0..98b5928 100644
--- a/nscd/nscd_conf.c
+++ b/nscd/nscd_conf.c
@@ -43,7 +43,8 @@ const char *const dbnames[lastdb] =
   [pwddb] = "passwd",
   [grpdb] = "group",
   [hstdb] = "hosts",
-  [servdb] = "services"
+  [servdb] = "services",
+  [netgrdb] = "netgroup"
 };
 
 
diff --git a/nscd/nscd_netgroup.c b/nscd/nscd_netgroup.c
new file mode 100644
index 0000000..8457cec
--- /dev/null
+++ b/nscd/nscd_netgroup.c
@@ -0,0 +1,290 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
+
+   The GNU C 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.
+
+   The GNU C 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 the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <alloca.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <not-cancel.h>
+
+#include "nscd-client.h"
+#include "nscd_proto.h"
+
+int __nss_not_use_nscd_netgroup;
+
+
+libc_locked_map_ptr (static, map_handle);
+/* Note that we only free the structure if necessary.  The memory
+   mapping is not removed since it is not visible to the malloc
+   handling.  */
+libc_freeres_fn (pw_map_free)
+{
+  if (map_handle.mapped != NO_MAPPING)
+    {
+      void *p = map_handle.mapped;
+      map_handle.mapped = NO_MAPPING;
+      free (p);
+    }
+}
+
+
+int
+__nscd_setnetgrent (const char *group, struct __netgrent *datap)
+{
+  int gc_cycle;
+  int nretries = 0;
+  size_t group_len = strlen (group);
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  struct mapped_database *mapped;
+  mapped = __nscd_get_map_ref (GETFDNETGR, "netgroup", &map_handle, &gc_cycle);
+
+ retry:;
+  char *respdata = NULL;
+  int retval = -1;
+  netgroup_response_header netgroup_resp;
+
+  if (mapped != NO_MAPPING)
+    {
+      struct datahead *found = __nscd_cache_search (GETNETGRENT, group,
+						    group_len, mapped,
+						    sizeof netgroup_resp);
+      if (found != NULL)
+	{
+	  respdata = (char *) (&found->data[0].netgroupdata + 1);
+	  netgroup_resp = found->data[0].netgroupdata;
+	  /* Now check if we can trust pw_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
+	}
+    }
+
+  int sock = -1;
+  if (respdata == NULL)
+    {
+      sock = __nscd_open_socket (group, group_len, GETNETGRENT,
+				 &netgroup_resp, sizeof (netgroup_resp));
+      if (sock == -1)
+	{
+	  /* nscd not running or wrong version.  */
+	  __nss_not_use_nscd_netgroup = 1;
+	  goto out;
+	}
+    }
+
+  if (netgroup_resp.found == 1)
+    {
+      size_t datalen = netgroup_resp.result_len;
+
+      /* If we do not have to read the data here it comes from the
+	 mapped data and does not have to be freed.  */
+      if (respdata == NULL)
+	{
+	  /* The data will come via the socket.  */
+	  respdata = malloc (datalen);
+	  if (respdata == NULL)
+	    goto out_close;
+
+	  if ((size_t) __readall (sock, respdata, datalen) != datalen)
+	    {
+	      free (respdata);
+	      goto out_close;
+	    }
+	}
+
+      datap->data = respdata;
+      datap->data_size = datalen;
+      datap->cursor = respdata;
+      datap->first = 1;
+      datap->nip = (service_user *) -1l;
+      datap->known_groups = NULL;
+      datap->needed_groups = NULL;
+
+      retval = 1;
+    }
+  else
+    {
+      if (__builtin_expect (netgroup_resp.found == -1, 0))
+	{
+	  /* The daemon does not cache this database.  */
+	  __nss_not_use_nscd_netgroup = 1;
+	  goto out_close;
+	}
+
+      /* Set errno to 0 to indicate no error, just no found record.  */
+      __set_errno (0);
+      /* Even though we have not found anything, the result is zero.  */
+      retval = 0;
+    }
+
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
+ out:
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
+    {
+      /* When we come here this means there has been a GC cycle while we
+	 were looking for the data.  This means the data might have been
+	 inconsistent.  Retry if possible.  */
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
+	{
+	  /* nscd is just running gc now.  Disable using the mapping.  */
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
+	  mapped = NO_MAPPING;
+	}
+
+      if (retval != -1)
+	goto retry;
+    }
+
+  return retval;
+}
+
+
+int
+__nscd_innetgr (const char *netgroup, const char *host, const char *user,
+		const char *domain)
+{
+  size_t key_len = (strlen (netgroup) + strlen (host ?: "")
+		    + strlen (user ?: "") + strlen (domain ?: "") + 7);
+  char *key;
+  bool use_alloca = __libc_use_alloca (key_len);
+  if (use_alloca)
+    key = alloca (key_len);
+  else
+    {
+      key = malloc (key_len);
+      if (key == NULL)
+	return -1;
+    }
+  char *wp = stpcpy (key, netgroup) + 1;
+  if (host != NULL)
+    {
+      *wp++ = '\1';
+      wp = stpcpy (wp, host) + 1;
+    }
+  else
+    *wp++ = '\0';
+  if (user != NULL)
+    {
+      *wp++ = '\1';
+      wp = stpcpy (wp, user) + 1;
+    }
+  else
+    *wp++ = '\0';
+  if (domain != NULL)
+    {
+      *wp++ = '\1';
+      wp = stpcpy (wp, domain) + 1;
+    }
+  else
+    *wp++ = '\0';
+  key_len = wp - key;
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  int gc_cycle;
+  int nretries = 0;
+  struct mapped_database *mapped;
+  mapped = __nscd_get_map_ref (GETFDNETGR, "netgroup", &map_handle, &gc_cycle);
+
+ retry:;
+  int retval = -1;
+  innetgroup_response_header innetgroup_resp;
+  int sock = -1;
+
+  if (mapped != NO_MAPPING)
+    {
+      struct datahead *found = __nscd_cache_search (INNETGR, key,
+						    key_len, mapped,
+						    sizeof innetgroup_resp);
+      if (found != NULL)
+	{
+	  innetgroup_resp = found->data[0].innetgroupdata;
+	  /* Now check if we can trust pw_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
+
+	  goto found_entry;
+	}
+    }
+
+  sock = __nscd_open_socket (key, key_len, INNETGR,
+			     &innetgroup_resp, sizeof (innetgroup_resp));
+  if (sock == -1)
+    {
+      /* nscd not running or wrong version.  */
+      __nss_not_use_nscd_netgroup = 1;
+      goto out;
+    }
+
+ found_entry:
+  if (innetgroup_resp.found == 1)
+    retval = innetgroup_resp.result;
+  else
+    {
+      if (__builtin_expect (innetgroup_resp.found == -1, 0))
+	{
+	  /* The daemon does not cache this database.  */
+	  __nss_not_use_nscd_netgroup = 1;
+	  goto out_close;
+	}
+
+      /* Set errno to 0 to indicate no error, just no found record.  */
+      __set_errno (0);
+      /* Even though we have not found anything, the result is zero.  */
+      retval = 0;
+    }
+
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
+ out:
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
+    {
+      /* When we come here this means there has been a GC cycle while we
+	 were looking for the data.  This means the data might have been
+	 inconsistent.  Retry if possible.  */
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
+	{
+	  /* nscd is just running gc now.  Disable using the mapping.  */
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
+	  mapped = NO_MAPPING;
+	}
+
+      if (retval != -1)
+	goto retry;
+    }
+
+  if (! use_alloca)
+    free (key);
+
+  return retval;
+}
diff --git a/nscd/nscd_proto.h b/nscd/nscd_proto.h
index 573ca2b..8aa2973 100644
--- a/nscd/nscd_proto.h
+++ b/nscd/nscd_proto.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 1998-2000, 2002, 2004, 2007 Free Software Foundation, Inc.
+/* Copyright (C) 1998-2000,2002,2004,2007,2011 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
 
@@ -36,6 +36,7 @@ extern int __nss_not_use_nscd_passwd attribute_hidden;
 extern int __nss_not_use_nscd_group attribute_hidden;
 extern int __nss_not_use_nscd_hosts attribute_hidden;
 extern int __nss_not_use_nscd_services attribute_hidden;
+extern int __nss_not_use_nscd_netgroup attribute_hidden;
 
 extern int __nscd_getpwnam_r (const char *name, struct passwd *resultbuf,
 			      char *buffer, size_t buflen,
@@ -71,5 +72,7 @@ extern int __nscd_getservbyname_r (const char *name, const char *proto,
 extern int __nscd_getservbyport_r (int port, const char *proto,
 				   struct servent *result_buf, char *buf,
 				   size_t buflen, struct servent **result);
+extern int __nscd_innetgr (const char *netgroup, const char *host,
+			   const char *user, const char *domain);
 
 #endif /* _NSCD_PROTO_H */
diff --git a/nscd/selinux.c b/nscd/selinux.c
index e07a454..f618640 100644
--- a/nscd/selinux.c
+++ b/nscd/selinux.c
@@ -1,5 +1,5 @@
 /* SELinux access controls for nscd.
-   Copyright (C) 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2004,2005,2006,2007,2009,2011 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Matthew Rickard <mjricka@epoch.ncsc.mil>, 2004.
 
@@ -46,7 +46,7 @@
 int selinux_enabled;
 
 /* Define mappings of access vector permissions to request types.  */
-static const int perms[LASTREQ] =
+static const access_vector_t perms[LASTREQ] =
 {
   [GETPWBYNAME] = NSCD__GETPWD,
   [GETPWBYUID] = NSCD__GETPWD,
@@ -69,6 +69,11 @@ static const int perms[LASTREQ] =
   [GETSERVBYPORT] = NSCD__GETSERV,
   [GETFDSERV] = NSCD__SHMEMSERV,
 #endif
+#ifdef NSCD__GETNETGRP
+  [GETNETGRENT] = NSCD__GETNETGRP,
+  [INNETGR] = NSCD__GETNETGRP,
+  [GETFDNETGR] = NSCD__SHMEMNETGRP,
+#endif
 };
 
 /* Store an entry ref to speed AVC decisions.  */
diff --git a/nss/Versions b/nss/Versions
index 666915d..d13d570 100644
--- a/nss/Versions
+++ b/nss/Versions
@@ -12,7 +12,7 @@ libc {
     __nss_disable_nscd; __nss_lookup_function; _nss_files_parse_sgent;
 
     __nss_passwd_lookup2; __nss_group_lookup2; __nss_hosts_lookup2;
-    __nss_services_lookup2; __nss_next2;
+    __nss_services_lookup2; __nss_next2; __nss_lookup;
   }
 }
 
diff --git a/nss/getent.c b/nss/getent.c
index 9d43e2f..b843433 100644
--- a/nss/getent.c
+++ b/nss/getent.c
@@ -480,18 +480,28 @@ netgroup_keys (int number, char *key[])
       return 3;
     }
 
-  for (i = 0; i < number; ++i)
+  if (number == 4)
+    {
+      char *host = strcmp (key[1], "*") == 0 ? NULL : key[1];
+      char *user = strcmp (key[2], "*") == 0 ? NULL : key[2];
+      char *domain = strcmp (key[3], "*") == 0 ? NULL : key[3];
+
+      printf ("%-21s (%s,%s,%s) = %d\n",
+	      key[0], host ?: "", user ?: "", domain ?: "",
+	      innetgr (key[0], host, user, domain));
+    }
+  else if (number == 1)
     {
-      if (!setnetgrent (key[i]))
+      if (!setnetgrent (key[0]))
 	result = 2;
       else
 	{
 	  char *p[3];
 
-	  printf ("%-21s", key[i]);
+	  printf ("%-21s", key[0]);
 
 	  while (getnetgrent (p, p + 1, p + 2))
-	    printf (" (%s, %s, %s)", p[0] ?: " ", p[1] ?: "", p[2] ?: "");
+	    printf (" (%s,%s,%s)", p[0] ?: " ", p[1] ?: "", p[2] ?: "");
 	  putchar_unlocked ('\n');
 	}
     }
diff --git a/nss/nss_files/files-init.c b/nss/nss_files/files-init.c
index b33cc3e..7012ab2 100644
--- a/nss/nss_files/files-init.c
+++ b/nss/nss_files/files-init.c
@@ -38,6 +38,7 @@ TF (grp, "/etc/group");
 TF (hst, "/etc/hosts");
 TF (resolv, "/etc/resolv.conf", .call_res_init = 1);
 TF (serv, "/etc/services");
+TF (netgr, "/etc/netgroup");
 
 
 void
@@ -52,4 +53,6 @@ _nss_files_init (void (*cb) (size_t, struct traced_file *))
   cb (hstdb, &resolv_traced_file.file);
 
   cb (servdb, &serv_traced_file.file);
+
+  cb (netgrdb, &netgr_traced_file.file);
 }
diff --git a/nss/nsswitch.c b/nss/nsswitch.c
index 924cc84..09fa0a6 100644
--- a/nss/nsswitch.c
+++ b/nss/nsswitch.c
@@ -176,6 +176,7 @@ __nss_lookup (service_user **ni, const char *fct_name, const char *fct2_name,
 
   return *fctp != NULL ? 0 : (*ni)->next == NULL ? 1 : -1;
 }
+libc_hidden_def (__nss_lookup)
 
 
 /* -1 == not found
@@ -812,6 +813,7 @@ __nss_disable_nscd (void (*cb) (size_t, struct traced_file *))
   __nss_not_use_nscd_group = -1;
   __nss_not_use_nscd_hosts = -1;
   __nss_not_use_nscd_services = -1;
+  __nss_not_use_nscd_netgroup = -1;
 }
 #endif
 
diff --git a/nss/nsswitch.h b/nss/nsswitch.h
index 3e37bc8..1bf663c 100644
--- a/nss/nsswitch.h
+++ b/nss/nsswitch.h
@@ -125,7 +125,8 @@ libc_hidden_proto (__nss_database_lookup)
    position is remembered in NI.  The function returns a value < 0 if
    an error occurred or no such function exists.  */
 extern int __nss_lookup (service_user **ni, const char *fct_name,
-			 const char *fct2_name, void **fctp) attribute_hidden;
+			 const char *fct2_name, void **fctp);
+libc_hidden_proto (__nss_lookup)
 
 /* Determine the next step in the lookup process according to the
    result STATUS of the call to the last function returned by

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                  |   40 +++
 NEWS                       |    5 +-
 inet/getnetgrent_r.c       |   65 ++++-
 nscd/Makefile              |    6 +-
 nscd/cache.c               |    6 +-
 nscd/connections.c         |   46 +++-
 nscd/netgroupcache.c       |  669 ++++++++++++++++++++++++++++++++++++++++++++
 nscd/nscd-client.h         |   23 ++
 nscd/nscd.conf             |    9 +
 nscd/nscd.h                |   22 ++-
 nscd/nscd_conf.c           |    3 +-
 nscd/nscd_netgroup.c       |  290 +++++++++++++++++++
 nscd/nscd_proto.h          |    5 +-
 nscd/selinux.c             |    9 +-
 nss/Versions               |    2 +-
 nss/getent.c               |   18 +-
 nss/nss_files/files-init.c |    3 +
 nss/nsswitch.c             |    2 +
 nss/nsswitch.h             |    3 +-
 19 files changed, 1196 insertions(+), 30 deletions(-)
 create mode 100644 nscd/netgroupcache.c
 create mode 100644 nscd/nscd_netgroup.c


hooks/post-receive
-- 
GNU C Library master sources


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]