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