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