diff options
Diffstat (limited to 'nscd')
-rw-r--r-- | nscd/Makefile | 9 | ||||
-rw-r--r-- | nscd/cache.c | 70 | ||||
-rw-r--r-- | nscd/connections.c | 72 | ||||
-rw-r--r-- | nscd/gai.c | 3 | ||||
-rw-r--r-- | nscd/getsrvbynm_r.c | 27 | ||||
-rw-r--r-- | nscd/getsrvbypt_r.c | 27 | ||||
-rw-r--r-- | nscd/grpcache.c | 18 | ||||
-rw-r--r-- | nscd/hstcache.c | 18 | ||||
-rw-r--r-- | nscd/nscd-client.h | 20 | ||||
-rw-r--r-- | nscd/nscd.c | 14 | ||||
-rw-r--r-- | nscd/nscd.conf | 11 | ||||
-rw-r--r-- | nscd/nscd.h | 22 | ||||
-rw-r--r-- | nscd/nscd_conf.c | 9 | ||||
-rw-r--r-- | nscd/nscd_getserv_r.c | 306 | ||||
-rw-r--r-- | nscd/nscd_proto.h | 10 | ||||
-rw-r--r-- | nscd/pwdcache.c | 18 | ||||
-rw-r--r-- | nscd/selinux.c | 9 | ||||
-rw-r--r-- | nscd/servicescache.c | 473 |
18 files changed, 999 insertions, 137 deletions
diff --git a/nscd/Makefile b/nscd/Makefile index 21657abeb7..ef3ce184f9 100644 --- a/nscd/Makefile +++ b/nscd/Makefile @@ -1,4 +1,4 @@ -# Copyright (C) 1998,2000,2002,2003,2004,2005,2006 +# Copyright (C) 1998,2000,2002,2003,2004,2005,2006,2007 # Free Software Foundation, Inc. # This file is part of the GNU C Library. @@ -22,7 +22,8 @@ # subdir := nscd -routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r nscd_getai nscd_initgroups +routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r nscd_getai \ + nscd_initgroups nscd_getserv_r aux := nscd_helper include ../Makeconfig @@ -32,6 +33,7 @@ vpath %.c ../locale/programs nscd-modules := nscd connections pwdcache getpwnam_r getpwuid_r grpcache \ getgrnam_r getgrgid_r hstcache gethstbyad_r gethstbynm2_r \ + getsrvbynm_r getsrvbypt_r servicescache \ dbg_log nscd_conf nscd_stat cache mem nscd_setup_thread \ xmalloc xstrdup aicache initgrcache gai @@ -117,6 +119,9 @@ CFLAGS-aicache.c += $(nscd-cflags) CFLAGS-selinux.c += $(nscd-cflags) CFLAGS-initgrcache.c += $(nscd-cflags) CFLAGS-gai.c += $(nscd-cflags) +CFLAGS-servicescache.c += $(nscd-cflags) +CFLAGS-getsrvbynm_r.c += $(nscd-cflags) +CFLAGS-getsrvbypt_r.c += $(nscd-cflags) ifeq (yesyes,$(have-fpie)$(build-shared)) relro-LDFLAGS += -Wl,-z,now diff --git a/nscd/cache.c b/nscd/cache.c index ef986f374a..e63a4f87df 100644 --- a/nscd/cache.c +++ b/nscd/cache.c @@ -1,4 +1,4 @@ -/* Copyright (c) 1998, 1999, 2003-2005, 2006 Free Software Foundation, Inc. +/* Copyright (c) 1998, 1999, 2003-2006, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. @@ -40,6 +40,25 @@ unsigned int reload_count = DEFAULT_RELOAD_LIMIT; +static void (*const readdfcts[LASTREQ]) (struct database_dyn *, + struct hashentry *, + struct datahead *) = +{ + [GETPWBYNAME] = readdpwbyname, + [GETPWBYUID] = readdpwbyuid, + [GETGRBYNAME] = readdgrbyname, + [GETGRBYGID] = readdgrbygid, + [GETHOSTBYNAME] = readdhstbyname, + [GETHOSTBYNAMEv6] = readdhstbynamev6, + [GETHOSTBYADDR] = readdhstbyaddr, + [GETHOSTBYADDRv6] = readdhstbyaddrv6, + [GETAI] = readdhstai, + [INITGROUPS] = readdinitgroups, + [GETSERVBYNAME] = readdservbyname, + [GETSERVBYPORT] = readdservbyport +}; + + /* Search the cache for a matching entry and return it when found. If this fails search the negative cache and return (void *) -1 if this search was successful. Otherwise return NULL. @@ -328,51 +347,10 @@ prune_cache (struct database_dyn *table, time_t now, int fd) /* Reload the value. We do this only for the initially used key, not the additionally added derived value. */ - switch (runp->type) - { - case GETPWBYNAME: - readdpwbyname (table, runp, dh); - break; - - case GETPWBYUID: - readdpwbyuid (table, runp, dh); - break; - - case GETGRBYNAME: - readdgrbyname (table, runp, dh); - break; - - case GETGRBYGID: - readdgrbygid (table, runp, dh); - break; - - case GETHOSTBYNAME: - readdhstbyname (table, runp, dh); - break; - - case GETHOSTBYNAMEv6: - readdhstbynamev6 (table, runp, dh); - break; - - case GETHOSTBYADDR: - readdhstbyaddr (table, runp, dh); - break; - - case GETHOSTBYADDRv6: - readdhstbyaddrv6 (table, runp, dh); - break; - - case GETAI: - readdhstai (table, runp, dh); - break; - - case INITGROUPS: - readdinitgroups (table, runp, dh); - break; - - default: - assert (! "should never happen"); - } + assert (runp->type < LASTREQ + && readdfcts[runp->type] != NULL); + + readdfcts[runp->type] (table, runp, dh); /* If the entry has been replaced, we might need cleanup. */ diff --git a/nscd/connections.c b/nscd/connections.c index f012b69ed8..a5e758e071 100644 --- a/nscd/connections.c +++ b/nscd/connections.c @@ -75,7 +75,7 @@ static void begin_drop_privileges (void); static void finish_drop_privileges (void); /* Map request type to a string. */ -const char *serv2str[LASTREQ] = +const char *const serv2str[LASTREQ] = { [GETPWBYNAME] = "GETPWBYNAME", [GETPWBYUID] = "GETPWBYUID", @@ -92,7 +92,10 @@ const char *serv2str[LASTREQ] = [GETFDGR] = "GETFDGR", [GETFDHST] = "GETFDHST", [GETAI] = "GETAI", - [INITGROUPS] = "INITGROUPS" + [INITGROUPS] = "INITGROUPS", + [GETSERVBYNAME] = "GETSERVBYNAME", + [GETSERVBYPORT] = "GETSERVBYPORT", + [GETFDSERV] = "GETFDSERV" }; /* The control data structures for the services. */ @@ -107,6 +110,7 @@ struct database_dyn dbs[lastdb] = .propagate = 1, .shared = 0, .max_db_size = DEFAULT_MAX_DB_SIZE, + .reset_res = 0, .filename = "/etc/passwd", .db_filename = _PATH_NSCD_PASSWD_DB, .disabled_iov = &pwd_iov_disabled, @@ -125,6 +129,7 @@ struct database_dyn dbs[lastdb] = .propagate = 1, .shared = 0, .max_db_size = DEFAULT_MAX_DB_SIZE, + .reset_res = 0, .filename = "/etc/group", .db_filename = _PATH_NSCD_GROUP_DB, .disabled_iov = &grp_iov_disabled, @@ -143,6 +148,7 @@ struct database_dyn dbs[lastdb] = .propagate = 0, /* Not used. */ .shared = 0, .max_db_size = DEFAULT_MAX_DB_SIZE, + .reset_res = 1, .filename = "/etc/hosts", .db_filename = _PATH_NSCD_HOSTS_DB, .disabled_iov = &hst_iov_disabled, @@ -151,6 +157,25 @@ struct database_dyn dbs[lastdb] = .wr_fd = -1, .ro_fd = -1, .mmap_used = false + }, + [servdb] = { + .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP, + .prunelock = PTHREAD_MUTEX_INITIALIZER, + .enabled = 0, + .check_file = 1, + .persistent = 0, + .propagate = 0, /* Not used. */ + .shared = 0, + .max_db_size = DEFAULT_MAX_DB_SIZE, + .reset_res = 0, + .filename = "/etc/services", + .db_filename = _PATH_NSCD_SERVICES_DB, + .disabled_iov = &serv_iov_disabled, + .postimeout = 28800, + .negtimeout = 20, + .wr_fd = -1, + .ro_fd = -1, + .mmap_used = false } }; @@ -170,7 +195,10 @@ static struct database_dyn *const serv2db[LASTREQ] = [GETFDGR] = &dbs[grpdb], [GETFDHST] = &dbs[hstdb], [GETAI] = &dbs[hstdb], - [INITGROUPS] = &dbs[grpdb] + [INITGROUPS] = &dbs[grpdb], + [GETSERVBYNAME] = &dbs[servdb], + [GETSERVBYPORT] = &dbs[servdb], + [GETFDSERV] = &dbs[servdb] }; @@ -305,7 +333,7 @@ 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); + assert (dbnr == pwddb || dbnr == grpdb || dbnr == hstdb || dbnr == servdb); time_t now = time (NULL); @@ -821,18 +849,16 @@ invalidate_cache (char *key, int fd) dbtype number; int32_t resp; - if (strcmp (key, "passwd") == 0) - number = pwddb; - else if (strcmp (key, "group") == 0) - number = grpdb; - else if (__builtin_expect (strcmp (key, "hosts"), 0) == 0) - { - number = hstdb; + for (number = pwddb; number < lastdb; ++number) + if (strcmp (key, dbnames[number]) == 0) + { + if (dbs[number].reset_res) + res_init (); - /* Re-initialize the resolver. resolv.conf might have changed. */ - res_init (); - } - else + break; + } + + if (number == lastdb) { resp = EINVAL; writeall (fd, &resp, sizeof (resp)); @@ -922,8 +948,9 @@ cannot handle old request version %d; current version is %d"), // XXX Clean up so that each new command need not introduce a // XXX new conditional. if ((__builtin_expect (req->type, GETPWBYNAME) >= GETPWBYNAME - && __builtin_expect (req->type, LASTDBREQ) <= LASTDBREQ) - || req->type == GETAI || req->type == INITGROUPS) + && __builtin_expect (req->type, GETHOSTBYADDRv6) <= GETHOSTBYADDRv6) + || req->type == GETAI || req->type == INITGROUPS + || req->type == GETSERVBYNAME || req->type == GETSERVBYPORT) { if (__builtin_expect (debug_level, 0) > 0) { @@ -1067,6 +1094,14 @@ cannot handle old request version %d; current version is %d"), addinitgroups (db, fd, req, key, uid); break; + case GETSERVBYNAME: + addservbyname (db, fd, req, key, uid); + break; + + case GETSERVBYPORT: + addservbyport (db, fd, req, key, uid); + break; + case GETSTAT: case SHUTDOWN: case INVALIDATE: @@ -1112,6 +1147,7 @@ cannot handle old request version %d; current version is %d"), case GETFDPW: case GETFDGR: case GETFDHST: + case GETFDSERV: #ifdef SCM_RIGHTS send_ro_fd (serv2db[req->type], key, fd); #endif @@ -1153,7 +1189,7 @@ cannot open /proc/self/cmdline: %s; disabling paranoia mode"), if (n == -1) { dbg_log (_("\ -cannot open /proc/self/cmdline: %s; disabling paranoia mode"), +cannot read /proc/self/cmdline: %s; disabling paranoia mode"), strerror (errno)); close (fd); diff --git a/nscd/gai.c b/nscd/gai.c index 2e706bdfe7..46f8b8bec9 100644 --- a/nscd/gai.c +++ b/nscd/gai.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. +/* Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 2004. @@ -17,7 +17,6 @@ /* This file uses the getaddrinfo code but it compiles it without NSCD support. We just need a few symbol renames. */ -#define __getservbyname_r getservbyname_r #define __inet_aton inet_aton #define __getsockname getsockname #define __socket socket diff --git a/nscd/getsrvbynm_r.c b/nscd/getsrvbynm_r.c new file mode 100644 index 0000000000..fb60f4802d --- /dev/null +++ b/nscd/getsrvbynm_r.c @@ -0,0 +1,27 @@ +/* Copyright (C) 1996, 1997, 2002, 2007 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation. + + 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 <netdb.h> + + +#define LOOKUP_TYPE struct servent +#define FUNCTION_NAME getservbyname +#define DATABASE_NAME services +#define ADD_PARAMS const char *name, const char *proto +#define ADD_VARIABLES name, proto + +#include "../nss/getXXbyYY_r.c" diff --git a/nscd/getsrvbypt_r.c b/nscd/getsrvbypt_r.c new file mode 100644 index 0000000000..fd5b11bc3d --- /dev/null +++ b/nscd/getsrvbypt_r.c @@ -0,0 +1,27 @@ +/* Copyright (C) 1996, 1997, 2002, 2007 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation. + + 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 <netdb.h> + + +#define LOOKUP_TYPE struct servent +#define FUNCTION_NAME getservbyport +#define DATABASE_NAME services +#define ADD_PARAMS int port, const char *proto +#define ADD_VARIABLES port, proto + +#include "../nss/getXXbyYY_r.c" diff --git a/nscd/grpcache.c b/nscd/grpcache.c index 5a8fba4759..54d1ef1eef 100644 --- a/nscd/grpcache.c +++ b/nscd/grpcache.c @@ -1,5 +1,5 @@ /* Cache handling for group lookup. - Copyright (C) 1998-2005, 2006 Free Software Foundation, Inc. + Copyright (C) 1998-2005, 2006, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. @@ -445,23 +445,14 @@ addgrbyX (struct database_dyn *db, int fd, request_header *req, dbg_log (_("Reloading \"%s\" in group cache!"), keystr); } -#if 0 - uid_t oldeuid = 0; - if (db->secure) - { - oldeuid = geteuid (); - pthread_seteuid_np (uid); - } -#endif - while (lookup (req->type, key, &resultbuf, buffer, buflen, &grp) != 0 && (errval = errno) == ERANGE) { - char *old_buffer = buffer; errno = 0; if (__builtin_expect (buflen > 32768, 0)) { + char *old_buffer = buffer; buflen *= 2; buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen); if (buffer == NULL) @@ -486,11 +477,6 @@ addgrbyX (struct database_dyn *db, int fd, request_header *req, buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen); } -#if 0 - if (db->secure) - pthread_seteuid_np (oldeuid); -#endif - cache_addgr (db, fd, req, keystr, grp, uid, he, dh, errval); if (use_malloc) diff --git a/nscd/hstcache.c b/nscd/hstcache.c index e27ece5bc6..742491aea3 100644 --- a/nscd/hstcache.c +++ b/nscd/hstcache.c @@ -1,5 +1,5 @@ /* Cache handling for host lookup. - Copyright (C) 1998-2005, 2006 Free Software Foundation, Inc. + Copyright (C) 1998-2005, 2006, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. @@ -463,24 +463,15 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req, dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) str); } -#if 0 - uid_t oldeuid = 0; - if (db->secure) - { - oldeuid = geteuid (); - pthread_seteuid_np (uid); - } -#endif - while (lookup (req->type, key, &resultbuf, buffer, buflen, &hst) != 0 && h_errno == NETDB_INTERNAL && (errval = errno) == ERANGE) { - char *old_buffer = buffer; errno = 0; if (__builtin_expect (buflen > 32768, 0)) { + char *old_buffer = buffer; buflen *= 2; buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen); if (buffer == NULL) @@ -505,11 +496,6 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req, buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen); } -#if 0 - if (db->secure) - pthread_seteuid_np (oldeuid); -#endif - cache_addhst (db, fd, req, key, hst, uid, he, dh, h_errno == TRY_AGAIN ? errval : 0); diff --git a/nscd/nscd-client.h b/nscd/nscd-client.h index 0fd2d9f547..7702f590bd 100644 --- a/nscd/nscd-client.h +++ b/nscd/nscd-client.h @@ -1,4 +1,4 @@ -/* Copyright (c) 1998, 1999, 2000, 2003, 2004, 2005, 2006 +/* Copyright (c) 1998, 1999, 2000, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998. @@ -56,7 +56,6 @@ typedef enum GETHOSTBYNAMEv6, GETHOSTBYADDR, GETHOSTBYADDRv6, - LASTDBREQ = GETHOSTBYADDRv6, SHUTDOWN, /* Shut the server down. */ GETSTAT, /* Get the server statistic. */ INVALIDATE, /* Invalidate one special cache. */ @@ -65,6 +64,9 @@ typedef enum GETFDHST, GETAI, INITGROUPS, + GETSERVBYNAME, + GETSERVBYPORT, + GETFDSERV, LASTREQ } request_type; @@ -153,6 +155,19 @@ typedef struct } initgr_response_header; +/* Structure sent in reply to services 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 s_name_len; + nscd_ssize_t s_proto_len; + nscd_ssize_t s_aliases_cnt; + int32_t s_port; +} serv_response_header; + + /* Type for offsets in data part of database. */ typedef uint32_t ref_t; /* Value for invalid/no reference. */ @@ -186,6 +201,7 @@ struct datahead hst_response_header hstdata; ai_response_header aidata; initgr_response_header initgrdata; + serv_response_header servdata; nscd_ssize_t align1; nscd_time_t align2; } data[0]; diff --git a/nscd/nscd.c b/nscd/nscd.c index b68ae2f413..d0c34eb010 100644 --- a/nscd/nscd.c +++ b/nscd/nscd.c @@ -332,15 +332,15 @@ parse_opt (int key, char *arg, struct argp_state *state) exit (EXIT_FAILURE); request_header req; - if (strcmp (arg, "passwd") == 0) - req.key_len = sizeof "passwd"; - else if (strcmp (arg, "group") == 0) - req.key_len = sizeof "group"; - else if (strcmp (arg, "hosts") == 0) - req.key_len = sizeof "hosts"; - else + dbtype cnt; + for (cnt = pwddb; cnt < lastdb; ++cnt) + if (strcmp (arg, dbnames[cnt]) == 0) + break; + + if (cnt == lastdb) return ARGP_ERR_UNKNOWN; + req.key_len = strlen (arg) + 1; req.version = NSCD_VERSION; req.type = INVALIDATE; diff --git a/nscd/nscd.conf b/nscd/nscd.conf index 43969898f9..d0d16ad963 100644 --- a/nscd/nscd.conf +++ b/nscd/nscd.conf @@ -26,7 +26,7 @@ # max-db-size <service> <number bytes> # auto-propagate <service> <yes|no> # -# Currently supported cache names (services): passwd, group, hosts +# Currently supported cache names (services): passwd, group, hosts, services # @@ -68,3 +68,12 @@ persistent hosts yes shared hosts yes max-db-size hosts 33554432 + + enable-cache services yes + positive-time-to-live services 28800 + negative-time-to-live services 20 + suggested-size services 211 + check-files services yes + persistent services yes + shared services yes + max-db-size services 33554432 diff --git a/nscd/nscd.h b/nscd/nscd.h index 5c2ff3a95b..e8199b8009 100644 --- a/nscd/nscd.h +++ b/nscd/nscd.h @@ -1,4 +1,4 @@ -/* Copyright (c) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006 +/* Copyright (c) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998. @@ -38,6 +38,7 @@ typedef enum pwddb, grpdb, hstdb, + servdb, lastdb } dbtype; @@ -65,7 +66,8 @@ struct database_dyn int persistent; int shared; int propagate; - const char filename[12]; + int reset_res; + const char filename[16]; const char *db_filename; time_t file_mtime; size_t suggested_module; @@ -92,6 +94,7 @@ struct database_dyn #define _PATH_NSCD_PASSWD_DB "/var/db/nscd/passwd" #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" /* Path used when not using persistent storage. */ #define _PATH_NSCD_XYZ_DB_TMP "/var/run/nscd/dbXXXXXX" @@ -110,12 +113,13 @@ struct database_dyn /* Global variables. */ extern struct database_dyn dbs[lastdb]; -extern const char *dbnames[lastdb]; -extern const char *serv2str[LASTREQ]; +extern const char *const dbnames[lastdb]; +extern const char *const serv2str[LASTREQ]; 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; /* Initial number of threads to run. */ @@ -238,6 +242,16 @@ extern void addinitgroups (struct database_dyn *db, int fd, extern void readdinitgroups (struct database_dyn *db, struct hashentry *he, struct datahead *dh); +/* servicecache.c */ +extern void addservbyname (struct database_dyn *db, int fd, + request_header *req, void *key, uid_t uid); +extern void readdservbyname (struct database_dyn *db, struct hashentry *he, + struct datahead *dh); +extern void addservbyport (struct database_dyn *db, int fd, + request_header *req, void *key, uid_t uid); +extern void readdservbyport (struct database_dyn *db, struct hashentry *he, + struct datahead *dh); + /* mem.c */ extern void *mempool_alloc (struct database_dyn *db, size_t len); extern void gc (struct database_dyn *db); diff --git a/nscd/nscd_conf.c b/nscd/nscd_conf.c index 2048eca886..e724c6bd59 100644 --- a/nscd/nscd_conf.c +++ b/nscd/nscd_conf.c @@ -1,4 +1,4 @@ -/* Copyright (c) 1998,2000,2003,2004,2005,2006 Free Software Foundation, Inc. +/* Copyright (c) 1998, 2000, 2003-2006, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998. @@ -37,11 +37,12 @@ extern char *xstrdup (const char *s); /* Names of the databases. */ -const char *dbnames[lastdb] = +const char *const dbnames[lastdb] = { [pwddb] = "passwd", [grpdb] = "group", - [hstdb] = "hosts" + [hstdb] = "hosts", + [servdb] = "services" }; @@ -52,7 +53,7 @@ find_db (const char *name) if (strcmp (name, dbnames[cnt]) == 0) return cnt; - error (0, 0, _("database %s is not supported\n"), name); + error (0, 0, _("database %s is not supported"), name); return -1; } diff --git a/nscd/nscd_getserv_r.c b/nscd/nscd_getserv_r.c new file mode 100644 index 0000000000..ea5dcedb88 --- /dev/null +++ b/nscd/nscd_getserv_r.c @@ -0,0 +1,306 @@ +/* Copyright (C) 2007 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2007. + + 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 <errno.h> +#include <string.h> +#include <not-cancel.h> +#include <stdio-common/_itoa.h> + +#include "nscd-client.h" +#include "nscd_proto.h" + + +int __nss_not_use_nscd_services; + + +static int nscd_getserv_r (const char *crit, size_t critlen, const char *proto, + request_type type, struct servent *resultbuf, + char *buf, size_t buflen, struct servent **result); + + +int +__nscd_getservbyname_r (const char *name, const char *proto, + struct servent *result_buf, char *buf, size_t buflen, + struct servent **result) +{ + return nscd_getserv_r (name, strlen (name), proto, GETSERVBYNAME, result_buf, + buf, buflen, result); +} + + +int +__nscd_getservbyport_r (int port, const char *proto, + struct servent *result_buf, char *buf, size_t buflen, + struct servent **result) +{ + char portstr[3 * sizeof (int) + 2]; + portstr[sizeof (portstr) - 1] = '\0'; + char *cp = _itoa_word (port, portstr + sizeof (portstr) - 1, 10, 0); + + return nscd_getserv_r (portstr, portstr + sizeof (portstr) - cp, proto, + GETSERVBYPORT, result_buf, buf, buflen, result); +} + + +libc_locked_map_ptr (, __serv_map_handle) attribute_hidden; +/* 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 (serv_map_free) +{ + if (__serv_map_handle.mapped != NO_MAPPING) + { + void *p = __serv_map_handle.mapped; + __serv_map_handle.mapped = NO_MAPPING; + free (p); + } +} + + +static int +nscd_getserv_r (const char *crit, size_t critlen, const char *proto, + request_type type, struct servent *resultbuf, + char *buf, size_t buflen, struct servent **result) +{ + int gc_cycle; + int nretries = 0; + + /* If the mapping is available, try to search there instead of + communicating with the nscd. */ + struct mapped_database *mapped; + mapped = __nscd_get_map_ref (GETFDSERV, "services", &__serv_map_handle, + &gc_cycle); + size_t protolen = proto == NULL ? 0 : strlen (proto); + size_t keylen = critlen + 1 + protolen + 1; + char *key = alloca (keylen); + memcpy (__mempcpy (__mempcpy (key, crit, critlen), + "/", 1), proto ?: "", protolen + 1); + + retry:; + const serv_response_header *serv_resp = NULL; + const char *s_name = NULL; + const char *s_proto = NULL; + const uint32_t *aliases_len = NULL; + const char *aliases_list = NULL; + int retval = -1; + const char *recend = (const char *) ~UINTMAX_C (0); + int sock = -1; + if (mapped != NO_MAPPING) + { + const struct datahead *found = __nscd_cache_search (type, key, keylen, + mapped); + + if (found != NULL) + { + serv_resp = &found->data[0].servdata; + s_name = (char *) (serv_resp + 1); + s_proto = s_name + serv_resp->s_name_len; + aliases_len = (uint32_t *) (s_proto + serv_resp->s_proto_len); + aliases_list = ((char *) aliases_len + + serv_resp->s_aliases_cnt * sizeof (uint32_t)); + +#ifndef _STRING_ARCH_unaligned + /* The aliases_len array in the mapped database might very + well be unaligned. We will access it word-wise so on + platforms which do not tolerate unaligned accesses we + need to make an aligned copy. */ + if (((uintptr_t) aliases_len & (__alignof__ (*aliases_len) - 1)) + != 0) + { + uint32_t *tmp = alloca (hst_resp->h_aliases_cnt + * sizeof (uint32_t)); + aliases_len = memcpy (tmp, aliases_len, + hst_resp->h_aliases_cnt + * sizeof (uint32_t)); + } +#endif + recend = (const char *) found->data + found->recsize; + if (__builtin_expect ((const char *) aliases_len + + serv_resp->s_aliases_cnt * sizeof (uint32_t) + > recend, 0)) + goto out_close; + } + } + + serv_response_header serv_resp_mem; + if (serv_resp == NULL) + { + sock = __nscd_open_socket (key, keylen, type, &serv_resp_mem, + sizeof (serv_resp_mem)); + if (sock == -1) + { + __nss_not_use_nscd_services = 1; + goto out; + } + + serv_resp = &serv_resp_mem; + } + + /* No value found so far. */ + *result = NULL; + + if (__builtin_expect (serv_resp->found == -1, 0)) + { + /* The daemon does not cache this database. */ + __nss_not_use_nscd_services = 1; + goto out_close; + } + + if (serv_resp->found == 1) + { + char *cp = buf; + uintptr_t align1; + uintptr_t align2; + size_t total_len; + ssize_t cnt; + int n; + + /* A first check whether the buffer is sufficiently large is possible. */ + /* Now allocate the buffer the array for the group members. We must + align the pointer and the base of the h_addr_list pointers. */ + align1 = ((__alignof__ (char *) - (cp - ((char *) 0))) + & (__alignof__ (char *) - 1)); + align2 = ((__alignof__ (char *) - ((cp + align1 + serv_resp->s_name_len + + serv_resp->s_proto_len) + - ((char *) 0))) + & (__alignof__ (char *) - 1)); + if (buflen < (align1 + serv_resp->s_name_len + serv_resp->s_proto_len + + align2 + + (serv_resp->s_aliases_cnt + 1) * sizeof (char *))) + { + no_room: + __set_errno (ERANGE); + retval = ERANGE; + goto out_close; + } + cp += align1; + + /* Prepare the result as far as we can. */ + resultbuf->s_aliases = (char **) cp; + cp += (serv_resp->s_aliases_cnt + 1) * sizeof (char *); + + resultbuf->s_name = cp; + cp += serv_resp->s_name_len; + resultbuf->s_proto = cp; + cp += serv_resp->s_proto_len + align2; + resultbuf->s_port = serv_resp->s_port; + + if (s_name == NULL) + { + struct iovec vec[2]; + + vec[0].iov_base = resultbuf->s_name; + vec[0].iov_len = serv_resp->s_name_len + serv_resp->s_proto_len; + total_len = vec[0].iov_len; + n = 1; + + if (serv_resp->s_aliases_cnt > 0) + { + aliases_len = alloca (serv_resp->s_aliases_cnt + * sizeof (uint32_t)); + vec[n].iov_base = (void *) aliases_len; + vec[n].iov_len = serv_resp->s_aliases_cnt * sizeof (uint32_t); + + total_len += serv_resp->s_aliases_cnt * sizeof (uint32_t); + ++n; + } + + if ((size_t) __readvall (sock, vec, n) != total_len) + goto out_close; + } + else + memcpy (resultbuf->s_name, s_name, + serv_resp->s_name_len + serv_resp->s_proto_len); + + /* Now we also can read the aliases. */ + total_len = 0; + for (cnt = 0; cnt < serv_resp->s_aliases_cnt; ++cnt) + { + resultbuf->s_aliases[cnt] = cp; + cp += aliases_len[cnt]; + total_len += aliases_len[cnt]; + } + resultbuf->s_aliases[cnt] = NULL; + + if (__builtin_expect ((const char *) aliases_list + total_len > recend, + 0)) + goto out_close; + /* See whether this would exceed the buffer capacity. */ + if (__builtin_expect (cp > buf + buflen, 0)) + goto no_room; + + /* And finally read the aliases. */ + if (aliases_list == NULL) + { + if (total_len == 0 + || ((size_t) __readall (sock, resultbuf->s_aliases[0], total_len) + == total_len)) + { + retval = 0; + *result = resultbuf; + } + } + else + { + memcpy (resultbuf->s_aliases[0], aliases_list, total_len); + + /* Try to detect corrupt databases. */ + if (resultbuf->s_name[serv_resp->s_name_len - 1] != '\0' + || resultbuf->s_proto[serv_resp->s_proto_len - 1] != '\0' + || ({for (cnt = 0; cnt < serv_resp->s_aliases_cnt; ++cnt) + if (resultbuf->s_aliases[cnt][aliases_len[cnt] - 1] + != '\0') + break; + cnt < serv_resp->s_aliases_cnt; })) + /* We cannot use the database. */ + goto out_close; + + retval = 0; + *result = resultbuf; + } + } + else + { + /* The `errno' to some value != ERANGE. */ + __set_errno (ENOENT); + /* 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 && retval != -1) + { + /* 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) + { + /* nscd is just running gc now. Disable using the mapping. */ + __nscd_unmap (mapped); + mapped = NO_MAPPING; + } + + goto retry; + } + + return retval; +} diff --git a/nscd/nscd_proto.h b/nscd/nscd_proto.h index 64d9bd0043..573ca2b2e7 100644 --- a/nscd/nscd_proto.h +++ b/nscd/nscd_proto.h @@ -1,4 +1,4 @@ -/* Copyright (C) 1998, 1999, 2000, 2002, 2004 Free Software Foundation, Inc. +/* Copyright (C) 1998-2000, 2002, 2004, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998. @@ -35,6 +35,7 @@ struct nscd_ai_result; 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 __nscd_getpwnam_r (const char *name, struct passwd *resultbuf, char *buffer, size_t buflen, @@ -64,4 +65,11 @@ extern int __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop); extern int __nscd_getgrouplist (const char *user, gid_t group, long int *size, gid_t **groupsp, long int limit); +extern int __nscd_getservbyname_r (const char *name, const char *proto, + struct servent *result_buf, char *buf, + size_t buflen, struct servent **result); +extern int __nscd_getservbyport_r (int port, const char *proto, + struct servent *result_buf, char *buf, + size_t buflen, struct servent **result); + #endif /* _NSCD_PROTO_H */ diff --git a/nscd/pwdcache.c b/nscd/pwdcache.c index 01c223add5..0461ec91ce 100644 --- a/nscd/pwdcache.c +++ b/nscd/pwdcache.c @@ -1,5 +1,5 @@ /* Cache handling for passwd lookup. - Copyright (C) 1998-2005, 2006 Free Software Foundation, Inc. + Copyright (C) 1998-2005, 2006, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. @@ -440,23 +440,14 @@ addpwbyX (struct database_dyn *db, int fd, request_header *req, dbg_log (_("Reloading \"%s\" in password cache!"), keystr); } -#if 0 - uid_t oldeuid = 0; - if (db->secure) - { - oldeuid = geteuid (); - pthread_seteuid_np (c_uid); - } -#endif - while (lookup (req->type, key, &resultbuf, buffer, buflen, &pwd) != 0 && (errval = errno) == ERANGE) { - char *old_buffer = buffer; errno = 0; if (__builtin_expect (buflen > 32768, 0)) { + char *old_buffer = buffer; buflen *= 2; buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen); if (buffer == NULL) @@ -481,11 +472,6 @@ addpwbyX (struct database_dyn *db, int fd, request_header *req, buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen); } -#if 0 - if (db->secure) - pthread_seteuid_np (oldeuid); -#endif - /* Add the entry to the cache. */ cache_addpw (db, fd, req, keystr, pwd, c_uid, he, dh, errval); diff --git a/nscd/selinux.c b/nscd/selinux.c index f0620d1012..4a462223f5 100644 --- a/nscd/selinux.c +++ b/nscd/selinux.c @@ -1,5 +1,5 @@ /* SELinux access controls for nscd. - Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Matthew Rickard <mjricka@epoch.ncsc.mil>, 2004. @@ -63,7 +63,12 @@ static const int perms[LASTREQ] = [GETFDGR] = NSCD__SHMEMGRP, [GETFDHST] = NSCD__SHMEMHOST, [GETAI] = NSCD__GETHOST, - [INITGROUPS] = NSCD__GETGRP + [INITGROUPS] = NSCD__GETGRP, +#ifdef NSCD__GETSERV + [GETSERVBYNAME] = NSCD__GETSERV, + [GETSERVBYPORT] = NSCD__GETSERV, + [GETFDSERV] = NSCD__SHMEMSERV, +#endif }; /* Store an entry ref to speed AVC decisions. */ diff --git a/nscd/servicescache.c b/nscd/servicescache.c new file mode 100644 index 0000000000..ea4fa35100 --- /dev/null +++ b/nscd/servicescache.c @@ -0,0 +1,473 @@ +/* Cache handling for services lookup. + Copyright (C) 2007 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@drepper.com>, 2007. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation. + + 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 <netdb.h> +#include <unistd.h> +#include <sys/mman.h> + +#include "nscd.h" +#include "dbg_log.h" + + +/* This is the standard reply in case the service is disabled. */ +static const serv_response_header disabled = +{ + .version = NSCD_VERSION, + .found = -1, + .s_name_len = 0, + .s_proto_len = 0, + .s_aliases_cnt = 0, + .s_port = -1 +}; + +/* This is the struct describing how to write this record. */ +const struct iovec serv_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 serv_response_header notfound = +{ + .version = NSCD_VERSION, + .found = 0, + .s_name_len = 0, + .s_proto_len = 0, + .s_aliases_cnt = 0, + .s_port = -1 +}; + + +static void +cache_addserv (struct database_dyn *db, int fd, request_header *req, + const void *key, struct servent *serv, uid_t owner, + struct hashentry *he, struct datahead *dh, int errval) +{ + ssize_t total; + ssize_t written; + time_t t = time (NULL); + + /* We allocate all data in one memory block: the iov vector, + the response header and the dataset itself. */ + struct dataset + { + struct datahead head; + serv_response_header resp; + char strdata[0]; + } *dataset; + + assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data)); + + if (serv == NULL) + { + if (he != NULL && errval == EAGAIN) + { + /* If we have an old record available but cannot find one + now because the service is not available we keep the old + record and make sure it does not get removed. */ + if (reload_count != UINT_MAX) + /* Do not reset the value if we never not reload the record. */ + dh->nreloads = reload_count - 1; + + written = total = 0; + } + else + { + /* We have no data. This means we send the standard reply for this + case. */ + total = sizeof (notfound); + + written = TEMP_FAILURE_RETRY (send (fd, ¬found, total, + MSG_NOSIGNAL)); + + dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len); + /* 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. */ + dataset->head.timeout = t + db->negtimeout; + + /* This is the reply. */ + memcpy (&dataset->resp, ¬found, total); + + /* Copy the key data. */ + memcpy (dataset->strdata, key, req->key_len); + + /* 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 (struct dataset) + req->key_len, MS_ASYNC); + } + + /* Now get the lock to safely insert the records. */ + pthread_rwlock_rdlock (&db->lock); + + if (cache_add (req->type, &dataset->strdata, req->key_len, + &dataset->head, true, db, owner) < 0) + /* Ensure the data can be recovered. */ + dataset->head.usable = false; + + pthread_rwlock_unlock (&db->lock); + + /* Mark the old entry as obsolete. */ + if (dh != NULL) + dh->usable = false; + } + else + ++db->head->addfailed; + } + } + else + { + /* Determine the I/O structure. */ + size_t s_name_len = strlen (serv->s_name) + 1; + size_t s_proto_len = strlen (serv->s_proto) + 1; + uint32_t *s_aliases_len; + size_t s_aliases_cnt; + char *aliases; + char *cp; + size_t cnt; + + /* Determine the number of aliases. */ + s_aliases_cnt = 0; + for (cnt = 0; serv->s_aliases[cnt] != NULL; ++cnt) + ++s_aliases_cnt; + /* Determine the length of all aliases. */ + s_aliases_len = (uint32_t *) alloca (s_aliases_cnt * sizeof (uint32_t)); + total = 0; + for (cnt = 0; cnt < s_aliases_cnt; ++cnt) + { + s_aliases_len[cnt] = strlen (serv->s_aliases[cnt]) + 1; + total += s_aliases_len[cnt]; + } + + total += (sizeof (struct dataset) + + s_name_len + + s_proto_len + + s_aliases_cnt * sizeof (uint32_t)); + written = total; + + /* If we refill the cache, first assume the reconrd did not + change. Allocate memory on the cache since it is likely + discarded anyway. If it turns out to be necessary to have a + new record we can still allocate real memory. */ + bool alloca_used = false; + dataset = NULL; + + if (he == NULL) + { + dataset = (struct dataset *) mempool_alloc (db, + total + req->key_len); + if (dataset == NULL) + ++db->head->addfailed; + } + + if (dataset == NULL) + { + /* We cannot permanently add the result in the moment. But + we can provide the result as is. Store the data in some + temporary memory. */ + dataset = (struct dataset *) alloca (total + req->key_len); + + /* We cannot add this record to the permanent database. */ + alloca_used = true; + } + + 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; + + /* Compute the timeout time. */ + dataset->head.timeout = t + db->postimeout; + + dataset->resp.version = NSCD_VERSION; + dataset->resp.found = 1; + dataset->resp.s_name_len = s_name_len; + dataset->resp.s_proto_len = s_proto_len; + dataset->resp.s_port = serv->s_port; + dataset->resp.s_aliases_cnt = s_aliases_cnt; + + cp = dataset->strdata; + + cp = mempcpy (cp, serv->s_name, s_name_len); + cp = mempcpy (cp, serv->s_proto, s_proto_len); + cp = mempcpy (cp, s_aliases_len, s_aliases_cnt * sizeof (uint32_t)); + + /* Then the aliases. */ + aliases = cp; + for (cnt = 0; cnt < s_aliases_cnt; ++cnt) + cp = mempcpy (cp, serv->s_aliases[cnt], s_aliases_len[cnt]); + + assert (cp + == dataset->strdata + total - offsetof (struct dataset, + strdata)); + + char *key_copy = memcpy (cp, key, 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 (total + req->key_len == dh->allocsize + && total - offsetof (struct dataset, resp) == 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->nreloads; + } + else + { + /* We have to create a new record. Just allocate + appropriate memory and copy it. */ + struct dataset *newp + = (struct dataset *) mempool_alloc (db, total + req->key_len); + if (newp != NULL) + { + /* Adjust pointers into the memory block. */ + aliases = (char *) newp + (aliases - (char *) dataset); + if (key_copy != NULL) + key_copy = (char *) newp + (key_copy - (char *) dataset); + + dataset = memcpy (newp, dataset, total + req->key_len); + alloca_used = false; + } + + /* Mark the old record as obsolete. */ + dh->usable = false; + } + } + else + { + /* We write the dataset before inserting it to the database + since while inserting this thread might block and so would + unnecessarily keep the receiver waiting. */ + assert (fd != -1); + +#ifdef HAVE_SENDFILE + if (__builtin_expect (db->mmap_used, 1) && !alloca_used) + { + assert (db->wr_fd != -1); + assert ((char *) &dataset->resp > (char *) db->data); + assert ((char *) &dataset->resp - (char *) db->head + + total + <= (sizeof (struct database_pers_head) + + db->head->module * sizeof (ref_t) + + db->head->data_size)); + written = sendfileall (fd, db->wr_fd, + (char *) &dataset->resp + - (char *) db->head, total); +# ifndef __ASSUME_SENDFILE + if (written == -1 && errno == ENOSYS) + goto use_write; +# endif + } + else +# ifndef __ASSUME_SENDFILE + use_write: +# endif +#endif + written = writeall (fd, &dataset->resp, total); + } + + /* Add the record to the database. But only if it has not been + stored on the stack. */ + if (! alloca_used) + { + /* 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); + } + + /* Now get the lock to safely insert the records. */ + pthread_rwlock_rdlock (&db->lock); + + if (cache_add (req->type, key_copy, req->key_len, + &dataset->head, true, db, owner) < 0) + /* Could not allocate memory. Make sure the + data gets discarded. */ + dataset->head.usable = false; + + pthread_rwlock_unlock (&db->lock); + } + } + + if (__builtin_expect (written != total, 0) && debug_level > 0) + { + char buf[256]; + dbg_log (_("short write in %s: %s"), __FUNCTION__, + strerror_r (errno, buf, sizeof (buf))); + } +} + + +static int +lookup (int type, char *key, struct servent *resultbufp, char *buffer, + size_t buflen, struct servent **serv) +{ + char *proto = strrchr (key, '/'); + if (proto != NULL && proto != key) + { + key = strndupa (key, proto - key); + if (proto[1] == '\0') + proto = NULL; + else + ++proto; + } + + if (type == GETSERVBYNAME) + return __getservbyname_r (key, proto, resultbufp, buffer, buflen, serv); + + assert (type == GETSERVBYPORT); + return __getservbyport_r (atol (key), proto, resultbufp, buffer, buflen, + serv); +} + + +static void +addservbyX (struct database_dyn *db, int fd, request_header *req, + char *key, uid_t uid, struct hashentry *he, struct datahead *dh) +{ + /* Search for the entry matching the key. Please note that we don't + look again in the table whether the dataset is now available. We + simply insert it. It does not matter if it is in there twice. The + pruning function only will look at the timestamp. */ + size_t buflen = 1024; + char *buffer = (char *) alloca (buflen); + struct servent resultbuf; + struct servent *serv; + bool use_malloc = false; + int errval = 0; + + if (__builtin_expect (debug_level > 0, 0)) + { + if (he == NULL) + dbg_log (_("Haven't found \"%s\" in services cache!"), key); + else + dbg_log (_("Reloading \"%s\" in services cache!"), key); + } + + while (lookup (req->type, key, &resultbuf, buffer, buflen, &serv) != 0 + && (errval = errno) == ERANGE) + { + errno = 0; + + if (__builtin_expect (buflen > 32768, 0)) + { + char *old_buffer = buffer; + buflen *= 2; + buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen); + if (buffer == NULL) + { + /* We ran out of memory. We cannot do anything but + sending a negative response. In reality this should + never happen. */ + serv = NULL; + buffer = old_buffer; + + /* We set the error to indicate this is (possibly) a + temporary error and that it does not mean the entry + is not available at all. */ + errval = EAGAIN; + break; + } + use_malloc = true; + } + else + /* Allocate a new buffer on the stack. If possible combine it + with the previously allocated buffer. */ + buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen); + } + + cache_addserv (db, fd, req, key, serv, uid, he, dh, errval); + + if (use_malloc) + free (buffer); +} + + +void +addservbyname (struct database_dyn *db, int fd, request_header *req, + void *key, uid_t uid) +{ + addservbyX (db, fd, req, key, uid, NULL, NULL); +} + + +void +readdservbyname (struct database_dyn *db, struct hashentry *he, + struct datahead *dh) +{ + request_header req = + { + .type = GETSERVBYNAME, + .key_len = he->len + }; + + addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh); +} + + +void +addservbyport (struct database_dyn *db, int fd, request_header *req, + void *key, uid_t uid) +{ + addservbyX (db, fd, req, key, uid, NULL, NULL); +} + + +void +readdservbyport (struct database_dyn *db, struct hashentry *he, + struct datahead *dh) +{ + request_header req = + { + .type = GETSERVBYPORT, + .key_len = he->len + }; + + addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh); +} |