diff options
author | jimb <jimb@7b3dc134-2b1b-0410-93df-9e9f96275f8d> | 2007-07-13 18:29:54 +0000 |
---|---|---|
committer | jimb <jimb@7b3dc134-2b1b-0410-93df-9e9f96275f8d> | 2007-07-13 18:29:54 +0000 |
commit | 1decd60e6634c6092519d58acacb9475cd498f9f (patch) | |
tree | 116e8d0ad6f7f88a951184ecd0297ebf4f1f4909 | |
parent | b6a246fa0e6117a98a0d0486543921550a25cf86 (diff) |
Implement the OPTION_EGLIBC_NSSWITCH option group.
* option-groups.def (OPTION_EGLIBC_NSSWITCH): New entry.
* option-groups.defaults (OPTION_EGLIBC_NSSWITCH): Initialize.
* nss/fixed-nsswitch.conf, nss/fixed-nsswitch.functions: Sample
configuration files.
* nss/nsswitch.h (service_user): Change the known function table
to a union, with appropriate types for tsearch and for fixed
nsswitch. Make the name a pointer.
(name_database_entry): Make the name a pointer.
* nss/nsswitch.c (nss_parse_file, nss_getline)
(nss_parse_service_list, nss_new_service, databases, ndatabases)
(lock, lock_nsswitch, unlock_nsswitch)
(__nss_shlib_revision, service_table):
If OPTION_EGLIBC_NSSWITCH is disabled, include statically
generated configuration data; remove code for parsing the
configuration file and reconfiguring individual databases
dynamically; initialize database and service name pointers;
simplify functions for looking up databases and query functions;
avoid locking and freeing, since no data changes at runtime.
* nss/gen-fixed-nsswitch.c: New program.
* nss/Makefile (before-compile, generated): Generate
fixed-nsswitch.h.
($(objfix)fixed-nsswitch.h, $(objpfx)gen-fixed-nsswitch)
(gen-fixed-nsswitch-CFLAGS): Rules for generating
fixed-nsswitch.h.
(CFLAGS-nsswitch.c): Define OPTION_EGLIBC_NSSWITCH as appropriate.
git-svn-id: svn://svn.eglibc.org/trunk@2816 7b3dc134-2b1b-0410-93df-9e9f96275f8d
-rw-r--r-- | libc/ChangeLog.eglibc | 27 | ||||
-rw-r--r-- | libc/nss/Makefile | 47 | ||||
-rw-r--r-- | libc/nss/fixed-nsswitch.conf | 22 | ||||
-rw-r--r-- | libc/nss/fixed-nsswitch.functions | 121 | ||||
-rw-r--r-- | libc/nss/gen-fixed-nsswitch.c | 710 | ||||
-rw-r--r-- | libc/nss/nsswitch.c | 104 | ||||
-rw-r--r-- | libc/nss/nsswitch.h | 18 | ||||
-rw-r--r-- | libc/option-groups.def | 72 | ||||
-rw-r--r-- | libc/option-groups.defaults | 1 |
9 files changed, 1102 insertions, 20 deletions
diff --git a/libc/ChangeLog.eglibc b/libc/ChangeLog.eglibc index c73fa4122..c1ef913b7 100644 --- a/libc/ChangeLog.eglibc +++ b/libc/ChangeLog.eglibc @@ -1,5 +1,32 @@ 2007-07-12 Jim Blandy <jimb@codesourcery.com> + Implement the OPTION_EGLIBC_NSSWITCH option group. + * option-groups.def (OPTION_EGLIBC_NSSWITCH): New entry. + * option-groups.defaults (OPTION_EGLIBC_NSSWITCH): Initialize. + * nss/fixed-nsswitch.conf, nss/fixed-nsswitch.functions: Sample + configuration files. + * nss/nsswitch.h (service_user): Change the known function table + to a union, with appropriate types for tsearch and for fixed + nsswitch. Make the name a pointer. + (name_database_entry): Make the name a pointer. + * nss/nsswitch.c (nss_parse_file, nss_getline) + (nss_parse_service_list, nss_new_service, databases, ndatabases) + (lock, lock_nsswitch, unlock_nsswitch) + (__nss_shlib_revision, service_table): + If OPTION_EGLIBC_NSSWITCH is disabled, include statically + generated configuration data; remove code for parsing the + configuration file and reconfiguring individual databases + dynamically; initialize database and service name pointers; + simplify functions for looking up databases and query functions; + avoid locking and freeing, since no data changes at runtime. + * nss/gen-fixed-nsswitch.c: New program. + * nss/Makefile (before-compile, generated): Generate + fixed-nsswitch.h. + ($(objfix)fixed-nsswitch.h, $(objpfx)gen-fixed-nsswitch) + (gen-fixed-nsswitch-CFLAGS): Rules for generating + fixed-nsswitch.h. + (CFLAGS-nsswitch.c): Define OPTION_EGLIBC_NSSWITCH as appropriate. + * include/netdb.h (DECLARE_NSS_PROTOTYPES): Also declare _nss_SERVICE_gethostbyname3_r. diff --git a/libc/nss/Makefile b/libc/nss/Makefile index 0936faa65..7cb74f567 100644 --- a/libc/nss/Makefile +++ b/libc/nss/Makefile @@ -26,7 +26,8 @@ subdir := nss headers := nss.h distribute := nsswitch.h XXX-lookup.c getXXbyYY.c getXXbyYY_r.c \ getXXent.c getXXent_r.c databases.def \ - nsswitch.conf digits_dots.c function.def + nsswitch.conf digits_dots.c function.def \ + gen-fixed-nsswitch.c # These are the databases that go through nss dispatch. # Caution: if you add a database here, you must add its real name @@ -76,6 +77,45 @@ ifneq ($(build-static-nss),yes) libnss_files-inhibit-o = $(filter-out .os,$(object-suffixes)) endif +ifneq ($(OPTION_EGLIBC_NSSWITCH),y) + +ifndef OPTION_EGLIBC_NSSWITCH_FIXED_CONFIG +$(error OPTION_EGLIBC_NSSWITCH_FIXED_CONFIG variable left unset) +endif + +ifndef OPTION_EGLIBC_NSSWITCH_FIXED_FUNCTIONS +$(error OPTION_EGLIBC_NSSWITCH_FIXED_FUNCTIONS variable left unset) +endif + +ifeq (,$(wildcard $(common-objpfx)$(OPTION_EGLIBC_NSSWITCH_FIXED_CONFIG))) +$(warning OPTION_EGLIBC_NSSWITCH is disabled, but fixed config file) +$(error does not exist: $(OPTION_EGLIBC_NSSWITCH_FIXED_CONFIG)) +endif + +ifeq (,$(wildcard $(common-objpfx)$(OPTION_EGLIBC_NSSWITCH_FIXED_FUNCTIONS))) +$(warning OPTION_EGLIBC_NSSWITCH is disabled, but fixed functions file) +oooo$(error does not exist: $(OPTION_EGLIBC_NSSWITCH_FIXED_FUNCTIONS)) +endif + +before-compile := $(objpfx)fixed-nsswitch.h +generated := fixed-nsswitch.h +$(objpfx)fixed-nsswitch.h $(objfpx)fixed-nsswitch-libs: \ + $(objpfx)gen-fixed-nsswitch \ + $(common-objpfx)$(OPTION_EGLIBC_NSSWITCH_FIXED_CONFIG) + $< $(objpfx)fixed-nsswitch.h \ + $(objpfx)fixed-nsswitch-libs \ + $(common-objpfx)$(OPTION_EGLIBC_NSSWITCH_FIXED_CONFIG) + +$(objpfx)gen-fixed-nsswitch: gen-fixed-nsswitch.c \ + $(common-objpfx)option-groups.config \ + $(common-objpfx)$(OPTION_EGLIBC_NSSWITCH_FIXED_FUNCTIONS) + $(native-compile) +gen-fixed-nsswitch-CFLAGS = \ + -g3 -O -Wall \ + -I $(objpfx) \ + -DFIXED_FUNCTIONS='"$(common-objpfx)$(OPTION_EGLIBC_NSSWITCH_FIXED_FUNCTIONS)"' +endif + include ../Rules @@ -90,7 +130,10 @@ $(services:%=$(objpfx)libnss_%.so): $(common-objpfx)libc.so \ $(common-objpfx)libc_nonshared.a OPTION_EGLIBC_INET-CFLAGS-$(OPTION_EGLIBC_INET) = -DOPTION_EGLIBC_INET=1 +OPTION_EGLIBC_NSSWITCH-CFLAGS-$(OPTION_EGLIBC_NSSWITCH) \ + = -DOPTION_EGLIBC_NSSWITCH=1 -CFLAGS-nsswitch.c = $(OPTION_EGLIBC_INET-CFLAGS-y) +CFLAGS-nsswitch.c = $(OPTION_EGLIBC_INET-CFLAGS-y) \ + $(OPTION_EGLIBC_NSSWITCH-CFLAGS-y) CFLAGS-getnssent_r.c = $(OPTION_EGLIBC_INET-CFLAGS-y) CFLAGS-getent.c = $(OPTION_EGLIBC_INET-CFLAGS-y) diff --git a/libc/nss/fixed-nsswitch.conf b/libc/nss/fixed-nsswitch.conf new file mode 100644 index 000000000..91bb675c8 --- /dev/null +++ b/libc/nss/fixed-nsswitch.conf @@ -0,0 +1,22 @@ +# /etc/nsswitch.conf +# +# Example configuration for fixed name service. +# See the description of OPTION_EGLIBC_NSSWITCH in option-groups.def +# for details. +# + +aliases: files + +passwd: files +group: files +shadow: files + +hosts: files dns +networks: files dns + +protocols: files +services: files +ethers: files +rpc: files + +netgroup: files diff --git a/libc/nss/fixed-nsswitch.functions b/libc/nss/fixed-nsswitch.functions new file mode 100644 index 000000000..2f3fa833c --- /dev/null +++ b/libc/nss/fixed-nsswitch.functions @@ -0,0 +1,121 @@ +/* List of functions defined for fixed NSS in GNU C Library. + Copyright (C) 1996, 1997, 1998, 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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. */ + +/* When OPTION_EGLIBC_NSSWITCH is disabled (see option-groups.def), + EGLIBC does not use the 'dlopen' and 'dlsym' functions to look for + database query functions in the individual name service libraries. + Instead, it uses a set of functions chosen at compile time, as + directed by the OPTION_EGLIBC_NSSWITCH_FIXED_FUNCTIONS file. This + file is a sample of what you might use there. + + This file is C source code; it should only contain invocations of + the following macros: + + - DEFINE_ENT (DATABASE, SERVICE, X) + + Declare the 'setXent', 'getXent_r', and 'endXent' functions that + query DATABASE using the service library 'libnss_SERVICE.so.2'. + DATABASE should be the full name of the database as it appears in + 'nsswitch.conf', like 'passwd' or 'aliases'. + + (The non-reentrant 'getXent' functions are implemented in terms + of the reentrant 'getXent_r' functions, so there is no need to + refer to them explicitly here.) + + - DEFINE_GETBY (DATABASE, SERVICE, X, KEY) + + Declare the 'getXbyKEY_r' functions that query DATABASE using + SERVICE. DATABASE and SERVICE are as described above. + + (The non-reentrant 'getXbyKEY' functions are implemented in terms + of the reentrant 'getXbyKEY_r' functions, so there is no need to + refer to them explicitly here.) + + Use the special key 'name3' for the service library function that + implements the 'getaddrinfo' function. + + - DEFINE_GET (DATABASE, SERVICE, QUERY) + + Declare the 'getQUERY_r' functions that query DATABASE using + SERVICE. This is used for functions like 'getpwnam'. + + (The non-reentrant 'getQUERY' functions are implemented in terms + of the reentrant 'getQUERY_r' functions, so there is no need to + refer to them explicitly here.) + + This sample file only includes functions that consult the files in + '/etc', and the Domain Name System (DNS). */ + +/* aliases */ +DEFINE_ENT (aliases, files, alias) +DEFINE_GETBY (aliases, files, alias, name) + +/* ethers */ +DEFINE_ENT (ethers, files, ether) + +/* group */ +DEFINE_ENT (group, files, gr) +DEFINE_GET (group, files, grgid) +DEFINE_GET (group, files, grnam) + +/* hosts */ +DEFINE_ENT (hosts, files, host) +DEFINE_GETBY (hosts, files, host, addr) +DEFINE_GETBY (hosts, files, host, name) +DEFINE_GETBY (hosts, files, host, name2) +DEFINE_GET (hosts, files, hostton) +DEFINE_GET (hosts, files, ntohost) +DEFINE_GETBY (hosts, dns, host, addr) +DEFINE_GETBY (hosts, dns, host, name) +DEFINE_GETBY (hosts, dns, host, name2) +DEFINE_GETBY (hosts, dns, host, name3) + +/* netgroup */ +DEFINE_ENT (netgroup, files, netgr) + +/* networks */ +DEFINE_ENT (networks, files, net) +DEFINE_GETBY (networks, files, net, name) +DEFINE_GETBY (networks, files, net, addr) +DEFINE_GETBY (networks, dns, net, name) +DEFINE_GETBY (networks, dns, net, addr) + +/* protocols */ +DEFINE_ENT (protocols, files, proto) +DEFINE_GETBY (protocols, files, proto, name) +DEFINE_GETBY (protocols, files, proto, number) + +/* passwd */ +DEFINE_ENT (passwd, files, pw) +DEFINE_GET (passwd, files, pwnam) +DEFINE_GET (passwd, files, pwuid) + +/* rpc */ +DEFINE_ENT (rpc, files, rpc) +DEFINE_GETBY (rpc, files, rpc, name) +DEFINE_GETBY (rpc, files, rpc, number) + +/* services */ +DEFINE_ENT (services, files, serv) +DEFINE_GETBY (services, files, serv, name) +DEFINE_GETBY (services, files, serv, port) + +/* shadow */ +DEFINE_ENT (shadow, files, sp) +DEFINE_GET (shadow, files, spnam) diff --git a/libc/nss/gen-fixed-nsswitch.c b/libc/nss/gen-fixed-nsswitch.c new file mode 100644 index 000000000..d8e24d4f3 --- /dev/null +++ b/libc/nss/gen-fixed-nsswitch.c @@ -0,0 +1,710 @@ +/* gen-fixed-nsswitch.c --- generate fixed name service data structures + Copyright (C) 1996-1999, 2001-2006, 2007 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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. */ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdarg.h> +#include <assert.h> +#include <ctype.h> + +#define libc_hidden_proto(func) + +#include "nsswitch.h" +#include "gnu/lib-names.h" + + +/* Simple utilities. */ + +void __attribute__ ((noreturn)) +error (const char *message) +{ + fprintf (stderr, "%s\n", message); + exit (1); +} + + +void * +check_alloc (void *p) +{ + if (p) + return p; + else + error ("out of memory"); +} + +void * +xmalloc (size_t size) +{ + return check_alloc (malloc (size)); +} + + +/* Format ARGS according to FORMAT, and return the result as a + malloc'ed string. */ +char * +saprintf (const char *format, ...) +{ + va_list args; + size_t len; + char *buf; + + va_start (args, format); + len = vsnprintf (NULL, 0, format, args); + va_end (args); + + buf = xmalloc (len + 1); + va_start (args, format); + assert (len == vsnprintf (buf, len + 1, format, args)); + va_end (args); + + return buf; +} + + + +/* Gathering the contents of the FIXED_FUNCTIONS file. */ + +/* It should be possible to generate this list automatically by + looking at the services and databases used in the nsswitch.conf + file, and having a hard-coded set of queries supported on each + database. */ + +/* We #include the FIXED_FUNCTIONS file several times to build an + array of function structures holding its data. */ +enum function_kind { + fk_end = 0, /* Last entry. */ + fk_setent, /* Like setpwent. */ + fk_getent, /* Like getpwent. */ + fk_endent, /* Like endpwent. */ + fk_getby, /* Like gethostbyname. */ + fk_get /* Like getpwnam. */ +}; + + +struct function { + /* What kind of function this is. */ + enum function_kind kind; + + /* The database and service of the function being hardwired in. */ + char *database, *service; + + /* The kind of entry being queried, for 'fk_setent', 'fk_getent', + 'fk_endent', and 'fk_getby' functions. */ + char *entry; + + /* The key, for 'fk_getby' entries. */ + char *key; + + /* The value and key, for 'fk_get' entries. */ + char *value_and_key; +}; + + +const struct function functions[] = + { + +#define DEFINE_ENT(database, service, entry) \ + { fk_setent, #database, #service, #entry }, \ + { fk_getent, #database, #service, #entry }, \ + { fk_endent, #database, #service, #entry }, +#define DEFINE_GETBY(database, service, entry, key) \ + { fk_getby, #database, #service, #entry, #key }, +#define DEFINE_GET(database, service, value_and_key) \ + { fk_get, #database, #service, NULL, NULL, #value_and_key }, + +#include FIXED_FUNCTIONS + +#undef DEFINE_ENT +#undef DEFINE_GETBY +#undef DEFINE_GET + + { fk_end } + }; + + +/* Parsing the config file. Functions copied from nsswitch.c. */ + +#define __strchrnul strchrnul +#define __getline getline +#define __strncasecmp strncasecmp + +/* Prototypes for the local functions. */ +static name_database *nss_parse_file (const char *fname) internal_function; +static name_database_entry *nss_getline (char *line) internal_function; +static service_user *nss_parse_service_list (const char *line) + internal_function; + +static name_database * +internal_function +nss_parse_file (const char *fname) +{ + FILE *fp; + name_database *result; + name_database_entry *last; + char *line; + size_t len; + + /* Open the configuration file. */ + fp = fopen (fname, "rc"); + if (fp == NULL) + return NULL; + + // /* No threads use this stream. */ + // __fsetlocking (fp, FSETLOCKING_BYCALLER); + + result = (name_database *) xmalloc (sizeof (name_database)); + + result->entry = NULL; + result->library = NULL; + last = NULL; + line = NULL; + len = 0; + do + { + name_database_entry *this; + ssize_t n; + + n = __getline (&line, &len, fp); + if (n < 0) + break; + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + + /* Because the file format does not know any form of quoting we + can search forward for the next '#' character and if found + make it terminating the line. */ + *__strchrnul (line, '#') = '\0'; + + /* If the line is blank it is ignored. */ + if (line[0] == '\0') + continue; + + /* Each line completely specifies the actions for a database. */ + this = nss_getline (line); + if (this != NULL) + { + if (last != NULL) + last->next = this; + else + result->entry = this; + + last = this; + } + } + while (!feof_unlocked (fp)); + + /* Free the buffer. */ + free (line); + /* Close configuration file. */ + fclose (fp); + + return result; +} + + +/* Read the source names: + `( <source> ( "[" "!"? (<status> "=" <action> )+ "]" )? )*' + */ +static service_user * +internal_function +nss_parse_service_list (const char *line) +{ + service_user *result = NULL, **nextp = &result; + + while (1) + { + service_user *new_service; + const char *name; + + while (isspace (line[0])) + ++line; + if (line[0] == '\0') + /* No source specified. */ + return result; + + /* Read <source> identifier. */ + name = line; + while (line[0] != '\0' && !isspace (line[0]) && line[0] != '[') + ++line; + if (name == line) + return result; + + + new_service = (service_user *) xmalloc (sizeof (*new_service)); + new_service->name = (char *) xmalloc (line - name + 1); + + *((char *) __mempcpy ((char *) new_service->name, name, line - name)) + = '\0'; + + /* Set default actions. */ + new_service->actions[2 + NSS_STATUS_TRYAGAIN] = NSS_ACTION_CONTINUE; + new_service->actions[2 + NSS_STATUS_UNAVAIL] = NSS_ACTION_CONTINUE; + new_service->actions[2 + NSS_STATUS_NOTFOUND] = NSS_ACTION_CONTINUE; + new_service->actions[2 + NSS_STATUS_SUCCESS] = NSS_ACTION_RETURN; + new_service->actions[2 + NSS_STATUS_RETURN] = NSS_ACTION_RETURN; + new_service->library = NULL; + new_service->known.tree = NULL; + new_service->next = NULL; + + while (isspace (line[0])) + ++line; + + if (line[0] == '[') + { + /* Read criterions. */ + do + ++line; + while (line[0] != '\0' && isspace (line[0])); + + do + { + int not; + enum nss_status status; + lookup_actions action; + + /* Grok ! before name to mean all statii but that one. */ + not = line[0] == '!'; + if (not) + ++line; + + /* Read status name. */ + name = line; + while (line[0] != '\0' && !isspace (line[0]) && line[0] != '=' + && line[0] != ']') + ++line; + + /* Compare with known statii. */ + if (line - name == 7) + { + if (__strncasecmp (name, "SUCCESS", 7) == 0) + status = NSS_STATUS_SUCCESS; + else if (__strncasecmp (name, "UNAVAIL", 7) == 0) + status = NSS_STATUS_UNAVAIL; + else + return result; + } + else if (line - name == 8) + { + if (__strncasecmp (name, "NOTFOUND", 8) == 0) + status = NSS_STATUS_NOTFOUND; + else if (__strncasecmp (name, "TRYAGAIN", 8) == 0) + status = NSS_STATUS_TRYAGAIN; + else + return result; + } + else + return result; + + while (isspace (line[0])) + ++line; + if (line[0] != '=') + return result; + do + ++line; + while (isspace (line[0])); + + name = line; + while (line[0] != '\0' && !isspace (line[0]) && line[0] != '=' + && line[0] != ']') + ++line; + + if (line - name == 6 && __strncasecmp (name, "RETURN", 6) == 0) + action = NSS_ACTION_RETURN; + else if (line - name == 8 + && __strncasecmp (name, "CONTINUE", 8) == 0) + action = NSS_ACTION_CONTINUE; + else + return result; + + if (not) + { + /* Save the current action setting for this status, + set them all to the given action, and reset this one. */ + const lookup_actions save = new_service->actions[2 + status]; + new_service->actions[2 + NSS_STATUS_TRYAGAIN] = action; + new_service->actions[2 + NSS_STATUS_UNAVAIL] = action; + new_service->actions[2 + NSS_STATUS_NOTFOUND] = action; + new_service->actions[2 + NSS_STATUS_SUCCESS] = action; + new_service->actions[2 + status] = save; + } + else + new_service->actions[2 + status] = action; + + /* Skip white spaces. */ + while (isspace (line[0])) + ++line; + } + while (line[0] != ']'); + + /* Skip the ']'. */ + ++line; + } + + *nextp = new_service; + nextp = &new_service->next; + } +} + +static name_database_entry * +internal_function +nss_getline (char *line) +{ + const char *name; + name_database_entry *result; + size_t len; + + /* Ignore leading white spaces. ATTENTION: this is different from + what is implemented in Solaris. The Solaris man page says a line + beginning with a white space character is ignored. We regard + this as just another misfeature in Solaris. */ + while (isspace (line[0])) + ++line; + + /* Recognize `<database> ":"'. */ + name = line; + while (line[0] != '\0' && !isspace (line[0]) && line[0] != ':') + ++line; + if (line[0] == '\0' || name == line) + /* Syntax error. */ + return NULL; + *line++ = '\0'; + + len = strlen (name) + 1; + + result = (name_database_entry *) xmalloc (sizeof (*result)); + result->name = (char *) xmalloc (len); + + /* Save the database name. */ + memcpy ((char *) result->name, name, len); + + /* Parse the list of services. */ + result->service = nss_parse_service_list (line); + + result->next = NULL; + return result; +} + + + +/* Generating code for statically initialized nsswitch structures. */ + + +/* Return the service-neutral suffix of the name of the service + library function referred to by the function F. The result is + allocated with malloc. */ +char * +known_function_suffix (const struct function *f) +{ + switch (f->kind) + { + case fk_setent: + return saprintf ("set%sent", f->entry); + + case fk_getent: + return saprintf ("get%sent_r", f->entry); + + case fk_endent: + return saprintf ("end%sent", f->entry); + + case fk_getby: + return saprintf ("get%sby%s_r", f->entry, f->key); + + case fk_get: + return saprintf ("get%s_r", f->value_and_key); + + default: + abort (); + } +} + + +/* Return the name of the service library function referred to by the + function F. The result is allocated with malloc. */ +char * +known_function_name (const struct function *f) +{ + return saprintf ("_nss_%s_%s", f->service, known_function_suffix (f)); +} + + +/* Write initialized known_function structures to OUT for + all the functions we'll use. */ +void +generate_known_functions (FILE *out) +{ + int i; + + /* First, generate weak references to the functions. The service + libraries depend on libc, and if these references weren't weak, + we'd be making libc depend circularly on the service + libraries. */ + for (i = 0; functions[i].kind; i++) + { + char *name = known_function_name (&functions[i]); + fprintf (out, "typeof (%s) %s __attribute__ ((weak));\n", + name, name); + } + fputs ("\n", out); + + /* Then, a table mapping names to functions. */ + fputs ("static const known_function fixed_known_functions[] = {\n", + out); + for (i = 0; functions[i].kind; i++) + { + const struct function *f = &functions[i]; + char *suffix = known_function_suffix (f); + + fprintf (out, " /* %2d */ { \"%s\", _nss_%s_%s },\n", + i, suffix, f->service, suffix); + } + fputs ("};\n", out); + fputs ("\n", out); +} + + +/* Print code to OUT for an initialized array of pointers to the + 'known_function' structures needed for USER, which is for + DATABASE. Return its name, allocated with malloc. */ +char * +generate_known_function_list (FILE *out, + const name_database_entry *database, + const service_user *user) +{ + char *list_name = saprintf ("fixed_%s_%s_known_funcs", + database->name, user->name); + fprintf (out, "static const known_function *%s[] = {\n", + list_name); + int i; + for (i = 0; functions[i].kind; i++) + if (strcmp (functions[i].database, database->name) == 0 + && strcmp (functions[i].service, user->name) == 0) + fprintf (out, " &fixed_known_functions[%d], /* %s */\n", + i, known_function_name (&functions[i])); + fputs (" NULL\n", out); + fputs ("};\n", out); + fputs ("\n", out); + + return list_name; +} + + +/* Return the name of the status value STATUS, as a statically + allocated string. */ +const char * +lookup_status_name (enum nss_status status) +{ + switch (status) + { + case NSS_STATUS_TRYAGAIN: return "NSS_STATUS_TRYAGAIN"; + case NSS_STATUS_UNAVAIL: return "NSS_STATUS_UNAVAIL"; + case NSS_STATUS_NOTFOUND: return "NSS_STATUS_NOTFOUND"; + case NSS_STATUS_SUCCESS: return "NSS_STATUS_SUCCESS"; + case NSS_STATUS_RETURN: return "NSS_STATUS_RETURN"; + default: abort (); + }; +} + + +/* Return the name of ACTION as a statically allocated string. */ +const char * +lookup_action_name (lookup_actions action) +{ + switch (action) + { + case NSS_ACTION_CONTINUE: return "NSS_ACTION_CONTINUE"; + case NSS_ACTION_RETURN: return "NSS_ACTION_RETURN"; + default: abort (); + } +} + + +/* Print code to OUT for the list of service_user structures starting + with USER, which are all for DATABASE. Return the name of the + first structure in that list, or zero if USER is NULL. */ +char * +generate_service_user_list (FILE *out, + name_database_entry *database, + service_user *user) +{ + if (user) + { + /* Generate the tail of the list. */ + char *next_name = generate_service_user_list (out, database, user->next); + /* Generate our known function list. */ + char *known_function_list_name = + generate_known_function_list (out, database, user); + + char *name = saprintf ("fixed_%s_%s_user", database->name, user->name); + + fprintf (out, "static const service_user %s = {\n", name); + if (next_name) + fprintf (out, " (service_user *) &%s,\n", next_name); + else + fprintf (out, " NULL, /* no next entry */\n"); + fputs (" {\n", out); + int i; + for (i = 0; i < sizeof (user->actions) / sizeof (user->actions[0]); i++) + fprintf (out, " %s, /* %s */\n", + lookup_action_name (user->actions[i]), + lookup_status_name (i - 2)); + fputs (" },\n", out); + fprintf (out, " NULL, /* we never need the service library */\n"); + fprintf (out, " { .array = %s },\n", known_function_list_name); + fprintf (out, " \"%s\"\n", user->name); + fputs ("};\n", out); + fputs ("\n", out); + + return name; + } + else + return NULL; +} + + +/* Print code to OUT for the list of name_database_entry structures + starting with DATABASE. Return the name of the first structure + in that list, or zero if DATABASE is NULL. */ +char * +generate_name_database_entries (FILE *out, name_database_entry *database) +{ + if (database) + { + char *next_name = generate_name_database_entries (out, database->next); + char *service_user_name + = generate_service_user_list (out, database, database->service); + char *name = saprintf ("fixed_%s_name_database", database->name); + + fprintf (out, "static const name_database_entry %s = {\n", name); + + if (next_name) + fprintf (out, " (name_database_entry *) &%s,\n", next_name); + else + fprintf (out, " NULL,\n"); + + if (service_user_name) + fprintf (out, " (service_user *) &%s,\n", service_user_name); + else + fprintf (out, " NULL,\n"); + + fprintf (out, " \"%s\"\n", database->name); + fprintf (out, "};\n"); + fputs ("\n", out); + + return name; + } + else + return NULL; +} + + +void +generate_name_database (FILE *out, name_database *service_table) +{ + /* Produce a linked list of the known name_database_entry + structures. */ + char *entries = generate_name_database_entries (out, service_table->entry); + + /* Now produce the main structure that points to them all. */ + fprintf (out, "static const name_database fixed_name_database = {\n"); + if (entries) + fprintf (out, " (name_database_entry *) &%s,\n", entries); + else + fprintf (out, " NULL,\n"); + fputs (" NULL /* we don't need the libraries */\n" + "};\n", + out); +} + + + +/* Generating the list of service libraries we generate references to. */ + +/* String with revision number of the shared object files. */ +static const char *const nss_shlib_revision = LIBNSS_FILES_SO + 15; + +void +generate_service_lib_list (FILE *out, name_database *service_table) +{ + int i, j; + int printed_any = 0; + + for (i = 0; functions[i].kind; i++) + { + /* Mention each service library only once. */ + for (j = 0; j < i; j++) + if (strcmp (functions[i].service, functions[j].service) == 0) + break; + + if (j >= i) + { + if (printed_any) + putc (' ', out); + fprintf (out, "libnss_%s.so%s", + functions[i].service, + nss_shlib_revision); + printed_any = 1; + } + } +} + + +/* Main. */ + +int +main (int argc, char **argv) +{ + if (argc != 4) + { + fprintf (stderr, "usage: gen-fixed-nsswitch HEADER SERVLIBS CONFIG\n"); + exit (1); + } + + name_database *service_table = nss_parse_file (argv[3]); + + FILE *header = fopen (argv[1], "w"); + if (! header) + { + fprintf (stderr, + "gen-fixed-nsswitch: couldn't open output file %s: %s\n", + argv[1], strerror (errno)); + exit (1); + } + fputs ("/* Generated by nss/gen-fixed-nsswitch.c. */\n", header); + fputs ("\n", header); + generate_known_functions (header); + generate_name_database (header, service_table); + fclose (header); + + FILE *service_lib_list = fopen (argv[2], "w"); + if (! service_lib_list) + { + fprintf (stderr, + "gen-fixed-nsswitch: couldn't open output file %s: %s\n", + argv[2], strerror (errno)); + exit (1); + } + generate_service_lib_list (service_lib_list, service_table); + fclose (service_lib_list); + + return 0; +} diff --git a/libc/nss/nsswitch.c b/libc/nss/nsswitch.c index 8d86e887f..fa360ff48 100644 --- a/libc/nss/nsswitch.c +++ b/libc/nss/nsswitch.c @@ -41,6 +41,15 @@ #include "nsswitch.h" #include "../nscd/nscd_proto.h" +/* When OPTION_EGLIBC_NSSWITCH is disabled, we use fixed tables of + databases and services, generated at library build time. Thus: + - We can't reconfigure individual databases, so we don't need a + name-to-database map. + - We never add databases or service libraries, or look up functions + at runtime, so there's no need for a lock to protect our tables. + See ../option-groups.def for the details. */ +#ifdef OPTION_EGLIBC_NSSWITCH + /* Prototypes for the local functions. */ static name_database *nss_parse_file (const char *fname) internal_function; static name_database_entry *nss_getline (char *line) internal_function; @@ -74,6 +83,9 @@ static const struct __libc_lock_define_initialized (static, lock) +#define lock_nsswitch (__libc_lock_lock (lock)) +#define unlock_nsswitch (__libc_lock_unlock (lock)) + #if !defined DO_STATIC_NSS || defined SHARED /* String with revision number of the shared object files. */ static const char *const __nss_shlib_revision = LIBNSS_FILES_SO + 15; @@ -82,6 +94,20 @@ static const char *const __nss_shlib_revision = LIBNSS_FILES_SO + 15; /* The root of the whole data base. */ static name_database *service_table; +#else /* OPTION_EGLIBC_NSSWITCH */ + +/* Bring in the statically initialized service table we generated at + build time. */ +#include "fixed-nsswitch.h" + +const static name_database *service_table = &fixed_name_database; + +/* Nothing ever changes, so there's no need to lock anything. */ +#define lock_nsswitch (0) +#define unlock_nsswitch (0) + +#endif /* OPTION_EGLIBC_NSSWITCH */ + /* -1 == database not found 0 == database entry pointer stored */ @@ -90,20 +116,22 @@ __nss_database_lookup (const char *database, const char *alternate_name, const char *defconfig, service_user **ni) { /* Prevent multiple threads to change the service table. */ - __libc_lock_lock (lock); + lock_nsswitch; /* Reconsider database variable in case some other thread called `__nss_configure_lookup' while we waited for the lock. */ if (*ni != NULL) { - __libc_lock_unlock (lock); + unlock_nsswitch; return 0; } +#ifdef OPTION_EGLIBC_NSSWITCH /* Are we initialized yet? */ if (service_table == NULL) /* Read config file. */ service_table = nss_parse_file (_PATH_NSSWITCH_CONF); +#endif /* Test whether configuration data is available. */ if (service_table != NULL) @@ -125,6 +153,7 @@ __nss_database_lookup (const char *database, const char *alternate_name, *ni = entry->service; } +#ifdef OPTION_EGLIBC_NSSWITCH /* No configuration data is available, either because nsswitch.conf doesn't exist or because it doesn't has a line for this database. @@ -133,8 +162,18 @@ __nss_database_lookup (const char *database, const char *alternate_name, if (*ni == NULL) *ni = nss_parse_service_list (defconfig ?: "nis [NOTFOUND=return] files"); +#else + /* Without the dynamic behavior, we can't process defconfig. The + databases the user specified at library build time are all you + get. */ + if (*ni == NULL) + { + unlock_nsswitch; + return -1; + } +#endif - __libc_lock_unlock (lock); + unlock_nsswitch; return 0; } @@ -205,6 +244,7 @@ __nss_next (service_user **ni, const char *fct_name, void **fctp, int status, libc_hidden_def (__nss_next) +#ifdef OPTION_EGLIBC_NSSWITCH int __nss_configure_lookup (const char *dbname, const char *service_line) { @@ -244,12 +284,12 @@ __nss_configure_lookup (const char *dbname, const char *service_line) } /* Prevent multiple threads to change the service table. */ - __libc_lock_lock (lock); + lock_nsswitch; /* Install new rules. */ *databases[cnt].dbp = new_db; - __libc_lock_unlock (lock); + unlock_nsswitch; return 0; } @@ -270,7 +310,7 @@ __nss_lookup_function (service_user *ni, const char *fct_name) void **found, *result; /* We now modify global data. Protect it. */ - __libc_lock_lock (lock); + lock_nsswitch; /* Search the tree of functions previously requested. Data in the tree are `known_function' structures, whose first member is a @@ -281,7 +321,7 @@ __nss_lookup_function (service_user *ni, const char *fct_name) enough to a pointer to our structure to use as a lookup key that will be passed to `known_compare' (above). */ - found = __tsearch (&fct_name, (void **) &ni->known, &known_compare); + found = __tsearch (&fct_name, &ni->known.tree, &known_compare); if (*found != &fct_name) /* The search found an existing structure in the tree. */ result = ((known_function *) *found)->fct_ptr; @@ -298,7 +338,7 @@ __nss_lookup_function (service_user *ni, const char *fct_name) remove_from_tree: /* Oops. We can't instantiate this node properly. Remove it from the tree. */ - __tdelete (&fct_name, (void **) &ni->known, &known_compare); + __tdelete (&fct_name, &ni->known.tree, &known_compare); result = NULL; } else @@ -411,13 +451,43 @@ __nss_lookup_function (service_user *ni, const char *fct_name) } /* Remove the lock. */ - __libc_lock_unlock (lock); + unlock_nsswitch; return result; } libc_hidden_def (__nss_lookup_function) +#else /* below if ! defined (OPTION_EGLIBC_NSSWITCH) */ + + +int +__nss_configure_lookup (const char *dbname, const char *service_line) +{ + /* We can't dynamically configure lookup without + OPTION_EGLIBC_NSSWITCH. */ + __set_errno (EINVAL); + return -1; +} + + +void * +__nss_lookup_function (service_user *ni, const char *fct_name) +{ + int i; + const known_function **known = ni->known.array; + + for (i = 0; known[i]; i++) + if (strcmp (fct_name, known[i]->fct_name) == 0) + return known[i]->fct_ptr; + + return NULL; +} +libc_hidden_def (__nss_lookup_function) +#endif + + +#ifdef OPTION_EGLIBC_NSSWITCH static name_database * internal_function nss_parse_file (const char *fname) @@ -520,8 +590,10 @@ nss_parse_service_list (const char *line) + (line - name + 1)); if (new_service == NULL) return result; + new_service->name = (char *) (new_service + 1); - *((char *) __mempcpy (new_service->name, name, line - name)) = '\0'; + *((char *) __mempcpy ((char *) new_service->name, name, line - name)) + = '\0'; /* Set default actions. */ new_service->actions[2 + NSS_STATUS_TRYAGAIN] = NSS_ACTION_CONTINUE; @@ -530,7 +602,7 @@ nss_parse_service_list (const char *line) new_service->actions[2 + NSS_STATUS_SUCCESS] = NSS_ACTION_RETURN; new_service->actions[2 + NSS_STATUS_RETURN] = NSS_ACTION_RETURN; new_service->library = NULL; - new_service->known = NULL; + new_service->known.tree = NULL; new_service->next = NULL; while (isspace (line[0])) @@ -661,9 +733,10 @@ nss_getline (char *line) result = (name_database_entry *) malloc (sizeof (name_database_entry) + len); if (result == NULL) return NULL; + result->name = (char *) (result + 1); /* Save the database name. */ - memcpy (result->name, name, len); + memcpy ((char *) result->name, name, len); /* Parse the list of services. */ result->service = nss_parse_service_list (line); @@ -697,6 +770,7 @@ nss_new_service (name_database *database, const char *name) return *currentp; } +#endif /* OPTION_EGLIBC_NSSWITCH */ #ifdef OPTION_EGLIBC_INET @@ -713,6 +787,7 @@ __nss_disable_nscd (void) #endif /* OPTION_EGLIBC_INET */ +#ifdef OPTION_EGLIBC_NSSWITCH /* Free all resources if necessary. */ libc_freeres_fn (free_mem) { @@ -737,8 +812,8 @@ libc_freeres_fn (free_mem) { service_user *olds = service; - if (service->known != NULL) - __tdestroy (service->known, free); + if (service->known.tree != NULL) + __tdestroy (service->known.tree, free); service = service->next; free (olds); @@ -762,3 +837,4 @@ libc_freeres_fn (free_mem) free (top); } +#endif /* OPTION_EGLIBC_NSSWITCH */ diff --git a/libc/nss/nsswitch.h b/libc/nss/nsswitch.h index 96568c69a..6eb0ed0c1 100644 --- a/libc/nss/nsswitch.h +++ b/libc/nss/nsswitch.h @@ -65,10 +65,20 @@ typedef struct service_user lookup_actions actions[5]; /* Link to the underlying library object. */ service_library *library; - /* Collection of known functions. */ - struct entry *known; + /* Collection of known functions. + + With OPTION_EGLIBC_NSSWITCH enabled, this is the root of a + 'tsearch'-style tree. + + With OPTION_EGLIBC_NSSWITCH disabled, this is an array of + pointers to known_function structures, NULL-terminated. */ + union + { + void *tree; + const known_function **array; + } known; /* Name of the service (`files', `dns', `nis', ...). */ - char name[0]; + const char *name; } service_user; /* To access the action based on the status value use this macro. */ @@ -82,7 +92,7 @@ typedef struct name_database_entry /* List of service to be used. */ service_user *service; /* Name of the database. */ - char name[0]; + const char *name; } name_database_entry; diff --git a/libc/option-groups.def b/libc/option-groups.def index 3a7a1af3a..46acf60b6 100644 --- a/libc/option-groups.def +++ b/libc/option-groups.def @@ -288,6 +288,78 @@ config OPTION_EGLIBC_LOCALE_CODE group; if you disable OPTION_EGLIBC_LOCALE_CODE, you must also disable OPTION_EGLIBC_CATGETS. +config OPTION_EGLIBC_NSSWITCH + bool "Name service switch (nsswitch) support" + help + + This option group includes support for the 'nsswitch' facility. + With this option group enabled, all EGLIBC functions for + accessing various system databases (passwords and groups; + networking; aliases; public keys; and so on) consult the + '/etc/nsswitch.conf' configuration file to decide how to handle + queries. + + With this option group disabled, EGLIBC uses a fixed list of + services to satisfy queries on each database, as requested by + configuration files specified when EGLIBC is built. Your + 'option-groups.config' file must set the following two + variables: + + OPTION_EGLIBC_NSSWITCH_FIXED_CONFIG + + Set this to the name of a file whose contents observe the + same syntax as an ordinary '/etc/nsswitch.conf' file. The + EGLIBC build process parses this file just as EGLIBC would + at run time if OPTION_EGLIBC_NSSWITCH were enabled, and + produces a C library that uses the nsswitch service + libraries to search for database entries as this file + specifies, instead of consulting '/etc/nsswitch.conf' at run + time. + + This should be a relative file name; EGLIBC will look for it + in the top build directory, along with the + 'option-groups.config' file. + + The EGLIBC source tree includes a sample configuration file + named 'nss/fixed-nsswitch.conf'; for simple configurations, + you will probably want to delete references to databases not + needed on your system. + + OPTION_EGLIBC_NSSWITCH_FIXED_FUNCTIONS + + The EGLIBC build process uses this file to decide which + functions to make available from which service libraries. + The file 'nss/fixed-nsswitch.functions' serves as a sample + configuration file for this setting, and explains its syntax + and meaning in more detail. + + This should be a relative file name; EGLIBC will look for it + in the top build directory, along with the + 'option-groups.config' file. + + Be sure to mention each function in each service you wish to + use. If you do not mention a service's function here, the + EGLIBC database access functions will not find it, even if + it is listed in the OPTION_EGLIBC_NSSWITCH_FIXED_CONFIG + file. + + In this arrangement, EGLIBC will not use the 'dlopen' and + 'dlsym' functions to find database access functions. Instead, + libc hard-codes references to the service libraries' database + access functions. You must explicitly link your program + against the name service libraries (those whose names start + with 'libnss_', in the sysroot's '/lib' directory) whose + functions you intend to use. This arrangement helps + system-wide static analysis tools decide which functions a + system actually uses. + + Note that some nsswitch service libraries require other option + groups to be enabled; for example, the OPTION_EGLIBC_INET + option group must be enabled to use the 'libnss_dns.so.2' or + 'libnss_nis.so.2' service libraries, which use the Domain Name + System and Network Information Service network protocols to + answer queries. + config OPTION_POSIX_REGEXP bool "Regular expressions" help diff --git a/libc/option-groups.defaults b/libc/option-groups.defaults index 9f619c4c2..4eb930f68 100644 --- a/libc/option-groups.defaults +++ b/libc/option-groups.defaults @@ -14,4 +14,5 @@ OPTION_EGLIBC_INET = y OPTION_EGLIBC_LIBM = y OPTION_EGLIBC_LOCALES = y OPTION_EGLIBC_LOCALE_CODE = y +OPTION_EGLIBC_NSSWITCH = y OPTION_POSIX_REGEXP = y |