chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.git] / nss / gen-fixed-nsswitch.c
1 /* gen-fixed-nsswitch.c --- generate fixed name service data structures
2    Copyright (C) 1996-1999, 2001-2006, 2007 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
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    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #define _GNU_SOURCE
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <assert.h>
28 #include <ctype.h>
29
30 #include "gnu/lib-names.h"
31 #include "nss.h"
32
33 /* Provide a fallback definition to allow this file to be compiled outside
34    libc.  */
35 #ifndef internal_function
36 # define internal_function
37 #endif
38
39 \f
40 /* Simple utilities.  */
41
42 void __attribute__ ((noreturn))
43 error (const char *message)
44 {
45   fprintf (stderr, "%s\n", message);
46   exit (1);
47 }
48
49
50 void *
51 check_alloc (void *p)
52 {
53   if (p)
54     return p;
55   else
56     error ("out of memory");
57 }
58
59 void *
60 xmalloc (size_t size)
61 {
62   return check_alloc (malloc (size));
63 }
64
65
66 /* Format ARGS according to FORMAT, and return the result as a
67    malloc'ed string.  */
68 char *
69 saprintf (const char *format, ...)
70 {
71   va_list args;
72   size_t len;
73   char *buf;
74   
75   va_start (args, format);
76   len = vsnprintf (NULL, 0, format, args);
77   va_end (args);
78
79   buf = xmalloc (len + 1);
80   va_start (args, format);
81   assert (len == vsnprintf (buf, len + 1, format, args));
82   va_end (args);
83
84   return buf;
85 }
86
87
88 \f
89 /* Data structures representing the configuration file in memory.  */
90
91 /* These are copied from nsswitch.h.
92
93    We could simply #include that file, but this program runs on the
94    build machine and links against the build machine's libraries,
95    whereas that header is meant for use by target code; it uses
96    'libc_hidden_proto', 'internal_function', and related hair.  Since
97    we've copied the parsing code, we might as well copy the data
98    structure definitions as well.  */
99
100 /* Actions performed after lookup finished.  */
101 typedef enum
102 {
103   NSS_ACTION_CONTINUE,
104   NSS_ACTION_RETURN
105 } lookup_actions;
106
107
108 typedef struct service_library
109 {
110   /* Name of service (`files', `dns', `nis', ...).  */
111   const char *name;
112   /* Pointer to the loaded shared library.  */
113   void *lib_handle;
114   /* And the link to the next entry.  */
115   struct service_library *next;
116 } service_library;
117
118
119 /* For mapping a function name to a function pointer.  It is known in
120    nsswitch.c:nss_lookup_function that a string pointer for the lookup key
121    is the first member.  */
122 typedef struct
123 {
124   const char *fct_name;
125   void *fct_ptr;
126 } known_function;
127
128
129 typedef struct service_user
130 {
131   /* And the link to the next entry.  */
132   struct service_user *next;
133   /* Action according to result.  */
134   lookup_actions actions[5];
135   /* Link to the underlying library object.  */
136   service_library *library;
137   /* Collection of known functions.
138
139      With OPTION_EGLIBC_NSSWITCH enabled, this is the root of a
140      'tsearch'-style tree.
141
142      With OPTION_EGLIBC_NSSWITCH disabled, this is an array of
143      pointers to known_function structures, NULL-terminated.  */
144   union
145   {
146     void *tree;
147     const known_function **array;
148   } known;
149   /* Name of the service (`files', `dns', `nis', ...).  */
150   const char *name;
151 } service_user;
152
153 /* To access the action based on the status value use this macro.  */
154 #define nss_next_action(ni, status) ((ni)->actions[2 + status])
155
156
157 typedef struct name_database_entry
158 {
159   /* And the link to the next entry.  */
160   struct name_database_entry *next;
161   /* List of service to be used.  */
162   service_user *service;
163   /* Name of the database.  */
164   const char *name;
165 } name_database_entry;
166
167
168 typedef struct name_database
169 {
170   /* List of all known databases.  */
171   name_database_entry *entry;
172   /* List of libraries with service implementation.  */
173   service_library *library;
174 } name_database;
175
176
177 \f
178 /* Gathering the contents of the FIXED_FUNCTIONS file.  */
179
180 /* It should be possible to generate this list automatically by
181    looking at the services and databases used in the nsswitch.conf
182    file, and having a hard-coded set of queries supported on each
183    database.  */
184
185 /* We #include the FIXED_FUNCTIONS file several times to build an
186    array of function structures holding its data.  */
187 enum function_kind {
188   fk_end = 0,                   /* Last entry.  */
189   fk_setent,                    /* Like setpwent.  */
190   fk_getent,                    /* Like getpwent.  */
191   fk_endent,                    /* Like endpwent.  */
192   fk_getby,                     /* Like gethostbyname.  */
193   fk_get                        /* Like getpwnam.  */
194 };
195
196
197 struct function {
198   /* What kind of function this is.  */
199   enum function_kind kind;
200
201   /* The database and service of the function being hardwired in.  */
202   char *database, *service;
203
204   /* The kind of entry being queried, for 'fk_setent', 'fk_getent',
205      'fk_endent', and 'fk_getby' functions.  */
206   char *entry;
207
208   /* The key, for 'fk_getby' entries.  */
209   char *key;
210
211   /* The value and key, for 'fk_get' entries.  */
212   char *value_and_key;
213 };
214
215
216 const struct function functions[] =
217   {
218
219 #define DEFINE_ENT(database, service, entry)    \
220     { fk_setent, #database, #service, #entry }, \
221     { fk_getent, #database, #service, #entry }, \
222     { fk_endent, #database, #service, #entry },
223 #define DEFINE_GETBY(database, service, entry, key)   \
224     { fk_getby, #database, #service, #entry, #key },
225 #define DEFINE_GET(database, service, value_and_key)     \
226     { fk_get, #database, #service, NULL, NULL, #value_and_key },
227
228 #include FIXED_FUNCTIONS
229
230 #undef DEFINE_ENT
231 #undef DEFINE_GETBY
232 #undef DEFINE_GET
233
234     { fk_end }
235   };
236
237 \f
238 /* Parsing the config file.  Functions copied from nsswitch.c.  */
239
240 #define __strchrnul strchrnul
241 #define __getline getline
242 #define __strncasecmp strncasecmp
243
244 /* Prototypes for the local functions.  */
245 static name_database *nss_parse_file (const char *fname) internal_function;
246 static name_database_entry *nss_getline (char *line) internal_function;
247 static service_user *nss_parse_service_list (const char *line)
248      internal_function;
249
250 static name_database *
251 internal_function
252 nss_parse_file (const char *fname)
253 {
254   FILE *fp;
255   name_database *result;
256   name_database_entry *last;
257   char *line;
258   size_t len;
259
260   /* Open the configuration file.  */
261   fp = fopen (fname, "rc");
262   if (fp == NULL)
263     return NULL;
264
265   // /* No threads use this stream.  */
266   // __fsetlocking (fp, FSETLOCKING_BYCALLER);
267
268   result = (name_database *) xmalloc (sizeof (name_database));
269
270   result->entry = NULL;
271   result->library = NULL;
272   last = NULL;
273   line = NULL;
274   len = 0;
275   do
276     {
277       name_database_entry *this;
278       ssize_t n;
279
280       n = __getline (&line, &len, fp);
281       if (n < 0)
282         break;
283       if (line[n - 1] == '\n')
284         line[n - 1] = '\0';
285
286       /* Because the file format does not know any form of quoting we
287          can search forward for the next '#' character and if found
288          make it terminating the line.  */
289       *__strchrnul (line, '#') = '\0';
290
291       /* If the line is blank it is ignored.  */
292       if (line[0] == '\0')
293         continue;
294
295       /* Each line completely specifies the actions for a database.  */
296       this = nss_getline (line);
297       if (this != NULL)
298         {
299           if (last != NULL)
300             last->next = this;
301           else
302             result->entry = this;
303
304           last = this;
305         }
306     }
307   while (!feof_unlocked (fp));
308
309   /* Free the buffer.  */
310   free (line);
311   /* Close configuration file.  */
312   fclose (fp);
313
314   return result;
315 }
316
317
318 /* Read the source names:
319         `( <source> ( "[" "!"? (<status> "=" <action> )+ "]" )? )*'
320    */
321 static service_user *
322 internal_function
323 nss_parse_service_list (const char *line)
324 {
325   service_user *result = NULL, **nextp = &result;
326
327   while (1)
328     {
329       service_user *new_service;
330       const char *name;
331
332       while (isspace (line[0]))
333         ++line;
334       if (line[0] == '\0')
335         /* No source specified.  */
336         return result;
337
338       /* Read <source> identifier.  */
339       name = line;
340       while (line[0] != '\0' && !isspace (line[0]) && line[0] != '[')
341         ++line;
342       if (name == line)
343         return result;
344
345
346       new_service = (service_user *) xmalloc (sizeof (*new_service));
347       new_service->name = (char *) xmalloc (line - name + 1);
348
349       *((char *) __mempcpy ((char *) new_service->name, name, line - name))
350         = '\0';
351
352       /* Set default actions.  */
353       new_service->actions[2 + NSS_STATUS_TRYAGAIN] = NSS_ACTION_CONTINUE;
354       new_service->actions[2 + NSS_STATUS_UNAVAIL] = NSS_ACTION_CONTINUE;
355       new_service->actions[2 + NSS_STATUS_NOTFOUND] = NSS_ACTION_CONTINUE;
356       new_service->actions[2 + NSS_STATUS_SUCCESS] = NSS_ACTION_RETURN;
357       new_service->actions[2 + NSS_STATUS_RETURN] = NSS_ACTION_RETURN;
358       new_service->library = NULL;
359       new_service->known.tree = NULL;
360       new_service->next = NULL;
361
362       while (isspace (line[0]))
363         ++line;
364
365       if (line[0] == '[')
366         {
367           /* Read criterions.  */
368           do
369             ++line;
370           while (line[0] != '\0' && isspace (line[0]));
371
372           do
373             {
374               int not;
375               enum nss_status status;
376               lookup_actions action;
377
378               /* Grok ! before name to mean all statii but that one.  */
379               not = line[0] == '!';
380               if (not)
381                 ++line;
382
383               /* Read status name.  */
384               name = line;
385               while (line[0] != '\0' && !isspace (line[0]) && line[0] != '='
386                      && line[0] != ']')
387                 ++line;
388
389               /* Compare with known statii.  */
390               if (line - name == 7)
391                 {
392                   if (__strncasecmp (name, "SUCCESS", 7) == 0)
393                     status = NSS_STATUS_SUCCESS;
394                   else if (__strncasecmp (name, "UNAVAIL", 7) == 0)
395                     status = NSS_STATUS_UNAVAIL;
396                   else
397                     return result;
398                 }
399               else if (line - name == 8)
400                 {
401                   if (__strncasecmp (name, "NOTFOUND", 8) == 0)
402                     status = NSS_STATUS_NOTFOUND;
403                   else if (__strncasecmp (name, "TRYAGAIN", 8) == 0)
404                     status = NSS_STATUS_TRYAGAIN;
405                   else
406                     return result;
407                 }
408               else
409                 return result;
410
411               while (isspace (line[0]))
412                 ++line;
413               if (line[0] != '=')
414                 return result;
415               do
416                 ++line;
417               while (isspace (line[0]));
418
419               name = line;
420               while (line[0] != '\0' && !isspace (line[0]) && line[0] != '='
421                      && line[0] != ']')
422                 ++line;
423
424               if (line - name == 6 && __strncasecmp (name, "RETURN", 6) == 0)
425                 action = NSS_ACTION_RETURN;
426               else if (line - name == 8
427                        && __strncasecmp (name, "CONTINUE", 8) == 0)
428                 action = NSS_ACTION_CONTINUE;
429               else
430                 return result;
431
432               if (not)
433                 {
434                   /* Save the current action setting for this status,
435                      set them all to the given action, and reset this one.  */
436                   const lookup_actions save = new_service->actions[2 + status];
437                   new_service->actions[2 + NSS_STATUS_TRYAGAIN] = action;
438                   new_service->actions[2 + NSS_STATUS_UNAVAIL] = action;
439                   new_service->actions[2 + NSS_STATUS_NOTFOUND] = action;
440                   new_service->actions[2 + NSS_STATUS_SUCCESS] = action;
441                   new_service->actions[2 + status] = save;
442                 }
443               else
444                 new_service->actions[2 + status] = action;
445
446               /* Skip white spaces.  */
447               while (isspace (line[0]))
448                 ++line;
449             }
450           while (line[0] != ']');
451
452           /* Skip the ']'.  */
453           ++line;
454         }
455
456       *nextp = new_service;
457       nextp = &new_service->next;
458     }
459 }
460
461 static name_database_entry *
462 internal_function
463 nss_getline (char *line)
464 {
465   const char *name;
466   name_database_entry *result;
467   size_t len;
468
469   /* Ignore leading white spaces.  ATTENTION: this is different from
470      what is implemented in Solaris.  The Solaris man page says a line
471      beginning with a white space character is ignored.  We regard
472      this as just another misfeature in Solaris.  */
473   while (isspace (line[0]))
474     ++line;
475
476   /* Recognize `<database> ":"'.  */
477   name = line;
478   while (line[0] != '\0' && !isspace (line[0]) && line[0] != ':')
479     ++line;
480   if (line[0] == '\0' || name == line)
481     /* Syntax error.  */
482     return NULL;
483   *line++ = '\0';
484
485   len = strlen (name) + 1;
486
487   result = (name_database_entry *) xmalloc (sizeof (*result));
488   result->name = (char *) xmalloc (len);
489
490   /* Save the database name.  */
491   memcpy ((char *) result->name, name, len);
492
493   /* Parse the list of services.  */
494   result->service = nss_parse_service_list (line);
495
496   result->next = NULL;
497   return result;
498 }
499
500
501 \f
502 /* Generating code for statically initialized nsswitch structures.  */
503
504
505 /* Return the service-neutral suffix of the name of the service
506    library function referred to by the function F.  The result is
507    allocated with malloc.  */
508 char *
509 known_function_suffix (const struct function *f)
510 {
511   switch (f->kind)
512     {
513     case fk_setent:
514       return saprintf ("set%sent", f->entry);
515
516     case fk_getent:
517       return saprintf ("get%sent_r", f->entry);
518
519     case fk_endent:
520       return saprintf ("end%sent", f->entry);
521
522     case fk_getby:
523       return saprintf ("get%sby%s_r", f->entry, f->key);
524
525     case fk_get:
526       return saprintf ("get%s_r", f->value_and_key);
527
528     default:
529       abort ();
530     }
531 }
532
533
534 /* Return the name of the service library function referred to by the
535    function F.  The result is allocated with malloc.  */
536 char *
537 known_function_name (const struct function *f)
538 {
539   return saprintf ("_nss_%s_%s", f->service, known_function_suffix (f));
540 }
541
542
543 /* Write initialized known_function structures to OUT for
544    all the functions we'll use.  */
545 void
546 generate_known_functions (FILE *out)
547 {
548   int i;
549
550   /* First, generate weak references to the functions.  The service
551      libraries depend on libc, and if these references weren't weak,
552      we'd be making libc depend circularly on the service
553      libraries.  */
554   for (i = 0; functions[i].kind; i++)
555     {
556       char *name = known_function_name (&functions[i]);
557       fprintf (out, "typeof (%s) %s __attribute__ ((weak));\n",
558                name, name);
559     }
560   fputs ("\n", out);
561
562   /* Then, a table mapping names to functions.  */
563   fputs ("static const known_function fixed_known_functions[] = {\n",
564          out);
565   for (i = 0; functions[i].kind; i++)
566     {
567       const struct function *f = &functions[i];
568       char *suffix = known_function_suffix (f);
569
570       fprintf (out, "  /* %2d */ { \"%s\", _nss_%s_%s },\n",
571                i, suffix, f->service, suffix);
572     }
573   fputs ("};\n", out);
574   fputs ("\n", out);
575 }
576
577
578 /* Print code to OUT for an initialized array of pointers to the
579    'known_function' structures needed for USER, which is for
580    DATABASE.  Return its name, allocated with malloc.  */
581 char *
582 generate_known_function_list (FILE *out,
583                               const name_database_entry *database,
584                               const service_user *user)
585 {
586   char *list_name = saprintf ("fixed_%s_%s_known_funcs",
587                               database->name, user->name);
588   fprintf (out, "static const known_function *%s[] = {\n",
589            list_name);
590   int i;
591   for (i = 0; functions[i].kind; i++)
592     if (strcmp (functions[i].database, database->name) == 0
593         && strcmp (functions[i].service, user->name) == 0)
594       fprintf (out, "  &fixed_known_functions[%d], /* %s */\n",
595                i, known_function_name (&functions[i]));
596   fputs ("  NULL\n", out);
597   fputs ("};\n", out);
598   fputs ("\n", out);
599
600   return list_name;
601 }
602
603
604 /* Return the name of the status value STATUS, as a statically
605    allocated string.  */
606 const char *
607 lookup_status_name (enum nss_status status)
608 {
609   switch (status)
610     {
611     case NSS_STATUS_TRYAGAIN: return "NSS_STATUS_TRYAGAIN";
612     case NSS_STATUS_UNAVAIL: return "NSS_STATUS_UNAVAIL";
613     case NSS_STATUS_NOTFOUND: return "NSS_STATUS_NOTFOUND";
614     case NSS_STATUS_SUCCESS: return "NSS_STATUS_SUCCESS";
615     case NSS_STATUS_RETURN: return "NSS_STATUS_RETURN";
616     default: abort ();
617     };
618 }
619
620
621 /* Return the name of ACTION as a statically allocated string.  */
622 const char *
623 lookup_action_name (lookup_actions action)
624 {
625   switch (action)
626     {
627     case NSS_ACTION_CONTINUE: return "NSS_ACTION_CONTINUE";
628     case NSS_ACTION_RETURN: return "NSS_ACTION_RETURN";
629     default: abort ();
630     }
631 }
632
633
634 /* Print code to OUT for the list of service_user structures starting
635    with USER, which are all for DATABASE.  Return the name of the 
636    first structure in that list, or zero if USER is NULL.  */
637 char *
638 generate_service_user_list (FILE *out,
639                             name_database_entry *database,
640                             service_user *user)
641 {
642   if (user)
643     {
644       /* Generate the tail of the list.  */
645       char *next_name = generate_service_user_list (out, database, user->next);
646       /* Generate our known function list.  */
647       char *known_function_list_name =
648         generate_known_function_list (out, database, user);
649
650       char *name = saprintf ("fixed_%s_%s_user", database->name, user->name);
651
652       fprintf (out, "static const service_user %s = {\n", name);
653       if (next_name)
654         fprintf (out, "  (service_user *) &%s,\n", next_name);
655       else
656         fprintf (out, "  NULL, /* no next entry */\n");
657       fputs ("  {\n", out);
658       int i;
659       for (i = 0; i < sizeof (user->actions) / sizeof (user->actions[0]); i++)
660         fprintf (out, "    %s, /* %s */\n",
661                  lookup_action_name (user->actions[i]),
662                  lookup_status_name (i - 2));
663       fputs ("  },\n", out);
664       fprintf (out, "  NULL,  /* we never need the service library */\n");
665       fprintf (out, "  { .array = %s },\n", known_function_list_name);
666       fprintf (out, "  \"%s\"\n", user->name);
667       fputs ("};\n", out);
668       fputs ("\n", out);
669       
670       return name;
671     }
672   else
673     return NULL;
674 }
675
676
677 /* Print code to OUT for the list of name_database_entry structures
678    starting with DATABASE.  Return the name of the first structure 
679    in that list, or zero if DATABASE is NULL.  */
680 char *
681 generate_name_database_entries (FILE *out, name_database_entry *database)
682 {
683   if (database)
684     {
685       char *next_name = generate_name_database_entries (out, database->next);
686       char *service_user_name
687         = generate_service_user_list (out, database, database->service);
688       char *name = saprintf ("fixed_%s_name_database", database->name);
689
690       fprintf (out, "static const name_database_entry %s = {\n", name);
691
692       if (next_name)
693         fprintf (out, "  (name_database_entry *) &%s,\n", next_name);
694       else
695         fprintf (out, "  NULL,\n");
696       
697       if (service_user_name)
698         fprintf (out, "  (service_user *) &%s,\n", service_user_name);
699       else
700         fprintf (out, "  NULL,\n");
701
702       fprintf (out, "  \"%s\"\n", database->name);
703       fprintf (out, "};\n");
704       fputs ("\n", out);
705
706       return name;
707     }
708   else
709     return NULL;
710 }
711
712
713 void
714 generate_name_database (FILE *out, name_database *service_table)
715 {
716   /* Produce a linked list of the known name_database_entry
717      structures.  */
718   char *entries = generate_name_database_entries (out, service_table->entry);
719
720   /* Now produce the main structure that points to them all.  */
721   fprintf (out, "static const name_database fixed_name_database = {\n");
722   if (entries)
723     fprintf (out, "  (name_database_entry *) &%s,\n", entries);
724   else
725     fprintf (out, "  NULL,\n");
726   fputs ("  NULL /* we don't need the libraries */\n"
727          "};\n",
728          out);
729 }
730
731
732 \f
733 /* Generating the list of service libraries we generate references to.  */
734
735 /* String with revision number of the shared object files.  */
736 static const char *const nss_shlib_revision = LIBNSS_FILES_SO + 15;
737
738 void
739 generate_service_lib_list (FILE *out, name_database *service_table)
740 {
741   int i, j;
742   int printed_any = 0;
743
744   for (i = 0; functions[i].kind; i++)
745     {
746       /* Mention each service library only once.  */
747       for (j = 0; j < i; j++)
748         if (strcmp (functions[i].service, functions[j].service) == 0)
749           break;
750
751       if (j >= i)
752         {
753           if (printed_any)
754             putc (' ', out);
755           fprintf (out, "-lnss_%s",
756                    functions[i].service,
757                    nss_shlib_revision);
758           printed_any = 1;
759         }
760     }
761 }
762
763 \f
764 /* Main.  */
765
766 int
767 main (int argc, char **argv)
768 {
769   if (argc != 4)
770     {
771       fprintf (stderr, "usage: gen-fixed-nsswitch HEADER SERVLIBS CONFIG\n");
772       exit (1);
773     }
774   
775   name_database *service_table = nss_parse_file (argv[3]);
776   
777   FILE *header = fopen (argv[1], "w");
778   if (! header)
779     {
780       fprintf (stderr,
781                "gen-fixed-nsswitch: couldn't open output file %s: %s\n",
782                argv[1], strerror (errno));
783       exit (1);
784     }
785   fputs ("/* Generated by nss/gen-fixed-nsswitch.c.  */\n", header);
786   fputs ("\n", header);
787   generate_known_functions (header);
788   generate_name_database (header, service_table);
789   fclose (header);
790
791   FILE *service_lib_list = fopen (argv[2], "w");
792   if (! service_lib_list)
793     {
794       fprintf (stderr,
795                "gen-fixed-nsswitch: couldn't open output file %s: %s\n",
796                argv[2], strerror (errno));
797       exit (1);
798     }
799   generate_service_lib_list (service_lib_list, service_table);
800   fclose (service_lib_list);
801
802   return 0;
803 }