X-Git-Url: http://git.csclub.uwaterloo.ca/?p=kopensolaris-gnu%2Fglibc.git;a=blobdiff_plain;f=nss%2Fnsswitch.c;h=36c1d0035ef7b4c52e650c71b3daf9a979d04933;hp=9b6c4eb12fa4993548110ec4b06fbba89c468243;hb=40b510f64dd26b2c5fedd93779895d54562f52bf;hpb=295323394b2fb5501b3108d5f0c37e82e6feed43 diff --git a/nss/nsswitch.c b/nss/nsswitch.c index 9b6c4eb12f..36c1d0035e 100644 --- a/nss/nsswitch.c +++ b/nss/nsswitch.c @@ -1,115 +1,136 @@ -/* Copyright (C) 1996 Free Software Foundation, Inc. -This file is part of the GNU C Library. -Contributed by Ulrich Drepper , 1996. +/* Copyright (C) 1996, 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 1996. -The GNU C Library is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public License as -published by the Free Software Foundation; either version 2 of the -License, or (at your option) any later version. + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 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 -Library General Public License for more details. + 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 + Library General Public License for more details. -You should have received a copy of the GNU Library General Public -License along with the GNU C Library; see the file COPYING.LIB. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. 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 /* We need some help from ld.so. */ #include #include #include #include +#if !defined DO_STATIC_NSS || defined PIC +# include +#endif + #include "nsswitch.h" -#include "../elf/link.h" /* We need some help from ld.so. */ /* Prototypes for the local functions. */ -static void nss_init (void); -static void *nss_lookup_function (service_user *ni, const char *fct_name); -static int nss_find_entry (struct entry **knownp, const char *key, - void **valp); -static void nss_insert_entry (struct entry **knownp, const char *key, - void *val); -static name_database *nss_parse_file (const char *fname); -static name_database_entry *nss_getline (char *line); -static service_user *nss_parse_service_list (const char *line); +static void *nss_lookup_function (service_user *ni, const char *fct_name) + internal_function; +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 service_library *nss_new_service (name_database *database, - const char *name); + const char *name) internal_function; -__libc_lock_define_initialized (static, lock); +/* Declare external database variables. */ +#define DEFINE_DATABASE(name) \ + extern service_user *__nss_##name##_database; \ + weak_extern (__nss_##name##_database) +#include "databases.def" +#undef DEFINE_DATABASE +/* Structure to map database name to variable. */ +static struct +{ + const char *name; + service_user **dbp; +} databases[] = +{ +#define DEFINE_DATABASE(name) \ + { #name, &__nss_##name##_database }, +#include "databases.def" +#undef DEFINE_DATABASE +}; -/* Global variable. */ -struct __res_state _res; +__libc_lock_define_initialized (static, lock) -/* Nonzero if the sevices are already initialized. */ -static int nss_initialized; +#if !defined DO_STATIC_NSS || defined PIC +/* String with revision number of the shared object files. */ +static const char *const __nss_shlib_revision = LIBNSS_FILES_SO + 15; +#endif /* The root of the whole data base. */ static name_database *service_table; -static void -nss_init (void) +/* -1 == database not found + 0 == database entry pointer stored */ +int +__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); + /* 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); + return 0; + } + + /* Are we initialized yet? */ if (service_table == NULL) + /* Read config file. */ service_table = nss_parse_file (_PATH_NSSWITCH_CONF); - __libc_lock_unlock (lock); -} - - -/* -1 == database not found - 0 == database entry pointer stored */ -int -__nss_database_lookup (const char *database, const char *defconfig, - service_user **ni) -{ - name_database_entry *entry; - - if (nss_initialized == 0) - nss_init (); - /* Test whether configuration data is available. */ - if (service_table) + if (service_table != NULL) { - /* Return first `service_user' entry for DATABASE. - XXX Will use perfect hashing function for known databases. */ + /* Return first `service_user' entry for DATABASE. */ + name_database_entry *entry; /* XXX Could use some faster mechanism here. But each database is only requested once and so this might not be critical. */ for (entry = service_table->entry; entry != NULL; entry = entry->next) if (strcmp (database, entry->name) == 0) - { + *ni = entry->service; + + if (*ni == NULL && alternate_name != NULL) + /* We haven't found an entry so far. Try to find it with the + alternative name. */ + for (entry = service_table->entry; entry != NULL; entry = entry->next) + if (strcmp (alternate_name, entry->name) == 0) *ni = entry->service; - return 0; - } } /* No configuration data is available, either because nsswitch.conf - doesn't exist or because it doesn't have a line for this database. */ - entry = malloc (sizeof *entry); - if (entry == NULL) - return -1; - entry->name = database; - /* DEFCONFIG specifies the default service list for this database, + doesn't exist or because it doesn't has a line for this database. + + DEFCONFIG specifies the default service list for this database, or null to use the most common default. */ - entry->service = nss_parse_service_list (defconfig ?: - "compat [NOTFOUND=return] files"); + if (*ni == NULL) + *ni = nss_parse_service_list (defconfig + ?: "nis [NOTFOUND=return] files"); + + __libc_lock_unlock (lock); - *ni = entry->service; return 0; } @@ -152,7 +173,7 @@ __nss_next (service_user **ni, const char *fct_name, void **fctp, int status, else { /* This is really only for debugging. */ - if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_SUCCESS) + if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN) __libc_fatal ("illegal status in " __FUNCTION__); if (nss_next_action (*ni, status) == NSS_ACTION_RETURN) @@ -176,150 +197,289 @@ __nss_next (service_user **ni, const char *fct_name, void **fctp, int status, } -static int -nss_dlerror_run (void (*operate) (void)) -{ - const char *last_errstring = NULL; - const char *last_object_name = NULL; - - (void) _dl_catch_error (&last_errstring, &last_object_name, operate); - - return last_errstring != NULL; -} - - -static void * -nss_lookup_function (service_user *ni, const char *fct_name) +int +__nss_configure_lookup (const char *dbname, const char *service_line) { - void *result; + service_user *new_db; + size_t cnt; - /* Determine whether current function is loaded. */ - if (nss_find_entry (&ni->known, fct_name, &result) >= 0) - return result; - - /* We now modify global data. Protect it. */ - __libc_lock_lock (lock); - - if (ni->library == NULL) + for (cnt = 0; cnt < sizeof databases; ++cnt) { - /* This service has not yet been used. Fetch the service library - for it, creating a new one if need be. If there is no service - table from the file, this static variable holds the head of the - service_library list made from the default configuration. */ - static name_database default_table; - ni->library = nss_new_service (service_table ?: &default_table, - ni->name); - if (ni->library == NULL) + int cmp = strcmp (dbname, databases[cnt].name); + if (cmp == 0) + break; + if (cmp < 0) { - /* This only happens when out of memory. */ - __libc_lock_unlock (lock); - return NULL; + __set_errno (EINVAL); + return -1; } } - if (ni->library->lib_handle == NULL) + if (cnt == sizeof databases) { - /* Load the shared library. */ - size_t shlen = (7 + strlen (ni->library->name) + 3 - + sizeof (NSS_SHLIB_REVISION)); - char shlib_name[shlen]; - - void do_open (void) - { - /* Open and relocate the shared object. */ - ni->library->lib_handle = _dl_open (shlib_name, RTLD_LAZY); - } - - /* Construct name. */ - __stpcpy (__stpcpy (__stpcpy (shlib_name, "libnss_"), ni->library->name), - ".so" NSS_SHLIB_REVISION); - - if (nss_dlerror_run (do_open) != 0) - /* Failed to load the library. */ - ni->library->lib_handle = (void *) -1; + __set_errno (EINVAL); + return -1; } - if (ni->library->lib_handle == (void *) -1) - /* Library not found => function not found. */ - result = NULL; - else - { - /* Get the desired function. Again, GNU ld.so magic ahead. */ - size_t namlen = (5 + strlen (ni->library->name) + 1 - + strlen (fct_name) + 1); - char name[namlen]; - struct link_map *map = ni->library->lib_handle; - Elf32_Addr loadbase; - const Elf32_Sym *ref = NULL; - void get_sym (void) - { - struct link_map *scope[2] = { map, NULL }; - loadbase = _dl_lookup_symbol (name, &ref, scope, map->l_name, 0, 0); - } - - __stpcpy (__stpcpy (__stpcpy (__stpcpy (name, "_nss_"), - ni->library->name), - "_"), - fct_name); + /* Test whether it is really used. */ + if (databases[cnt].dbp == NULL) + /* Nothing to do, but we could do. */ + return 0; - result = (nss_dlerror_run (get_sym) - ? NULL : (void *) (loadbase + ref->st_value)); + /* Try to generate new data. */ + new_db = nss_parse_service_list (service_line); + if (new_db == NULL) + { + /* Illegal service specification. */ + __set_errno (EINVAL); + return -1; } - /* Remember function pointer for the usage. */ - nss_insert_entry (&ni->known, fct_name, result); + /* Prevent multiple threads to change the service table. */ + __libc_lock_lock (lock); + + /* Install new rules. */ + *databases[cnt].dbp = new_db; - /* Remove the lock. */ __libc_lock_unlock (lock); - return result; + return 0; } +#if !defined DO_STATIC_NSS || defined PIC static int -known_compare (const void *p1, const void *p2) +nss_dlerror_run (void (*operate) (void *), void *args) { - known_function *v1 = (known_function *) p1; - known_function *v2 = (known_function *) p2; + char *last_errstring = NULL; + const char *last_object_name = NULL; + int result; + + (void) _dl_catch_error (&last_errstring, &last_object_name, operate, args); - return strcmp (v1->fct_name, v2->fct_name); + result = last_errstring != NULL; + if (result) + free (last_errstring); + + return result; } -static int -nss_find_entry (struct entry **knownp, const char *key, void **valp) +struct do_open_args { - known_function looking_for = { fct_name: key }; - struct entry **found; + /* Argument to do_open. */ + char *shlib_name; + service_user *ni; +}; - found = __tfind (&looking_for, (const void **) knownp, known_compare); +struct get_sym_args +{ + /* Arguments to get_sym. */ + struct link_map *map; + char *name; - if (found == NULL) - return -1; + /* Return values of get_sym. */ + ElfW(Addr) loadbase; + const ElfW(Sym) *ref; +}; - *valp = ((known_function *) (*found)->key)->fct_ptr; +static void +do_open (void *a) +{ + struct do_open_args *args = (struct do_open_args *) a; + /* Open and relocate the shared object. */ + args->ni->library->lib_handle = _dl_open (args->shlib_name, RTLD_LAZY); +} - return 0; +static void +get_sym (void *a) +{ + struct get_sym_args *args = (struct get_sym_args *) a; + struct link_map *scope[2] = { args->map, NULL }; + args->ref = NULL; + args->loadbase = _dl_lookup_symbol (args->name, &args->ref, + scope, args->map->l_name, 0); } +#endif +/* Comparison function for searching NI->known tree. */ +static int +known_compare (const void *p1, const void *p2) +{ + return p1 == p2 ? 0 : strcmp (*(const char *const *) p1, + *(const char *const *) p2); +} -static void -nss_insert_entry (struct entry **knownp, const char *key, void *val) + +static void * +internal_function +nss_lookup_function (service_user *ni, const char *fct_name) { - known_function *to_insert; + void **found, *result; - to_insert = (known_function *) malloc (sizeof (known_function)); - if (to_insert == NULL) - return; + /* We now modify global data. Protect it. */ + __libc_lock_lock (lock); - to_insert->fct_name = key; - to_insert->fct_ptr = val; + /* Search the tree of functions previously requested. Data in the + tree are `known_function' structures, whose first member is a + `const char *', the lookup key. The search returns a pointer to + the tree node structure; the first member of the is a pointer to + our structure (i.e. what will be a `known_function'); since the + first member of that is the lookup key string, &FCT_NAME is close + 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); + if (*found != &fct_name) + /* The search found an existing structure in the tree. */ + result = ((known_function *) *found)->fct_ptr; + else + { + /* This name was not known before. Now we have a node in the tree + (in the proper sorted position for FCT_NAME) that points to + &FCT_NAME instead of any real `known_function' structure. + Allocate a new structure and fill it in. */ - __tsearch (to_insert, (void **) knownp, known_compare); + known_function *known = malloc (sizeof *known); + if (! known) + { + remove_from_tree: + /* Oops. We can't instantiate this node properly. + Remove it from the tree. */ + __tdelete (&fct_name, (void **) &ni->known, &known_compare); + result = NULL; + } + else + { + /* Point the tree node at this new structure. */ + *found = known; + known->fct_name = fct_name; + + if (ni->library == NULL) + { + /* This service has not yet been used. Fetch the service + library for it, creating a new one if need be. If there + is no service table from the file, this static variable + holds the head of the service_library list made from the + default configuration. */ + static name_database default_table; + ni->library = nss_new_service (service_table ?: &default_table, + ni->name); + if (ni->library == NULL) + { + /* This only happens when out of memory. */ + free (known); + goto remove_from_tree; + } + } + +#if !defined DO_STATIC_NSS || defined PIC + if (ni->library->lib_handle == NULL) + { + /* Load the shared library. */ + size_t shlen = (7 + strlen (ni->library->name) + 3 + + strlen (__nss_shlib_revision) + 1); + + struct do_open_args args; + args.shlib_name = __alloca (shlen); + args.ni = ni; + + /* Construct shared object name. */ + __stpcpy (__stpcpy (__stpcpy (__stpcpy (args.shlib_name, + "libnss_"), + ni->library->name), + ".so"), + __nss_shlib_revision); + + if (nss_dlerror_run (do_open, &args) != 0) + /* Failed to load the library. */ + ni->library->lib_handle = (void *) -1l; + } + + if (ni->library->lib_handle == (void *) -1l) + /* Library not found => function not found. */ + result = NULL; + else + { + /* Get the desired function. Again, GNU ld.so magic ahead. */ + size_t namlen = (5 + strlen (ni->library->name) + 1 + + strlen (fct_name) + 1); + struct get_sym_args args; + args.name = __alloca (namlen); + args.map = ni->library->lib_handle; + + /* Construct the function name. */ + __stpcpy (__stpcpy (__stpcpy (__stpcpy (args.name, "_nss_"), + ni->library->name), + "_"), + fct_name); + + /* Look up the symbol. */ + result = (nss_dlerror_run (get_sym, &args) ? NULL + : (void *) (args.loadbase + args.ref->st_value)); + } +#else + /* We can't get function address dynamically in static linking. */ + { +# define DEFINE_ENT(h,nm) \ + extern void _nss_##h##_get##nm##ent_r (void); \ + extern void _nss_##h##_end##nm##ent (void); \ + extern void _nss_##h##_set##nm##ent (void); +# define DEFINE_GET(h,nm) \ + extern void _nss_##h##_get##nm##_r (void); +# define DEFINE_GETBY(h,nm,ky) \ + extern void _nss_##h##_get##nm##by##ky##_r (void); +# include "function.def" +# undef DEFINE_ENT +# undef DEFINE_GET +# undef DEFINE_GETBY +# define DEFINE_ENT(h,nm) \ + { #h"_get"#nm"ent_r", _nss_##h##_get##nm##ent_r }, \ + { #h"_end"#nm"ent", _nss_##h##_end##nm##ent }, \ + { #h"_set"#nm"ent", _nss_##h##_set##nm##ent }, +# define DEFINE_GET(h,nm) \ + { #h"_get"#nm"_r", _nss_##h##_get##nm##_r }, +# define DEFINE_GETBY(h,nm,ky) \ + { #h"_get"#nm"by"#ky"_r", _nss_##h##_get##nm##by##ky##_r }, + static struct fct_tbl { const char *fname; void *fp; } *tp, tbl[] = + { +# include "function.def" + { NULL, NULL } + }; + size_t namlen = (5 + strlen (ni->library->name) + 1 + + strlen (fct_name) + 1); + char name[namlen]; + + /* Construct the function name. */ + __stpcpy (__stpcpy (__stpcpy (name, ni->library->name), + "_"), + fct_name); + + result = NULL; + for (tp = &tbl[0]; tp->fname; tp++) + if (strcmp (tp->fname, name) == 0) + { + result = tp->fp; + break; + } + } +#endif + + /* Remember function pointer for later calls. Even if null, we + record it so a second try needn't search the library again. */ + known->fct_ptr = result; + } + } + + /* Remove the lock. */ + __libc_lock_unlock (lock); + + return result; } static name_database * +internal_function nss_parse_file (const char *fname) { FILE *fp; @@ -388,8 +548,11 @@ nss_parse_file (const char *fname) } -/* Read the source names: ` ( "[" "=" "]" )*'. */ +/* Read the source names: + `( ( "[" "!"? ( "=" )+ "]" )? )*' + */ static service_user * +internal_function nss_parse_service_list (const char *line) { service_user *result = NULL, **nextp = &result; @@ -397,7 +560,7 @@ nss_parse_service_list (const char *line) while (1) { service_user *new_service; - char *name; + const char *name; while (isspace (line[0])) ++line; @@ -424,8 +587,7 @@ nss_parse_service_list (const char *line) free (new_service); return result; } - memcpy (source, name, line - name); - source[line - name] = '\0'; + *((char *) __mempcpy (source, name, line - name)) = '\0'; new_service->name = source; } @@ -435,6 +597,7 @@ nss_parse_service_list (const char *line) 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 = NULL; new_service->next = NULL; @@ -538,6 +701,7 @@ nss_parse_service_list (const char *line) } static name_database_entry * +internal_function nss_getline (char *line) { const char *name; @@ -584,6 +748,7 @@ nss_getline (char *line) static service_library * +internal_function nss_new_service (name_database *database, const char *name) { service_library **currentp = &database->library;