diff --git a/ChangeLog b/ChangeLog index edd2728500..08e46b6419 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,45 @@ 2011-10-07 Ulrich Drepper + * 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 8e22f43c2d..e9bf1ff97b 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 6aaba7b030..87903877bf 100644 --- a/inet/getnetgrent_r.c +++ b/inet/getnetgrent_r.c @@ -28,6 +28,7 @@ #include "netgroup.h" #include "nsswitch.h" #include +#include /* 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 097e6f0f43..be0afed2d8 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 58f0bcc5f1..507ca78464 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 7d3ff2e43b..2b5c7ef53c 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 0000000000..a406ade995 --- /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 , 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 +#include +#include +#include +#include +#include +#include + +#include "../inet/netgroup.h" +#include "nscd.h" +#include "dbg_log.h" +#ifdef HAVE_SENDFILE +# include +#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, ¬found, 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, ¬found, 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 482b052d16..caad26a9cc 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 ada88e6a3d..39b875912d 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 c15e88bb6f..fdaf01bfbe 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 3b6cbb0b15..98b59280c6 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 0000000000..8457cec9d3 --- /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 , 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 +#include +#include +#include +#include + +#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 573ca2b2e7..8aa29732ef 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 , 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 e07a454bf8..f618640dd6 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 , 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 666915d6bf..d13d570079 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 9d43e2f656..b843433ed6 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 b33cc3e88f..7012ab2b5e 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 924cc84f44..09fa0a6694 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 3e37bc8bd8..1bf663c854 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