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