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