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