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