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