[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[patches] Re: Patch for OPTION_EGLIBC_NSSWITCH option group.



Okay; here's what I've actually committed.  The only change from
yesterday's patch is the nit about database and service names.

It occurs to me that there might be an even smaller and simpler way to
do things, if we can bring the random uses of __nss_lookup and
__nss_lookup_function scattered around EGLIBC into line.  If
everything goes through a getXXent_r.c or getXXbyYY_r.c file, then
gen-fixed-nsswitch could produce a header file of macro definitions,
and the getXXmumble files can cite the macro for the database and
query they're making.  This would mean that references to service
library functions would appear *only* in the files that need them.  If
those functions were not referred to by user code, then libopt could
trim stuff from the service libraries as well.  A project for another
day...

ChangeLog.eglibc:
2007-07-12  Jim Blandy  <jimb@xxxxxxxxxxxxxxxx>

	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.

Index: nss/fixed-nsswitch.conf
===================================================================
--- nss/fixed-nsswitch.conf	(revision 0)
+++ nss/fixed-nsswitch.conf	(revision 0)
@@ -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
Index: nss/Makefile
===================================================================
--- nss/Makefile	(revision 2804)
+++ nss/Makefile	(working copy)
@@ -26,7 +26,8 @@
 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 @@
 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 @@
 				    $(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)
Index: nss/nsswitch.c
===================================================================
--- nss/nsswitch.c	(revision 2804)
+++ nss/nsswitch.c	(working copy)
@@ -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 @@
 
 __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,7 +94,21 @@
 /* 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 */
 int
@@ -90,20 +116,22 @@
 		       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 @@
 	    *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 @@
   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 @@
 libc_hidden_def (__nss_next)
 
 
+#ifdef OPTION_EGLIBC_NSSWITCH
 int
 __nss_configure_lookup (const char *dbname, const char *service_line)
 {
@@ -244,12 +284,12 @@
     }
 
   /* 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 @@
   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 @@
      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 @@
 	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 @@
     }
 
   /* 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 @@
 					     + (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 @@
       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 @@
   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 @@
 
   return *currentp;
 }
+#endif /* OPTION_EGLIBC_NSSWITCH */
 
 
 #ifdef OPTION_EGLIBC_INET
@@ -713,6 +787,7 @@
 #endif /* OPTION_EGLIBC_INET */
 
 
+#ifdef OPTION_EGLIBC_NSSWITCH
 /* Free all resources if necessary.  */
 libc_freeres_fn (free_mem)
 {
@@ -737,8 +812,8 @@
 	{
 	  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 @@
 
   free (top);
 }
+#endif /* OPTION_EGLIBC_NSSWITCH */
Index: nss/gen-fixed-nsswitch.c
===================================================================
--- nss/gen-fixed-nsswitch.c	(revision 0)
+++ nss/gen-fixed-nsswitch.c	(revision 0)
@@ -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;
+}
Index: nss/nsswitch.h
===================================================================
--- nss/nsswitch.h	(revision 2804)
+++ nss/nsswitch.h	(working copy)
@@ -65,10 +65,20 @@
   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 @@
   /* List of service to be used.  */
   service_user *service;
   /* Name of the database.  */
-  char name[0];
+  const char *name;
 } name_database_entry;
 
 
Index: nss/fixed-nsswitch.functions
===================================================================
--- nss/fixed-nsswitch.functions	(revision 0)
+++ nss/fixed-nsswitch.functions	(revision 0)
@@ -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)
Index: option-groups.defaults
===================================================================
--- option-groups.defaults	(revision 2804)
+++ option-groups.defaults	(working copy)
@@ -14,4 +14,5 @@
 OPTION_EGLIBC_LIBM = y
 OPTION_EGLIBC_LOCALES = y
 OPTION_EGLIBC_LOCALE_CODE = y
+OPTION_EGLIBC_NSSWITCH = y
 OPTION_POSIX_REGEXP = y
Index: option-groups.def
===================================================================
--- option-groups.def	(revision 2804)
+++ option-groups.def	(working copy)
@@ -288,6 +288,78 @@
        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