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