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