1 /* Copyright (C) 1996 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If
17 not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
23 #include <libc-lock.h>
30 #include "../elf/link.h" /* We need some help from ld.so. */
32 /* Prototypes for the local functions. */
33 static void nss_init (void);
34 static void *nss_lookup_function (service_user *ni, const char *fct_name);
35 static int nss_find_entry (struct entry **knownp, const char *key,
37 static void nss_insert_entry (struct entry **knownp, const char *key,
39 static name_database *nss_parse_file (const char *fname);
40 static name_database_entry *nss_getline (char *line);
41 static service_library *nss_new_service (name_database *database,
45 __libc_lock_define_initialized (static, lock);
48 /* Global variable. */
49 struct __res_state _res;
52 /* Known aliases for service names. */
58 { "nis+", "nisplus" },
63 /* Nonzero if the sevices are already initialized. */
64 static int nss_initialized;
67 /* The root of the whole data base. */
68 static name_database *service_table;
74 /* Prevent multiple threads to change the service table. */
75 __libc_lock_lock (lock);
77 if (service_table == NULL)
78 service_table = nss_parse_file (_PATH_NSSWITCH_CONF);
80 __libc_lock_unlock (lock);
84 /* -1 == database not found
85 0 == database entry pointer stored */
87 __nss_database_lookup (const char *database, service_user **ni)
89 if (nss_initialized == 0)
92 /* Test whether configuration data is available. */
95 /* Return first `service_user' entry for DATABASE.
96 XXX Will use perfect hashing function for known databases. */
97 name_database_entry *entry;
99 /* XXX Could use some faster mechanism here. But each database is
100 only requested once and so this might not be critical. */
101 for (entry = service_table->entry; entry != NULL; entry = entry->next)
102 if (strcmp (database, entry->name) == 0)
104 *ni = entry->service;
109 /* No configuration data is available, either because nsswitch.conf
110 doesn't exist or because it doesn't have a line for this database.
111 Use a default equivalent to:
112 database: compat [NOTFOUND=return] dns [NOTFOUND=return] files
115 #define DEFAULT_SERVICE(name, next) \
116 static service_user default_##name = \
120 NSS_ACTION_CONTINUE, \
121 NSS_ACTION_CONTINUE, \
128 DEFAULT_SERVICE (files, NULL);
129 DEFAULT_SERVICE (dns, &default_files);
130 DEFAULT_SERVICE (compat, &default_dns);
131 *ni = &default_compat;
138 0 == adjusted for next function */
140 __nss_lookup (service_user **ni, const char *fct_name, void **fctp)
142 *fctp = nss_lookup_function (*ni, fct_name);
145 && nss_next_action (*ni, NSS_STATUS_UNAVAIL) == NSS_ACTION_CONTINUE
146 && (*ni)->next != NULL)
150 *fctp = nss_lookup_function (*ni, fct_name);
153 return *fctp != NULL ? 0 : -1;
158 0 == adjusted for next function
161 __nss_next (service_user **ni, const char *fct_name, void **fctp, int status,
166 if (nss_next_action (*ni, NSS_STATUS_TRYAGAIN) == NSS_ACTION_RETURN
167 && nss_next_action (*ni, NSS_STATUS_UNAVAIL) == NSS_ACTION_RETURN
168 && nss_next_action (*ni, NSS_STATUS_NOTFOUND) == NSS_ACTION_RETURN
169 && nss_next_action (*ni, NSS_STATUS_SUCCESS) == NSS_ACTION_RETURN)
174 /* This is really only for debugging. */
175 if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_SUCCESS)
176 __libc_fatal ("illegal status in " __FUNCTION__);
178 if (nss_next_action (*ni, status) == NSS_ACTION_RETURN)
182 if ((*ni)->next == NULL)
189 *fctp = nss_lookup_function (*ni, fct_name);
192 && nss_next_action (*ni, NSS_STATUS_UNAVAIL) == NSS_ACTION_CONTINUE
193 && (*ni)->next != NULL);
195 return *fctp != NULL ? 0 : -1;
200 nss_dlerror_run (void (*operate) (void))
202 const char *last_errstring = NULL;
203 const char *last_object_name = NULL;
205 (void) _dl_catch_error (&last_errstring, &last_object_name, operate);
207 return last_errstring != NULL;
212 nss_lookup_function (service_user *ni, const char *fct_name)
216 /* Determine whether current function is loaded. */
217 if (nss_find_entry (&ni->known, fct_name, &result) >= 0)
220 /* We now modify global data. Protect it. */
221 __libc_lock_lock (lock);
223 if (ni->library == NULL)
225 /* This service has not yet been used. Fetch the service library
226 for it, creating a new one if need be. If there is no service
227 table from the file, this static variable holds the head of the
228 service_library list made from the default configuration. */
229 static name_database default_table;
230 ni->library = nss_new_service (service_table ?: &default_table,
232 if (ni->library == NULL)
234 /* This only happens when out of memory. */
235 __libc_lock_unlock (lock);
240 if (ni->library->lib_handle == NULL)
242 /* Load the shared library. */
243 size_t shlen = (7 + strlen (ni->library->name) + 3
244 + sizeof (NSS_SHLIB_REVISION));
245 char shlib_name[shlen];
249 /* The used function is found in GNU ld.so. XXX The first
250 argument to _dl_open used to be `_dl_loaded'. But this
251 does (currently) not work. */
252 ni->library->lib_handle = _dl_open (shlib_name, RTLD_LAZY);
255 /* Construct name. */
256 __stpcpy (__stpcpy (__stpcpy (shlib_name, "libnss_"), ni->library->name),
257 ".so" NSS_SHLIB_REVISION);
259 if (nss_dlerror_run (do_open) != 0)
260 /* Failed to load the library. */
261 ni->library->lib_handle = (void *) -1;
264 if (ni->library->lib_handle == (void *) -1)
265 /* Library not found => function not found. */
269 /* Get the desired function. Again, GNU ld.so magic ahead. */
270 size_t namlen = (5 + strlen (ni->library->name) + 1
271 + strlen (fct_name) + 1);
273 struct link_map *map = ni->library->lib_handle;
275 const Elf32_Sym *ref = NULL;
278 struct link_map *scope[2] = { map, NULL };
279 loadbase = _dl_lookup_symbol (name, &ref, scope, map->l_name, 0, 0);
282 __stpcpy (__stpcpy (__stpcpy (__stpcpy (name, "_nss_"),
287 result = (nss_dlerror_run (get_sym)
288 ? NULL : (void *) (loadbase + ref->st_value));
291 /* Remember function pointer for the usage. */
292 nss_insert_entry (&ni->known, fct_name, result);
294 /* Remove the lock. */
295 __libc_lock_unlock (lock);
302 known_compare (const void *p1, const void *p2)
304 known_function *v1 = (known_function *) p1;
305 known_function *v2 = (known_function *) p2;
307 return strcmp (v1->fct_name, v2->fct_name);
312 nss_find_entry (struct entry **knownp, const char *key, void **valp)
314 known_function looking_for = { fct_name: key };
315 struct entry **found;
317 found = __tfind (&looking_for, (const void **) knownp, known_compare);
322 *valp = ((known_function *) (*found)->key)->fct_ptr;
329 nss_insert_entry (struct entry **knownp, const char *key, void *val)
331 known_function *to_insert;
333 to_insert = (known_function *) malloc (sizeof (known_function));
334 if (to_insert == NULL)
337 to_insert->fct_name = key;
338 to_insert->fct_ptr = val;
340 __tsearch (to_insert, (void **) knownp, known_compare);
344 static name_database *
345 nss_parse_file (const char *fname)
348 name_database *result;
349 name_database_entry *last;
353 /* Open the configuration file. */
354 fp = fopen (fname, "r");
358 result = (name_database *) malloc (sizeof (name_database));
362 result->entry = NULL;
363 result->library = NULL;
369 name_database_entry *this;
373 n = getline (&line, &len, fp);
376 if (line[n - 1] == '\n')
379 /* Because the file format does not know any form of quoting we
380 can search forward for the next '#' character and if found
381 make it terminating the line. */
382 cp = strchr (line, '#');
386 /* If the line is blank it is ignored. */
390 /* Each line completely specifies the actions for a database. */
391 this = nss_getline (line);
397 result->entry = this;
404 /* Free the buffer. */
406 /* Close configuration file. */
413 static name_database_entry *
414 nss_getline (char *line)
417 name_database_entry *result;
420 /* Ignore leading white spaces. ATTENTION: this is different from
421 what is implemented in Solaris. The Solaris man page says a line
422 beginning with a white space character is ignored. We regard
423 this as just another misfeature in Solaris. */
424 while (isspace (line[0]))
427 /* Recognize `<database> ":"'. */
429 while (line[0] != '\0' && !isspace (line[0]) && line[0] != ':')
431 if (line[0] == '\0' || name == line)
436 result = (name_database_entry *) malloc (sizeof (name_database_entry));
440 result->name = strdup (name);
441 if (result->name == NULL)
446 result->service = NULL;
450 /* Read the source names: `<source> ( "[" <status> "=" <action> "]" )*'. */
453 service_user *new_service;
456 while (isspace (line[0]))
459 /* No source specified. */
462 /* Read <source> identifier. */
464 while (line[0] != '\0' && !isspace (line[0]) && line[0] != '[')
470 new_service = (service_user *) malloc (sizeof (service_user));
471 if (new_service == NULL)
474 /* Test whether the source name is one of the aliases. */
475 for (n = 0; n < sizeof (service_alias) / sizeof (service_alias[0]); ++n)
476 if (strncmp (service_alias[n].alias, name, line - name) == 0
477 && service_alias[n].alias[line - name] == '\0')
480 if (n < sizeof (service_alias) / sizeof (service_alias[0]))
481 new_service->name = service_alias[n].value;
484 char *source = (char *) malloc (line - name + 1);
490 memcpy (source, name, line - name);
491 source[line - name] = '\0';
493 new_service->name = source;
496 /* Set default actions. */
497 new_service->actions[2 + NSS_STATUS_TRYAGAIN] = NSS_ACTION_CONTINUE;
498 new_service->actions[2 + NSS_STATUS_UNAVAIL] = NSS_ACTION_CONTINUE;
499 new_service->actions[2 + NSS_STATUS_NOTFOUND] = NSS_ACTION_CONTINUE;
500 new_service->actions[2 + NSS_STATUS_SUCCESS] = NSS_ACTION_RETURN;
501 new_service->library = NULL;
502 new_service->known = NULL;
503 new_service->next = NULL;
505 while (isspace (line[0]))
512 /* Read criterions. */
515 while (line[0] != '\0' && isspace (line[0]));
519 /* Read status name. */
521 while (line[0] != '\0' && !isspace (line[0]) && line[0] != '='
525 /* Compare with known statii. */
526 if (line - name == 7)
528 if (strncasecmp (name, "SUCCESS", 7) == 0)
529 status = NSS_STATUS_SUCCESS;
530 else if (strncasecmp (name, "UNAVAIL", 7) == 0)
531 status = NSS_STATUS_UNAVAIL;
535 else if (line - name == 8)
537 if (strncasecmp (name, "NOTFOUND", 8) == 0)
538 status = NSS_STATUS_NOTFOUND;
539 else if (strncasecmp (name, "TRYAGAIN", 8) == 0)
540 status = NSS_STATUS_TRYAGAIN;
547 while (isspace (line[0]))
553 while (isspace (line[0]));
556 while (line[0] != '\0' && !isspace (line[0]) && line[0] != '='
560 if (line - name == 6 && strncasecmp (name, "RETURN", 6) == 0)
561 new_service->actions[2 + status] = NSS_ACTION_RETURN;
562 else if (line - name == 8
563 && strncasecmp (name, "CONTINUE", 8) == 0)
564 new_service->actions[2 + status] = NSS_ACTION_CONTINUE;
568 /* Match white spaces. */
569 while (isspace (line[0]))
572 while (line[0] != ']');
579 result->service = new_service;
581 last->next = new_service;
589 static service_library *
590 nss_new_service (name_database *database, const char *name)
592 service_library **currentp = &database->library;
594 while (*currentp != NULL)
596 if (strcmp ((*currentp)->name, name) == 0)
598 currentp = &(*currentp)->next;
601 /* We have to add the new service. */
602 *currentp = (service_library *) malloc (sizeof (service_library));
603 if (*currentp == NULL)
606 (*currentp)->name = name;
607 (*currentp)->lib_handle = NULL;
608 (*currentp)->next = NULL;