Implement caching of nscd
authorUlrich Drepper <drepper@gmail.com>
Fri, 7 Oct 2011 14:06:31 +0000 (10:06 -0400)
committerUlrich Drepper <drepper@gmail.com>
Fri, 7 Oct 2011 14:06:31 +0000 (10:06 -0400)
19 files changed:
ChangeLog
NEWS
inet/getnetgrent_r.c
nscd/Makefile
nscd/cache.c
nscd/connections.c
nscd/netgroupcache.c [new file with mode: 0644]
nscd/nscd-client.h
nscd/nscd.conf
nscd/nscd.h
nscd/nscd_conf.c
nscd/nscd_netgroup.c [new file with mode: 0644]
nscd/nscd_proto.h
nscd/selinux.c
nss/Versions
nss/getent.c
nss/nss_files/files-init.c
nss/nsswitch.c
nss/nsswitch.h

index edd2728..08e46b6 100644 (file)
--- 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 (file)
--- 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.
 \f
 Version 2.14
 
index 6aaba7b..8790387 100644 (file)
@@ -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;
        }
 
index 097e6f0..be0afed 100644 (file)
@@ -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
index 58f0bcc..507ca78 100644 (file)
@@ -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;
index 7d3ff2e..2b5c7ef 100644 (file)
 #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 (file)
index 0000000..a406ade
--- /dev/null
@@ -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);
+}
index 482b052..caad26a 100644 (file)
@@ -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];
index ada88e6..39b8759 100644 (file)
        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
index c15e88b..fdaf01b 100644 (file)
@@ -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);
index 3b6cbb0..98b5928 100644 (file)
@@ -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 (file)
index 0000000..8457cec
--- /dev/null
@@ -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;
+}
index 573ca2b..8aa2973 100644 (file)
@@ -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 */
index e07a454..f618640 100644 (file)
@@ -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.  */
index 666915d..d13d570 100644 (file)
@@ -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;
   }
 }
 
index 9d43e2f..b843433 100644 (file)
@@ -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');
        }
     }
index b33cc3e..7012ab2 100644 (file)
@@ -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);
 }
index 924cc84..09fa0a6 100644 (file)
@@ -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
 
index 3e37bc8..1bf663c 100644 (file)
@@ -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