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