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