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