xref: /original-bsd/usr.sbin/amd/amd/mapc.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1989 Jan-Simon Pendry
3  * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
4  * Copyright (c) 1989, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Jan-Simon Pendry at Imperial College, London.
9  *
10  * %sccs.include.redist.c%
11  *
12  * $Id: mapc.c,v 5.2.2.1 1992/02/09 15:08:38 jsp beta $
13  */
14 
15 #ifndef lint
16 static char sccsid[] = "@(#)mapc.c	8.1 (Berkeley) 06/06/93";
17 #endif /* not lint */
18 
19 /*
20  * Mount map cache
21  */
22 
23 #include "am.h"
24 #ifdef HAS_REGEXP
25 #include RE_HDR
26 #endif
27 
28 /*
29  * Hash table size
30  */
31 #define	NKVHASH	(1 << 2)		/* Power of two */
32 
33 /*
34  * Wildcard key
35  */
36 static char wildcard[] = "*";
37 
38 /*
39  * Map cache types
40  * default, none, incremental, all, regexp
41  * MAPC_RE implies MAPC_ALL and must be numerically
42  * greater.
43  */
44 #define	MAPC_DFLT	0x000
45 #define	MAPC_NONE	0x001
46 #define	MAPC_INC	0x002
47 #define	MAPC_ROOT	0x004
48 #define	MAPC_ALL	0x010
49 #ifdef HAS_REGEXP
50 #define MAPC_RE		0x020
51 #define	MAPC_ISRE(m) ((m)->alloc == MAPC_RE)
52 #else
53 #define MAPC_ISRE(m) FALSE
54 #endif
55 #define	MAPC_CACHE_MASK	0x0ff
56 #define	MAPC_SYNC	0x100
57 
58 static struct opt_tab mapc_opt[] = {
59 	{ "all", MAPC_ALL },
60 	{ "default", MAPC_DFLT },
61 	{ "inc", MAPC_INC },
62 	{ "mapdefault", MAPC_DFLT },
63 	{ "none", MAPC_NONE },
64 #ifdef HAS_REGEXP
65 	{ "re", MAPC_RE },
66 	{ "regexp", MAPC_RE },
67 #endif
68 	{ "sync", MAPC_SYNC },
69 	{ 0, 0 }
70 };
71 
72 /*
73  * Lookup recursion
74  */
75 #define	MREC_FULL	2
76 #define	MREC_PART	1
77 #define	MREC_NONE	0
78 
79 /*
80  * Cache map operations
81  */
82 typedef void add_fn P((mnt_map*, char*, char*));
83 typedef int init_fn P((char*, time_t*));
84 typedef int search_fn P((mnt_map*, char*, char*, char**, time_t*));
85 typedef int reload_fn P((mnt_map*, char*, add_fn*));
86 typedef int mtime_fn P((char*, time_t*));
87 
88 static void mapc_sync P((mnt_map*));
89 
90 /*
91  * Map type
92  */
93 typedef struct map_type map_type;
94 struct map_type {
95 	char *name;			/* Name of this map type */
96 	init_fn *init;			/* Initialisation */
97 	reload_fn *reload;		/* Reload or fill */
98 	search_fn *search;		/* Search for new entry */
99 	mtime_fn *mtime;		/* Find modify time */
100 	int def_alloc;			/* Default allocation mode */
101 };
102 
103 /*
104  * Key-value pair
105  */
106 typedef struct kv kv;
107 struct kv {
108 	kv *next;
109 	char *key;
110 	char *val;
111 };
112 
113 struct mnt_map {
114 	qelem hdr;
115 	int refc;			/* Reference count */
116 	short flags;			/* Allocation flags */
117 	short alloc;			/* Allocation mode */
118 	time_t modify;			/* Modify time of map */
119 	char *map_name;			/* Name of this map */
120 	char *wildcard;			/* Wildcard value */
121 	reload_fn *reload;		/* Function to be used for reloads */
122 	search_fn *search;		/* Function to be used for searching */
123 	mtime_fn *mtime;		/* Modify time function */
124 	kv *kvhash[NKVHASH];		/* Cached data */
125 };
126 
127 /*
128  * Map for root node
129  */
130 static mnt_map *root_map;
131 
132 /*
133  * List of known maps
134  */
135 extern qelem map_list_head;
136 qelem map_list_head = { &map_list_head, &map_list_head };
137 
138 /*
139  * Configuration
140  */
141 
142 /* ROOT MAP */
143 static int root_init P((char*, time_t*));
144 
145 /* FILE MAPS */
146 #ifdef HAS_FILE_MAPS
147 extern int file_init P((char*, time_t*));
148 extern int file_reload P((mnt_map*, char*, add_fn*));
149 extern int file_search P((mnt_map*, char*, char*, char**, time_t*));
150 extern int file_mtime P((char*, time_t*));
151 #endif /* HAS_FILE_MAPS */
152 
153 /* Network Information Service (NIS) MAPS */
154 #ifdef HAS_NIS_MAPS
155 extern int nis_init P((char*, time_t*));
156 #ifdef HAS_NIS_RELOAD
157 extern int nis_reload P((mnt_map*, char*, add_fn*));
158 #else
159 #define nis_reload error_reload
160 #endif
161 extern int nis_search P((mnt_map*, char*, char*, char**, time_t*));
162 #define nis_mtime nis_init
163 #endif /* HAS_NIS_MAPS */
164 
165 /* NDBM MAPS */
166 #ifdef HAS_NDBM_MAPS
167 #ifdef OS_HAS_NDBM
168 extern int ndbm_init P((char*, time_t*));
169 extern int ndbm_search P((mnt_map*, char*, char*, char**, time_t*));
170 #define ndbm_mtime ndbm_init
171 #endif /* OS_HAS_NDBM */
172 #endif /* HAS_NDBM_MAPS */
173 
174 /* PASSWD MAPS */
175 #ifdef HAS_PASSWD_MAPS
176 extern int passwd_init P((char*, time_t*));
177 extern int passwd_search P((mnt_map*, char*, char*, char**, time_t*));
178 #endif /* HAS_PASSWD_MAPS */
179 
180 /* HESIOD MAPS */
181 #ifdef HAS_HESIOD_MAPS
182 extern int hesiod_init P((char*, time_t*));
183 #ifdef HAS_HESIOD_RELOAD
184 extern int hesiod_reload P((mnt_map*, char*, add_fn*));
185 #else
186 #define hesiod_reload error_reload
187 #endif
188 extern int hesiod_search P((mnt_map*, char*, char*, char**, time_t*));
189 #endif /* HAS_HESIOD_MAPS */
190 
191 /* UNION MAPS */
192 #ifdef HAS_UNION_MAPS
193 extern int union_init P((char*, time_t*));
194 extern int union_search P((mnt_map*, char*, char*, char**, time_t*));
195 extern int union_reload P((mnt_map*, char*, add_fn*));
196 #endif /* HAS_UNION_MAPS */
197 
198 /* ERROR MAP */
199 static int error_init P((char*, time_t*));
200 static int error_reload P((mnt_map*, char*, add_fn*));
201 static int error_search P((mnt_map*, char*, char*, char**, time_t*));
202 static int error_mtime P((char*, time_t*));
203 
204 static map_type maptypes[] = {
205 	{ "root", root_init, error_reload, error_search, error_mtime, MAPC_ROOT },
206 
207 #ifdef HAS_PASSWD_MAPS
208 	{ "passwd", passwd_init, error_reload, passwd_search, error_mtime, MAPC_INC },
209 #endif
210 
211 #ifdef HAS_HESIOD_MAPS
212 	{ "hesiod", hesiod_init, hesiod_reload, hesiod_search, error_mtime, MAPC_ALL },
213 #endif
214 
215 #ifdef HAS_UNION_MAPS
216 	{ "union", union_init, union_reload, union_search, error_mtime, MAPC_ALL },
217 #endif
218 
219 #ifdef HAS_NIS_MAPS
220 	{ "nis", nis_init, nis_reload, nis_search, nis_mtime, MAPC_INC },
221 #endif
222 
223 #ifdef HAS_NDBM_MAPS
224 	{ "ndbm", ndbm_init, error_reload, ndbm_search, ndbm_mtime, MAPC_INC },
225 #endif
226 
227 #ifdef HAS_FILE_MAPS
228 	{ "file", file_init, file_reload, file_search, file_mtime, MAPC_ALL },
229 #endif
230 
231 	{ "error", error_init, error_reload, error_search, error_mtime, MAPC_NONE },
232 };
233 
234 /*
235  * Hash function
236  */
237 static unsigned int kvhash_of P((char *key));
238 static unsigned int kvhash_of(key)
239 char *key;
240 {
241 	unsigned int i, j;
242 
243 	for (i = 0; j = *key++; i += j)
244 		;
245 
246 	return i % NKVHASH;
247 }
248 
249 void mapc_showtypes P((FILE *fp));
250 void mapc_showtypes(fp)
251 FILE *fp;
252 {
253 	map_type *mt;
254 	char *sep = "";
255 	for (mt = maptypes; mt < maptypes+sizeof(maptypes)/sizeof(maptypes[0]); mt++) {
256 		fprintf(fp, "%s%s", sep, mt->name);
257 		sep = ", ";
258 	}
259 }
260 
261 static Const char *reg_error = "?";
262 void regerror P((Const char *m));
263 void regerror(m)
264 Const char *m;
265 {
266 	reg_error = m;
267 }
268 
269 /*
270  * Add key and val to the map m.
271  * key and val are assumed to be safe copies
272  */
273 void mapc_add_kv P((mnt_map *m, char *key, char *val));
274 void mapc_add_kv(m, key, val)
275 mnt_map *m;
276 char *key;
277 char *val;
278 {
279 	kv **h;
280 	kv *n;
281 	int hash = kvhash_of(key);
282 
283 #ifdef DEBUG
284 	dlog("add_kv: %s -> %s", key, val);
285 #endif
286 
287 #ifdef HAS_REGEXP
288 	if (MAPC_ISRE(m)) {
289 		char keyb[MAXPATHLEN];
290 		regexp *re;
291 		/*
292 		 * Make sure the string is bound to the start and end
293 		 */
294 		sprintf(keyb, "^%s$", key);
295 		re = regcomp(keyb);
296 		if (re == 0) {
297 			plog(XLOG_USER, "error compiling RE \"%s\": %s", keyb, reg_error);
298 			return;
299 		} else {
300 			free(key);
301 			key = (char *) re;
302 		}
303 	}
304 #endif
305 
306 	h = &m->kvhash[hash];
307 	n = ALLOC(kv);
308 	n->key = key;
309 	n->val = val;
310 	n->next = *h;
311 	*h = n;
312 }
313 
314 void mapc_repl_kv P((mnt_map *m, char *key, char *val));
315 void mapc_repl_kv(m, key, val)
316 mnt_map *m;
317 char *key;
318 char *val;
319 {
320 	kv *k;
321 
322 	/*
323 	 * Compute the hash table offset
324 	 */
325 	k = m->kvhash[kvhash_of(key)];
326 
327 	/*
328 	 * Scan the linked list for the key
329 	 */
330 	while (k && !FSTREQ(k->key, key))
331 		k = k->next;
332 
333 	if (k) {
334 		free(k->val);
335 		k->val = val;
336 	} else {
337 		mapc_add_kv(m, key, val);
338 	}
339 
340 }
341 
342 /*
343  * Search a map for a key.
344  * Calls map specific search routine.
345  * While map is out of date, keep re-syncing.
346  */
347 static int search_map P((mnt_map *m, char *key, char **valp));
348 static int search_map(m, key, valp)
349 mnt_map *m;
350 char *key;
351 char **valp;
352 {
353 	int rc;
354 	do {
355 		rc = (*m->search)(m, m->map_name, key, valp, &m->modify);
356 		if (rc < 0) {
357 			plog(XLOG_MAP, "Re-synchronizing cache for map %s", m->map_name);
358 			mapc_sync(m);
359 		}
360 	} while (rc < 0);
361 
362 	return rc;
363 }
364 
365 /*
366  * Do a wildcard lookup in the map and
367  * save the result.
368  */
369 static void mapc_find_wildcard P((mnt_map *m));
370 static void mapc_find_wildcard(m)
371 mnt_map *m;
372 {
373 	/*
374 	 * Attempt to find the wildcard entry
375 	 */
376 	int rc = search_map(m, wildcard, &m->wildcard);
377 
378 	if (rc != 0)
379 		m->wildcard = 0;
380 }
381 
382 /*
383  * Make a duplicate reference to an existing map
384  */
385 #define mapc_dup(m) ((m)->refc++, (m))
386 
387 /*
388  * Do a map reload
389  */
390 static int mapc_reload_map(m)
391 mnt_map *m;
392 {
393 	int error;
394 #ifdef DEBUG
395 	dlog("calling map reload on %s", m->map_name);
396 #endif
397 	error = (*m->reload)(m, m->map_name, mapc_add_kv);
398 	if (error)
399 		return error;
400 	m->wildcard = 0;
401 #ifdef DEBUG
402 	dlog("calling mapc_search for wildcard");
403 #endif
404 	error = mapc_search(m, wildcard, &m->wildcard);
405 	if (error)
406 		m->wildcard = 0;
407 	return 0;
408 }
409 
410 /*
411  * Create a new map
412  */
413 static mnt_map *mapc_create P((char *map, char *opt));
414 static mnt_map *mapc_create(map, opt)
415 char *map;
416 char *opt;
417 {
418 	mnt_map *m = ALLOC(mnt_map);
419 	map_type *mt;
420 	time_t modify;
421 	int alloc = 0;
422 
423 	(void) cmdoption(opt, mapc_opt, &alloc);
424 
425 	for (mt = maptypes; mt < maptypes+sizeof(maptypes)/sizeof(maptypes[0]); mt++)
426 		if ((*mt->init)(map, &modify) == 0)
427 			break;
428 	/* assert: mt in maptypes */
429 
430 	m->flags = alloc & ~MAPC_CACHE_MASK;
431 	alloc &= MAPC_CACHE_MASK;
432 
433 	if (alloc == MAPC_DFLT)
434 		alloc = mt->def_alloc;
435 	switch (alloc) {
436 	default:
437 		plog(XLOG_USER, "Ambiguous map cache type \"%s\"; using \"inc\"", opt);
438 		alloc = MAPC_INC;
439 		/* fallthrough... */
440 	case MAPC_NONE:
441 	case MAPC_INC:
442 	case MAPC_ROOT:
443 		break;
444 	case MAPC_ALL:
445 		/*
446 		 * If there is no support for reload and it was requested
447 		 * then back off to incremental instead.
448 		 */
449 		if (mt->reload == error_reload) {
450 			plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"all\"; using \"inc\"", mt->name);
451 			alloc = MAPC_INC;
452 		}
453 		break;
454 #ifdef HAS_REGEXP
455 	case MAPC_RE:
456 		if (mt->reload == error_reload) {
457 			plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"re\"", mt->name);
458 			mt = &maptypes[sizeof(maptypes)/sizeof(maptypes[0]) - 1];
459 			/* assert: mt->name == "error" */
460 		}
461 		break;
462 #endif
463 	}
464 
465 #ifdef DEBUG
466 	dlog("Map for %s coming from maptype %s", map, mt->name);
467 #endif
468 
469 	m->alloc = alloc;
470 	m->reload = mt->reload;
471 	m->modify = modify;
472 	m->search = alloc >= MAPC_ALL ? error_search : mt->search;
473 	m->mtime = mt->mtime;
474 	bzero((voidp) m->kvhash, sizeof(m->kvhash));
475 	m->map_name = strdup(map);
476 	m->refc = 1;
477 	m->wildcard = 0;
478 
479 	/*
480 	 * synchronize cache with reality
481 	 */
482 	mapc_sync(m);
483 
484 	return m;
485 }
486 
487 /*
488  * Free the cached data in a map
489  */
490 static void mapc_clear P((mnt_map *m));
491 static void mapc_clear(m)
492 mnt_map *m;
493 {
494 	int i;
495 
496 	/*
497 	 * For each of the hash slots, chain
498 	 * along free'ing the data.
499 	 */
500 	for (i = 0; i < NKVHASH; i++) {
501 		kv *k = m->kvhash[i];
502 		while (k) {
503 			kv *n = k->next;
504 			free((voidp) k->key);
505 			if (k->val)
506 				free((voidp) k->val);
507 			free((voidp) k);
508 			k = n;
509 		}
510 	}
511 	/*
512 	 * Zero the hash slots
513 	 */
514 	bzero((voidp) m->kvhash, sizeof(m->kvhash));
515 	/*
516 	 * Free the wildcard if it exists
517 	 */
518 	if (m->wildcard) {
519 		free(m->wildcard);
520 		m->wildcard = 0;
521 	}
522 }
523 
524 /*
525  * Find a map, or create one if it does not exist
526  */
527 mnt_map *mapc_find P((char *map, char *opt));
528 mnt_map *mapc_find(map, opt)
529 char *map;
530 char *opt;
531 {
532 	mnt_map *m;
533 
534 	/*
535 	 * Search the list of known maps to see if
536 	 * it has already been loaded.  If it is found
537 	 * then return a duplicate reference to it.
538 	 * Otherwise make a new map as required and
539 	 * add it to the list of maps
540 	 */
541 	ITER(m, mnt_map, &map_list_head)
542 		if (STREQ(m->map_name, map))
543 			return mapc_dup(m);
544 
545 	m = mapc_create(map, opt);
546 	ins_que(&m->hdr, &map_list_head);
547 	return m;
548 }
549 
550 /*
551  * Free a map.
552  */
553 void mapc_free P((mnt_map *m));
554 void mapc_free(m)
555 mnt_map *m;
556 {
557 	/*
558 	 * Decrement the reference count.
559 	 * If the reference count hits zero
560 	 * then throw the map away.
561 	 */
562 	if (m && --m->refc == 0) {
563 		mapc_clear(m);
564 		free((voidp) m->map_name);
565 		rem_que(&m->hdr);
566 		free((voidp) m);
567 	}
568 }
569 
570 /*
571  * Search the map for the key.
572  * Put a safe copy in *pval or return
573  * an error code
574  */
575 int mapc_meta_search P((mnt_map *m, char *key, char **pval, int recurse));
576 int mapc_meta_search(m, key, pval, recurse)
577 mnt_map *m;
578 char *key;
579 char **pval;
580 int recurse;
581 {
582 	int error = 0;
583 	kv *k = 0;
584 
585 	/*
586 	 * Firewall
587 	 */
588 	if (!m) {
589 		plog(XLOG_ERROR, "Null map request for %s", key);
590 		return ENOENT;
591 	}
592 
593 	if (m->flags & MAPC_SYNC) {
594 		/*
595 		 * Get modify time...
596 		 */
597 		time_t t;
598 		error = (*m->mtime)(m->map_name, &t);
599 		if (error || t > m->modify) {
600 			m->modify = t;
601 			plog(XLOG_INFO, "Map %s is out of date", m->map_name);
602 			mapc_sync(m);
603 		}
604 	}
605 
606 	if (!MAPC_ISRE(m)) {
607 		/*
608 		 * Compute the hash table offset
609 		 */
610 		k = m->kvhash[kvhash_of(key)];
611 
612 		/*
613 		 * Scan the linked list for the key
614 		 */
615 		while (k && !FSTREQ(k->key, key)) k = k->next;
616 
617 	}
618 #ifdef HAS_REGEXP
619 	else if (recurse == MREC_FULL) {
620 		/*
621 		 * Try for an RE match against the entire map.
622 		 * Note that this will be done in a "random"
623 		 * order.
624 		 */
625 
626 		int i;
627 
628 		for (i = 0; i < NKVHASH; i++) {
629 			k = m->kvhash[i];
630 			while (k) {
631 				if (regexec((regexp *) k->key, key))
632 					break;
633 				k = k->next;
634 			}
635 			if (k)
636 				break;
637 		}
638 	}
639 #endif
640 
641 	/*
642 	 * If found then take a copy
643 	 */
644 	if (k) {
645 		if (k->val)
646 			*pval = strdup(k->val);
647 		else
648 			error = ENOENT;
649 	} else if (m->alloc >= MAPC_ALL) {
650 		/*
651 		 * If the entire map is cached then this
652 		 * key does not exist.
653 		 */
654 		error = ENOENT;
655 	} else {
656 		/*
657 		 * Otherwise search the map.  If we are
658 		 * in incremental mode then add the key
659 		 * to the cache.
660 		 */
661 		error = search_map(m, key, pval);
662 		if (!error && m->alloc == MAPC_INC)
663 			mapc_add_kv(m, strdup(key), strdup(*pval));
664 	}
665 
666 	/*
667 	 * If an error, and a wildcard exists,
668 	 * and the key is not internal then
669 	 * return a copy of the wildcard.
670 	 */
671 	if (error > 0) {
672 		if (recurse == MREC_FULL && !MAPC_ISRE(m)) {
673 			char wildname[MAXPATHLEN];
674 			char *subp;
675 			if (*key == '/')
676 				return error;
677 			/*
678 			 * Keep chopping sub-directories from the RHS
679 			 * and replacing with "/ *" and repeat the lookup.
680 			 * For example:
681 			 * "src/gnu/gcc" -> "src / gnu / *" -> "src / *"
682 			 */
683 			strcpy(wildname, key);
684 			while (error && (subp = strrchr(wildname, '/'))) {
685 				strcpy(subp, "/*");
686 #ifdef DEBUG
687 				dlog("mapc recurses on %s", wildname);
688 #endif
689 				error = mapc_meta_search(m, wildname, pval, MREC_PART);
690 				if (error)
691 					*subp = 0;
692 			}
693 			if (error > 0 && m->wildcard) {
694 				*pval = strdup(m->wildcard);
695 				error = 0;
696 			}
697 		}
698 	}
699 
700 	return error;
701 }
702 
703 int mapc_search P((mnt_map *m, char *key, char **pval));
704 int mapc_search(m, key, pval)
705 mnt_map *m;
706 char *key;
707 char **pval;
708 {
709 	return mapc_meta_search(m, key, pval, MREC_FULL);
710 }
711 
712 /*
713  * Get map cache in sync with physical representation
714  */
715 static void mapc_sync P((mnt_map *m));
716 static void mapc_sync(m)
717 mnt_map *m;
718 {
719 	if (m->alloc != MAPC_ROOT) {
720 		mapc_clear(m);
721 
722 		if (m->alloc >= MAPC_ALL)
723 			if (mapc_reload_map(m))
724 				m->alloc = MAPC_INC;
725 		/*
726 		 * Attempt to find the wildcard entry
727 		 */
728 		if (m->alloc < MAPC_ALL)
729 			mapc_find_wildcard(m);
730 	}
731 }
732 
733 /*
734  * Reload all the maps
735  * Called when Amd gets hit by a SIGHUP.
736  */
737 void mapc_reload(P_void);
738 void mapc_reload()
739 {
740 	mnt_map *m;
741 
742 	/*
743 	 * For all the maps,
744 	 * Throw away the existing information.
745 	 * Do a reload
746 	 * Find the wildcard
747 	 */
748 	ITER(m, mnt_map, &map_list_head)
749 		mapc_sync(m);
750 }
751 
752 /*
753  * Root map.
754  * The root map is used to bootstrap amd.
755  * All the require top-level mounts are added
756  * into the root map and then the map is iterated
757  * and a lookup is done on all the mount points.
758  * This causes the top level mounts to be automounted.
759  */
760 
761 static int root_init P((char *map, time_t *tp));
762 static int root_init(map, tp)
763 char *map;
764 time_t *tp;
765 {
766 	*tp = clocktime();
767 	return strcmp(map, ROOT_MAP) == 0 ? 0 : ENOENT;
768 }
769 
770 /*
771  * Add a new entry to the root map
772  *
773  * dir - directory (key)
774  * opts - mount options
775  * map - map name
776  */
777 void root_newmap P((char *dir, char *opts, char *map));
778 void root_newmap(dir, opts, map)
779 char *dir;
780 char *opts;
781 char *map;
782 {
783 	char str[MAXPATHLEN];
784 
785 	/*
786 	 * First make sure we have a root map to talk about...
787 	 */
788 	if (!root_map)
789 		root_map = mapc_find(ROOT_MAP, "mapdefault");
790 
791 	/*
792 	 * Then add the entry...
793 	 */
794 	dir = strdup(dir);
795 	if (map)
796 		sprintf(str, "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s",
797 			map, opts ? opts : "");
798 	else
799 		strcpy(str, opts);
800 	mapc_repl_kv(root_map, dir, strdup(str));
801 }
802 
803 int mapc_keyiter P((mnt_map *m, void (*fn)(char*,voidp), voidp arg));
804 int mapc_keyiter(m, fn, arg)
805 mnt_map *m;
806 void (*fn)P((char*, voidp));
807 voidp arg;
808 {
809 	int i;
810 	int c = 0;
811 
812 	for (i = 0; i < NKVHASH; i++) {
813 		kv *k = m->kvhash[i];
814 		while (k) {
815 			(*fn)(k->key, arg);
816 			k = k->next;
817 			c++;
818 		}
819 	}
820 
821 	return c;
822 }
823 
824 /*
825  * Iterate of the the root map
826  * and call (*fn)() on the key
827  * of all the nodes.
828  * Finally throw away the root map.
829  */
830 int root_keyiter P((void (*fn)(char*,voidp), voidp arg));
831 int root_keyiter(fn, arg)
832 void (*fn)P((char*,voidp));
833 voidp arg;
834 {
835 	if (root_map) {
836 		int c = mapc_keyiter(root_map, fn, arg);
837 #ifdef notdef
838 		mapc_free(root_map);
839 		root_map = 0;
840 #endif
841 		return c;
842 	}
843 	return 0;
844 }
845 
846 /*
847  * Error map
848  */
849 static int error_init P((char *map, time_t *tp));
850 static int error_init(map, tp)
851 char *map;
852 time_t *tp;
853 {
854 	plog(XLOG_USER, "No source data for map %s", map);
855 	*tp = 0;
856 	return 0;
857 }
858 
859 /*ARGSUSED*/
860 static int error_search P((mnt_map *m, char *map, char *key, char **pval, time_t *tp));
861 static int error_search(m, map, key, pval, tp)
862 mnt_map *m;
863 char *map;
864 char *key;
865 char **pval;
866 time_t *tp;
867 {
868 	return ENOENT;
869 }
870 
871 /*ARGSUSED*/
872 static int error_reload P((mnt_map *m, char *map, add_fn *fn));
873 static int error_reload(m, map, fn)
874 mnt_map *m;
875 char *map;
876 add_fn *fn;
877 {
878 	return ENOENT;
879 }
880 
881 static int error_mtime P((char *map, time_t *tp));
882 static int error_mtime(map, tp)
883 char *map;
884 time_t *tp;
885 {
886 	*tp = 0;
887 	return 0;
888 }
889