]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/amd/amd/mapc.c
Virgin import of AMD (am-utils) v6.0
[FreeBSD/FreeBSD.git] / contrib / amd / amd / mapc.c
1 /*
2  * Copyright (c) 1997-1998 Erez Zadok
3  * Copyright (c) 1989 Jan-Simon Pendry
4  * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
5  * Copyright (c) 1989 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry at Imperial College, London.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgment:
21  *      This product includes software developed by the University of
22  *      California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  *      %W% (Berkeley) %G%
40  *
41  * $Id: mapc.c,v 1.1.1.1 1998/11/05 02:04:48 ezk Exp $
42  *
43  */
44
45 /*
46  * Mount map cache
47  */
48
49 #ifdef HAVE_CONFIG_H
50 # include <config.h>
51 #endif /* HAVE_CONFIG_H */
52 #include <am_defs.h>
53 #include <amd.h>
54
55 /*
56  * Make a duplicate reference to an existing map
57  */
58 #define mapc_dup(m) ((m)->refc++, (m))
59
60 /*
61  * Map cache types
62  * default, none, incremental, all, regexp
63  * MAPC_RE implies MAPC_ALL and must be numerically
64  * greater.
65  */
66 #define MAPC_DFLT       0x000
67 #define MAPC_NONE       0x001
68 #define MAPC_INC        0x002
69 #define MAPC_ROOT       0x004
70 #define MAPC_ALL        0x010
71 #define MAPC_CACHE_MASK 0x0ff
72 #define MAPC_SYNC       0x100
73
74 #ifdef HAVE_REGEXEC
75 # define        MAPC_RE         0x020
76 # define        MAPC_ISRE(m)    ((m)->alloc == MAPC_RE)
77 #else /* not HAVE_REGEXEC */
78 # define        MAPC_ISRE(m)    FALSE
79 #endif /* not HAVE_REGEXEC */
80
81 /*
82  * Lookup recursion
83  */
84 #define MREC_FULL       2
85 #define MREC_PART       1
86 #define MREC_NONE       0
87
88 #define MAX_CHAIN       2048
89
90 static struct opt_tab mapc_opt[] =
91 {
92   {"all", MAPC_ALL},
93   {"default", MAPC_DFLT},
94   {"inc", MAPC_INC},
95   {"mapdefault", MAPC_DFLT},
96   {"none", MAPC_NONE},
97 #ifdef HAVE_REGEXEC
98   {"re", MAPC_RE},
99   {"regexp", MAPC_RE},
100 #endif /* HAVE_REGEXEC */
101   {"sync", MAPC_SYNC},
102   {0, 0}
103 };
104
105 /*
106  * Wildcard key
107  */
108 static char wildcard[] = "*";
109
110 /*
111  * Map type
112  */
113 typedef struct map_type map_type;
114 struct map_type {
115   char *name;                   /* Name of this map type */
116   init_fn *init;                /* Initialization */
117   reload_fn *reload;            /* Reload or fill */
118   isup_fn *isup;                /* Is service up or not? (1=up, 0=down) */
119   search_fn *search;            /* Search for new entry */
120   mtime_fn *mtime;              /* Find modify time */
121   int def_alloc;                /* Default allocation mode */
122 };
123
124 /*
125  * Map for root node
126  */
127 static mnt_map *root_map;
128
129 /*
130  * List of known maps
131  */
132 qelem map_list_head = {&map_list_head, &map_list_head};
133
134 /*
135  * Configuration
136  */
137
138 /* forward definitions */
139 static const char *get_full_path(const char *map, const char *path, const char *type);
140 static int mapc_meta_search(mnt_map *, char *, char **, int);
141 static void mapc_sync(mnt_map *);
142
143 /* ROOT MAP */
144 static int root_init(mnt_map *, char *, time_t *);
145
146 /* ERROR MAP */
147 static int error_init(mnt_map *, char *, time_t *);
148 static int error_reload(mnt_map *, char *, add_fn *);
149 static int error_search(mnt_map *, char *, char *, char **, time_t *);
150 static int error_mtime(mnt_map *, char *, time_t *);
151
152 /* PASSWD MAPS */
153 #ifdef HAVE_MAP_PASSWD
154 extern int passwd_init(mnt_map *, char *, time_t *);
155 extern int passwd_search(mnt_map *, char *, char *, char **, time_t *);
156 #endif /* HAVE_MAP_PASSWD */
157
158 /* HESIOD MAPS */
159 #ifdef HAVE_MAP_HESIOD
160 extern int amu_hesiod_init(mnt_map *, char *map, time_t *tp);
161 extern int hesiod_search(mnt_map *, char *, char *, char **, time_t *);
162 #endif /* HAVE_MAP_HESIOD */
163
164 /* LDAP MAPS */
165 #ifdef HAVE_MAP_LDAP
166 extern int amu_ldap_init(mnt_map *, char *map, time_t *tp);
167 extern int amu_ldap_search(mnt_map *, char *, char *, char **, time_t *);
168 extern int amu_ldap_mtime(mnt_map *, char *, time_t *);
169 #endif /* HAVE_MAP_LDAP */
170
171 /* UNION MAPS */
172 #ifdef HAVE_MAP_UNION
173 extern int union_init(mnt_map *, char *, time_t *);
174 extern int union_search(mnt_map *, char *, char *, char **, time_t *);
175 extern int union_reload(mnt_map *, char *, add_fn *);
176 #endif /* HAVE_MAP_UNION */
177
178 /* Network Information Service PLUS (NIS+) */
179 #ifdef HAVE_MAP_NISPLUS
180 extern int nisplus_init(mnt_map *, char *, time_t *);
181 extern int nisplus_reload(mnt_map *, char *, add_fn *);
182 extern int nisplus_search(mnt_map *, char *, char *, char **, time_t *);
183 extern int nisplus_mtime(mnt_map *, char *, time_t *);
184 #endif /* HAVE_MAP_NISPLUS */
185
186 /* Network Information Service (YP, Yellow Pages) */
187 #ifdef HAVE_MAP_NIS
188 extern int nis_init(mnt_map *, char *, time_t *);
189 extern int nis_reload(mnt_map *, char *, add_fn *);
190 extern int nis_isup(mnt_map *, char *);
191 extern int nis_search(mnt_map *, char *, char *, char **, time_t *);
192 extern int nis_mtime(mnt_map *, char *, time_t *);
193 #endif /* HAVE_MAP_NIS */
194
195 /* NDBM MAPS */
196 #ifdef HAVE_MAP_NDBM
197 extern int ndbm_init(mnt_map *, char *, time_t *);
198 extern int ndbm_search(mnt_map *, char *, char *, char **, time_t *);
199 extern int ndbm_mtime(mnt_map *, char *, time_t *);
200 #endif /* HAVE_MAP_NDBM */
201
202 /* FILE MAPS */
203 #ifdef HAVE_MAP_FILE
204 extern int file_init(mnt_map *, char *, time_t *);
205 extern int file_reload(mnt_map *, char *, add_fn *);
206 extern int file_search(mnt_map *, char *, char *, char **, time_t *);
207 extern int file_mtime(mnt_map *, char *, time_t *);
208 #endif /* HAVE_MAP_FILE */
209
210
211 /* note that the choice of MAPC_{INC,ALL} will affect browsable_dirs */
212 static map_type maptypes[] =
213 {
214   {
215     "root",
216     root_init,
217     error_reload,
218     NULL,                       /* isup function */
219     error_search,
220     error_mtime,
221     MAPC_ROOT
222   },
223 #ifdef HAVE_MAP_PASSWD
224   {
225     "passwd",
226     passwd_init,
227     error_reload,
228     NULL,                       /* isup function */
229     passwd_search,
230     error_mtime,
231     MAPC_ALL
232   },
233 #endif /* HAVE_MAP_PASSWD */
234 #ifdef HAVE_MAP_HESIOD
235   {
236     "hesiod",
237     amu_hesiod_init,
238     error_reload,
239     NULL,                       /* isup function */
240     hesiod_search,
241     error_mtime,
242     MAPC_ALL
243   },
244 #endif /* HAVE_MAP_HESIOD */
245 #ifdef HAVE_MAP_LDAP
246   {
247     "ldap",
248     amu_ldap_init,
249     error_reload,
250     NULL,                       /* isup function */
251     amu_ldap_search,
252     amu_ldap_mtime,
253     MAPC_ALL
254   },
255 #endif /* HAVE_MAP_LDAP */
256 #ifdef HAVE_MAP_UNION
257   {
258     "union",
259     union_init,
260     union_reload,
261     NULL,                       /* isup function */
262     union_search,
263     error_mtime,
264     MAPC_ALL
265   },
266 #endif /* HAVE_MAP_UNION */
267 #ifdef HAVE_MAP_NISPLUS
268   {
269     "nisplus",
270     nisplus_init,
271     nisplus_reload,
272     NULL,                       /* isup function */
273     nisplus_search,
274     nisplus_mtime,
275     MAPC_INC
276   },
277 #endif /* HAVE_MAP_NISPLUS */
278 #ifdef HAVE_MAP_NIS
279   {
280     "nis",
281     nis_init,
282     nis_reload,
283     nis_isup,                   /* is NIS up or not? */
284     nis_search,
285     nis_mtime,
286     MAPC_ALL
287   },
288 #endif /* HAVE_MAP_NIS */
289 #ifdef HAVE_MAP_NDBM
290   {
291     "ndbm",
292     ndbm_init,
293     error_reload,
294     NULL,                       /* isup function */
295     ndbm_search,
296     ndbm_mtime,
297     MAPC_ALL
298   },
299 #endif /* HAVE_MAP_NDBM */
300 #ifdef HAVE_MAP_FILE
301   {
302     "file",
303     file_init,
304     file_reload,
305     NULL,                       /* isup function */
306     file_search,
307     file_mtime,
308     MAPC_ALL
309   },
310 #endif /* HAVE_MAP_FILE */
311   {
312     "error",
313     error_init,
314     error_reload,
315     NULL,                       /* isup function */
316     error_search,
317     error_mtime,
318     MAPC_NONE
319   },
320 };
321
322
323 /*
324  * Hash function
325  */
326 static u_int
327 kvhash_of(char *key)
328 {
329   u_int i, j;
330
331   for (i = 0; (j = *key++); i += j) ;
332
333   return i % NKVHASH;
334 }
335
336
337 void
338 mapc_showtypes(char *buf)
339 {
340   map_type *mt;
341   char *sep = "";
342
343   buf[0] = '\0';
344   for (mt = maptypes; mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]); mt++) {
345     strcat(buf, sep);
346     strcat(buf, mt->name);
347     sep = ", ";
348   }
349 }
350
351
352 /*
353  * Check if a map of a certain type exists.
354  * Return 1 (true) if exists, 0 (false) if not.
355  */
356 int
357 mapc_type_exists(const char *type)
358 {
359   map_type *mt;
360
361   if (!type)
362     return 0;
363   for (mt = maptypes;
364        mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
365        mt++) {
366     if (STREQ(type, mt->name))
367       return 1;
368   }
369   return 0;                     /* not found anywhere */
370 }
371
372
373 /*
374  * Add key and val to the map m.
375  * key and val are assumed to be safe copies
376  */
377 void
378 mapc_add_kv(mnt_map *m, char *key, char *val)
379 {
380   kv **h;
381   kv *n;
382   int hash = kvhash_of(key);
383 #ifdef HAVE_REGEXEC
384   regex_t re;
385 #endif /* HAVE_REGEXEC */
386
387 #ifdef DEBUG
388   dlog("add_kv: %s -> %s", key, val);
389 #endif /* DEBUG */
390
391 #ifdef HAVE_REGEXEC
392   if (MAPC_ISRE(m)) {
393     char pattern[MAXPATHLEN];
394     int retval;
395
396     /*
397      * Make sure the string is bound to the start and end
398      */
399     sprintf(pattern, "^%s$", key);
400     retval = regcomp(&re, pattern, REG_ICASE);
401     if (retval != 0) {
402       char errstr[256];
403
404       /* XXX: this code was recently ported, and must be tested -Erez */
405       errstr[0] = '\0';
406       regerror(retval, &re, errstr, 256);
407       plog(XLOG_USER, "error compiling RE \"%s\": %s", pattern, errstr);
408       return;
409     }
410   }
411 #endif /* HAVE_REGEXEC */
412
413   h = &m->kvhash[hash];
414   n = ALLOC(struct kv);
415   n->key = key;
416 #ifdef HAVE_REGEXEC
417   memcpy(&n->re, &re, sizeof(regex_t));
418 #endif /* HAVE_REGEXEC */
419   n->val = val;
420   n->next = *h;
421   *h = n;
422 }
423
424
425 static void
426 mapc_repl_kv(mnt_map *m, char *key, char *val)
427 {
428   kv *k;
429
430   /*
431    * Compute the hash table offset
432    */
433   k = m->kvhash[kvhash_of(key)];
434
435   /*
436    * Scan the linked list for the key
437    */
438   while (k && !FSTREQ(k->key, key))
439     k = k->next;
440
441   if (k) {
442     XFREE(k->val);
443     k->val = val;
444   } else {
445     mapc_add_kv(m, key, val);
446   }
447 }
448
449
450 /*
451  * Search a map for a key.
452  * Calls map specific search routine.
453  * While map is out of date, keep re-syncing.
454  */
455 static int
456 search_map(mnt_map *m, char *key, char **valp)
457 {
458   int rc;
459
460   do {
461     rc = (*m->search) (m, m->map_name, key, valp, &m->modify);
462     if (rc < 0) {
463       plog(XLOG_MAP, "Re-synchronizing cache for map %s", m->map_name);
464       mapc_sync(m);
465     }
466   } while (rc < 0);
467
468   return rc;
469 }
470
471
472 /*
473  * Do a wildcard lookup in the map and
474  * save the result.
475  */
476 static void
477 mapc_find_wildcard(mnt_map *m)
478 {
479   /*
480    * Attempt to find the wildcard entry
481    */
482   int rc = search_map(m, wildcard, &m->wildcard);
483
484   if (rc != 0)
485     m->wildcard = 0;
486 }
487
488
489 /*
490  * Do a map reload
491  */
492 static int
493 mapc_reload_map(mnt_map *m)
494 {
495   int error;
496
497 #ifdef DEBUG
498   dlog("calling map reload on %s", m->map_name);
499 #endif /* DEBUG */
500   error = (*m->reload) (m, m->map_name, mapc_add_kv);
501   if (error)
502     return error;
503   m->wildcard = 0;
504
505 #ifdef DEBUG
506   dlog("calling mapc_search for wildcard");
507 #endif /* DEBUG */
508   error = mapc_search(m, wildcard, &m->wildcard);
509   if (error)
510     m->wildcard = 0;
511
512   return 0;
513 }
514
515
516 /*
517  * Create a new map
518  */
519 static mnt_map *
520 mapc_create(char *map, char *opt, const char *type)
521 {
522   mnt_map *m = ALLOC(struct mnt_map);
523   map_type *mt;
524   time_t modify;
525   int alloc = 0;
526
527   cmdoption(opt, mapc_opt, &alloc);
528
529   /*
530    * If using a configuration file, and the map_type is defined, then look
531    * for it, in the maptypes array.  If found, initialize the map using that
532    * map_type.  If not found, return error.  If no map_type was defined,
533    * default to cycling through all maptypes.
534    */
535   if (use_conf_file && type) {
536     /* find what type of map this one is */
537     for (mt = maptypes;
538          mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
539          mt++) {
540       if (STREQ(type, mt->name)) {
541         plog(XLOG_INFO, "initializing amd conf map %s of type %s", map, type);
542         if ((*mt->init) (m, map, &modify) == 0) {
543           break;
544         } else {
545           plog(XLOG_ERROR, "failed to initialize map %s", map);
546           error_init(m, map, &modify);
547           break;
548         }
549       }
550     } /* end of "for (mt =" loop */
551
552   } else {                      /* cycle through all known maptypes */
553
554     /*
555      * not using amd conf file or using it by w/o specifying map type
556      */
557     for (mt = maptypes;
558          mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
559          mt++) {
560 #ifdef DEBUG
561       dlog("trying to initialize map %s of type %s ...", map, mt->name);
562 #endif /* DEBUG */
563       if ((*mt->init) (m, map, &modify) == 0) {
564         break;
565       }
566     }
567   } /* end of "if (use_conf_file && (colpos = strchr ..." statement */
568
569   /* assert: mt in maptypes */
570
571   m->flags = alloc & ~MAPC_CACHE_MASK;
572   alloc &= MAPC_CACHE_MASK;
573
574   if (alloc == MAPC_DFLT)
575     alloc = mt->def_alloc;
576
577   switch (alloc) {
578   default:
579     plog(XLOG_USER, "Ambiguous map cache type \"%s\"; using \"inc\"", opt);
580     alloc = MAPC_INC;
581     /* fall-through... */
582   case MAPC_NONE:
583   case MAPC_INC:
584   case MAPC_ROOT:
585     break;
586
587   case MAPC_ALL:
588     /*
589      * If there is no support for reload and it was requested
590      * then back off to incremental instead.
591      */
592     if (mt->reload == error_reload) {
593       plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"all\"; using \"inc\"", mt->name);
594       alloc = MAPC_INC;
595     }
596     break;
597
598 #ifdef HAVE_REGEXEC
599   case MAPC_RE:
600     if (mt->reload == error_reload) {
601       plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"re\"", mt->name);
602       mt = &maptypes[sizeof(maptypes) / sizeof(maptypes[0]) - 1];
603       /* assert: mt->name == "error" */
604     }
605     break;
606 #endif /* HAVE_REGEXEC */
607   }
608
609 #ifdef DEBUG
610   dlog("Map for %s coming from maptype %s", map, mt->name);
611 #endif /* DEBUG */
612
613   m->alloc = alloc;
614   m->reload = mt->reload;
615   m->isup = mt->isup;
616   m->modify = modify;
617   m->search = alloc >= MAPC_ALL ? error_search : mt->search;
618   m->mtime = mt->mtime;
619   memset((voidp) m->kvhash, 0, sizeof(m->kvhash));
620   m->map_name = strdup(map);
621   m->refc = 1;
622   m->wildcard = 0;
623
624   /*
625    * synchronize cache with reality
626    */
627   mapc_sync(m);
628
629   return m;
630 }
631
632
633 /*
634  * Free the cached data in a map
635  */
636 static void
637 mapc_clear(mnt_map *m)
638 {
639   int i;
640
641   /*
642    * For each of the hash slots, chain
643    * along free'ing the data.
644    */
645   for (i = 0; i < NKVHASH; i++) {
646     kv *k = m->kvhash[i];
647     while (k) {
648       kv *n = k->next;
649       XFREE(k->key);
650       if (k->val)
651         XFREE(k->val);
652       XFREE(k);
653       k = n;
654     }
655   }
656
657   /*
658    * Zero the hash slots
659    */
660   memset((voidp) m->kvhash, 0, sizeof(m->kvhash));
661
662   /*
663    * Free the wildcard if it exists
664    */
665   if (m->wildcard) {
666     XFREE(m->wildcard);
667     m->wildcard = 0;
668   }
669 }
670
671
672 /*
673  * Find a map, or create one if it does not exist
674  */
675 mnt_map *
676 mapc_find(char *map, char *opt, const char *maptype)
677 {
678   mnt_map *m;
679
680   /*
681    * Search the list of known maps to see if
682    * it has already been loaded.  If it is found
683    * then return a duplicate reference to it.
684    * Otherwise make a new map as required and
685    * add it to the list of maps
686    */
687   ITER(m, mnt_map, &map_list_head)
688   if (STREQ(m->map_name, map))
689       return mapc_dup(m);
690   m = mapc_create(map, opt, maptype);
691   ins_que(&m->hdr, &map_list_head);
692
693   return m;
694 }
695
696
697 /*
698  * Free a map.
699  */
700 void
701 mapc_free(voidp v)
702 {
703   mnt_map *m = v;
704
705   /*
706    * Decrement the reference count.
707    * If the reference count hits zero
708    * then throw the map away.
709    */
710   if (m && --m->refc == 0) {
711     mapc_clear(m);
712     XFREE(m->map_name);
713     rem_que(&m->hdr);
714     XFREE(m);
715   }
716 }
717
718
719 /*
720  * Search the map for the key.  Put a safe (malloc'ed) copy in *pval or
721  * return an error code
722  */
723 static int
724 mapc_meta_search(mnt_map *m, char *key, char **pval, int recurse)
725 {
726   int error = 0;
727   kv *k = 0;
728
729   /*
730    * Firewall
731    */
732   if (!m) {
733     plog(XLOG_ERROR, "Null map request for %s", key);
734     return ENOENT;
735   }
736   if (m->flags & MAPC_SYNC) {
737     /*
738      * Get modify time...
739      */
740     time_t t;
741     error = (*m->mtime) (m, m->map_name, &t);
742     if (error || t > m->modify) {
743       m->modify = t;
744       plog(XLOG_INFO, "Map %s is out of date", m->map_name);
745       mapc_sync(m);
746     }
747   }
748
749   if (!MAPC_ISRE(m)) {
750     /*
751      * Compute the hash table offset
752      */
753     k = m->kvhash[kvhash_of(key)];
754
755     /*
756      * Scan the linked list for the key
757      */
758     while (k && !FSTREQ(k->key, key))
759       k = k->next;
760
761   }
762
763 #ifdef HAVE_REGEXEC
764   else if (recurse == MREC_FULL) {
765     /*
766      * Try for an RE match against the entire map.
767      * Note that this will be done in a "random"
768      * order.
769      */
770     int i;
771
772     for (i = 0; i < NKVHASH; i++) {
773       k = m->kvhash[i];
774       while (k) {
775         int retval;
776
777         /* XXX: this code was recently ported, and must be tested -Erez */
778         retval = regexec(&k->re, key, 0, 0, 0);
779         if (retval == 0) {      /* succeeded */
780           break;
781         } else {                /* failed to match, log error */
782           char errstr[256];
783
784           errstr[0] = '\0';
785           regerror(retval, &k->re, errstr, 256);
786           plog(XLOG_USER, "error matching RE \"%s\" against \"%s\": %s",
787                key, k->key, errstr);
788         }
789         k = k->next;
790       }
791       if (k)
792         break;
793     }
794   }
795 #endif /* HAVE_REGEXEC */
796
797   /*
798    * If found then take a copy
799    */
800   if (k) {
801     if (k->val)
802       *pval = strdup(k->val);
803     else
804       error = ENOENT;
805   } else if (m->alloc >= MAPC_ALL) {
806     /*
807      * If the entire map is cached then this
808      * key does not exist.
809      */
810     error = ENOENT;
811   } else {
812     /*
813      * Otherwise search the map.  If we are
814      * in incremental mode then add the key
815      * to the cache.
816      */
817     error = search_map(m, key, pval);
818     if (!error && m->alloc == MAPC_INC)
819       mapc_add_kv(m, strdup(key), strdup(*pval));
820   }
821
822   /*
823    * If an error, and a wildcard exists,
824    * and the key is not internal then
825    * return a copy of the wildcard.
826    */
827   if (error > 0) {
828     if (recurse == MREC_FULL && !MAPC_ISRE(m)) {
829       char wildname[MAXPATHLEN];
830       char *subp;
831       if (*key == '/')
832         return error;
833       /*
834        * Keep chopping sub-directories from the RHS
835        * and replacing with "/ *" and repeat the lookup.
836        * For example:
837        * "src/gnu/gcc" -> "src / gnu / *" -> "src / *"
838        */
839       strcpy(wildname, key);
840       while (error && (subp = strrchr(wildname, '/'))) {
841         strcpy(subp, "/*");
842 #ifdef DEBUG
843         dlog("mapc recurses on %s", wildname);
844 #endif /* DEBUG */
845         error = mapc_meta_search(m, wildname, pval, MREC_PART);
846         if (error)
847           *subp = 0;
848       }
849
850       if (error > 0 && m->wildcard) {
851         *pval = strdup(m->wildcard);
852         error = 0;
853       }
854     }
855   }
856   return error;
857 }
858
859
860 int
861 mapc_search(mnt_map *m, char *key, char **pval)
862 {
863   return mapc_meta_search(m, key, pval, MREC_FULL);
864 }
865
866
867 /*
868  * Get map cache in sync with physical representation
869  */
870 static void
871 mapc_sync(mnt_map *m)
872 {
873   if (m->alloc != MAPC_ROOT) {
874
875     /* do not clear map if map service is down */
876     if (m->isup) {
877       if (!((*m->isup)(m, m->map_name))) {
878         plog(XLOG_ERROR, "mapc_sync: map %s is down: not clearing map", m->map_name);
879         return;
880       }
881     }
882
883     mapc_clear(m);
884
885     if (m->alloc >= MAPC_ALL)
886       if (mapc_reload_map(m))
887         m->alloc = MAPC_INC;
888     /*
889      * Attempt to find the wildcard entry
890      */
891     if (m->alloc < MAPC_ALL)
892       mapc_find_wildcard(m);
893   }
894 }
895
896
897 /*
898  * Reload all the maps
899  * Called when Amd gets hit by a SIGHUP.
900  */
901 void
902 mapc_reload(void)
903 {
904   mnt_map *m;
905
906   /*
907    * For all the maps,
908    * Throw away the existing information.
909    * Do a reload
910    * Find the wildcard
911    */
912   ITER(m, mnt_map, &map_list_head)
913     mapc_sync(m);
914 }
915
916
917 /*
918  * Root map.
919  * The root map is used to bootstrap amd.
920  * All the require top-level mounts are added
921  * into the root map and then the map is iterated
922  * and a lookup is done on all the mount points.
923  * This causes the top level mounts to be automounted.
924  */
925 static int
926 root_init(mnt_map *m, char *map, time_t *tp)
927 {
928   *tp = clocktime();
929   return STREQ(map, ROOT_MAP) ? 0 : ENOENT;
930 }
931
932
933 /*
934  * Add a new entry to the root map
935  *
936  * dir - directory (key)
937  * opts - mount options
938  * map - map name
939  * cfm - optional amd configuration file map section structure
940  */
941 void
942 root_newmap(const char *dir, const char *opts, const char *map, const cf_map_t *cfm)
943 {
944   char str[MAXPATHLEN];
945
946   /*
947    * First make sure we have a root map to talk about...
948    */
949   if (!root_map)
950     root_map = mapc_find(ROOT_MAP, "mapdefault", NULL);
951
952   /*
953    * Then add the entry...
954    */
955
956   /*
957    * Here I plug in the code to process other amd.conf options like
958    * map_type, search_path, and flags (browsable_dirs, mount_type).
959    */
960
961   if (cfm) {
962     if (map) {
963       sprintf(str, "cache:=mapdefault;type:=%s;fs:=\"%s\"",
964               cfm->cfm_flags & CFM_MOUNT_TYPE_AUTOFS ? "autofs" : "toplvl",
965               get_full_path(map, cfm->cfm_search_path, cfm->cfm_type));
966       if (opts && opts[0] != '\0') {
967         strcat(str, ";");
968         strcat(str, opts);
969       }
970       if (cfm->cfm_flags & CFM_BROWSABLE_DIRS_FULL)
971         strcat(str, ";opts:=rw,fullybrowsable");
972       if (cfm->cfm_flags & CFM_BROWSABLE_DIRS)
973         strcat(str, ";opts:=rw,browsable");
974       if (cfm->cfm_type) {
975         strcat(str, ";maptype:=");
976         strcat(str, cfm->cfm_type);
977       }
978     } else {
979       strcpy(str, opts);
980     }
981   } else {
982     if (map)
983       sprintf(str, "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s",
984               map, opts ? opts : "");
985     else
986       strcpy(str, opts);
987   }
988   mapc_repl_kv(root_map, strdup((char *)dir), strdup(str));
989 }
990
991
992 int
993 mapc_keyiter(mnt_map *m, void (*fn) (char *, voidp), voidp arg)
994 {
995   int i;
996   int c = 0;
997
998   for (i = 0; i < NKVHASH; i++) {
999     kv *k = m->kvhash[i];
1000     while (k) {
1001       (*fn) (k->key, arg);
1002       k = k->next;
1003       c++;
1004     }
1005   }
1006
1007   return c;
1008 }
1009
1010
1011 /*
1012  * Iterate on the root map and call (*fn)() on the key of all the nodes.
1013  * Finally throw away the root map.
1014  */
1015 int
1016 root_keyiter(void (*fn)(char *, voidp), voidp arg)
1017 {
1018   if (root_map) {
1019     int c = mapc_keyiter(root_map, fn, arg);
1020     return c;
1021   }
1022
1023   return 0;
1024 }
1025
1026
1027 /*
1028  * Was: NEW_TOPLVL_READDIR
1029  * Search a chain for an entry with some name.
1030  * -Erez Zadok <ezk@cs.columbia.edu>
1031  */
1032 static int
1033 key_already_in_chain(char *keyname, const nfsentry *chain)
1034 {
1035   const nfsentry *tmpchain = chain;
1036
1037   while (tmpchain) {
1038     if (keyname && tmpchain->ne_name && STREQ(keyname, tmpchain->ne_name))
1039         return 1;
1040     tmpchain = tmpchain->ne_nextentry;
1041   }
1042
1043   return 0;
1044 }
1045
1046
1047 /*
1048  * Create a chain of entries which are not linked.
1049  * -Erez Zadok <ezk@cs.columbia.edu>
1050  */
1051 nfsentry *
1052 make_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable)
1053 {
1054   static u_int last_cookie = ~(u_int) 0 - 1;
1055   static nfsentry chain[MAX_CHAIN];
1056   static int max_entries = MAX_CHAIN;
1057   char *key;
1058   int num_entries = 0, preflen = 0, i;
1059   nfsentry *retval = (nfsentry *) NULL;
1060   mntfs *mf;
1061   mnt_map *mmp;
1062
1063   if (!mp) {
1064     plog(XLOG_DEBUG, "make_entry_chain: mp is (NULL)");
1065     return retval;
1066   }
1067   mf = mp->am_mnt;
1068   if (!mf) {
1069     plog(XLOG_DEBUG, "make_entry_chain: mp->am_mnt is (NULL)");
1070     return retval;
1071   }
1072   mmp = (mnt_map *) mf->mf_private;
1073   if (!mmp) {
1074     plog(XLOG_DEBUG, "make_entry_chain: mp->am_mnt->mf_private is (NULL)");
1075     return retval;
1076   }
1077
1078   /* iterate over keys */
1079   for (i = 0; i < NKVHASH; i++) {
1080     kv *k;
1081     for (k = mmp->kvhash[i]; k ; k = k->next) {
1082
1083       /*
1084        * Skip unwanted entries which are either not real entries or
1085        * very difficult to interpret (wildcards...)  This test needs
1086        * lots of improvement.  Any takers?
1087        */
1088       key = k->key;
1089       if (!key)
1090         continue;
1091
1092       /* Skip '*' */
1093       if (!fully_browsable && strchr(key, '*'))
1094         continue;
1095
1096       /*
1097        * If the map has a prefix-string then check if the key starts with
1098        * this * string, and if it does, skip over this prefix.
1099        */
1100       if (preflen) {
1101         if (!NSTREQ(key, mp->am_pref, preflen))
1102           continue;
1103         key += preflen;
1104       }
1105
1106       /* no more '/' are allowed, unless browsable_dirs=full was used */
1107       if (!fully_browsable && strchr(key, '/'))
1108         continue;
1109
1110       /* no duplicates allowed */
1111       if (key_already_in_chain(key, current_chain))
1112         continue;
1113
1114       /* fill in a cell and link the entry */
1115       if (num_entries >= max_entries) {
1116         /* out of space */
1117         plog(XLOG_DEBUG, "make_entry_chain: no more space in chain");
1118         if (num_entries > 0) {
1119           chain[num_entries - 1].ne_nextentry = 0;
1120           retval = &chain[0];
1121         }
1122         return retval;
1123       }
1124
1125       /* we have space.  put entry in next cell */
1126       --last_cookie;
1127       chain[num_entries].ne_fileid = (u_int) last_cookie;
1128       *(u_int *) chain[num_entries].ne_cookie =
1129         (u_int) last_cookie;
1130       chain[num_entries].ne_name = key;
1131       if (num_entries < max_entries - 1) {      /* link to next one */
1132         chain[num_entries].ne_nextentry = &chain[num_entries + 1];
1133       }
1134       ++num_entries;
1135     } /* end of "while (k)" */
1136   } /* end of "for (i ... NKVHASH ..." */
1137
1138   /* terminate chain */
1139   if (num_entries > 0) {
1140     chain[num_entries - 1].ne_nextentry = 0;
1141     retval = &chain[0];
1142   }
1143
1144   return retval;
1145 }
1146
1147
1148 /*
1149  * Error map
1150  */
1151 static int
1152 error_init(mnt_map *m, char *map, time_t *tp)
1153 {
1154   plog(XLOG_USER, "No source data for map %s", map);
1155   *tp = 0;
1156
1157   return 0;
1158 }
1159
1160
1161 static int
1162 error_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
1163 {
1164   return ENOENT;
1165 }
1166
1167
1168 static int
1169 error_reload(mnt_map *m, char *map, add_fn *fn)
1170 {
1171   return ENOENT;
1172 }
1173
1174
1175 static int
1176 error_mtime(mnt_map *m, char *map, time_t *tp)
1177 {
1178   *tp = 0;
1179
1180   return 0;
1181 }
1182
1183
1184 /*
1185  * Return absolute path of map, searched in a type-specific path.
1186  * Note: uses a static buffer for returned data.
1187  */
1188 static const char *
1189 get_full_path(const char *map, const char *path, const char *type)
1190 {
1191   char component[MAXPATHLEN], *str;
1192   static char full_path[MAXPATHLEN];
1193   int len;
1194
1195   /* for now, only file-type search paths are implemented */
1196   if (type && !STREQ(type, "file"))
1197     return map;
1198
1199   /* if null map, return it */
1200   if (!map)
1201     return map;
1202
1203   /* if map includes a '/', return it (absolute or relative path) */
1204   if (strchr(map, '/'))
1205     return map;
1206
1207   /* if path is empty, return map */
1208   if (!path)
1209     return map;
1210
1211   /* now break path into components, and search in each */
1212   strcpy(component, path);
1213
1214   str = strtok(component, ":");
1215   do {
1216     strcpy(full_path, str);
1217     len = strlen(full_path);
1218     if (full_path[len - 1] != '/') /* add trailing "/" if needed */
1219       strcat(full_path, "/");
1220     strcat(full_path, map);
1221     if (access(full_path, R_OK) == 0)
1222       return full_path;
1223     str = strtok(NULL, ":");
1224   } while (str);
1225
1226   return map;                   /* if found nothing, return map */
1227 }