1/* 2 * Copyright (c) 1997-2006 Erez Zadok 3 * Copyright (c) 1989 Jan-Simon Pendry 4 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 5 * Copyright (c) 1989 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Jan-Simon Pendry at Imperial College, London. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgment: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * 40 * File: am-utils/amd/mapc.c 41 * 42 */ 43 44/* 45 * Mount map cache 46 */ 47 48#ifdef HAVE_CONFIG_H 49# include <config.h> 50#endif /* HAVE_CONFIG_H */ 51#include <am_defs.h> 52#include <amd.h> 53 54/* 55 * Make a duplicate reference to an existing map 56 */ 57#define mapc_dup(m) ((m)->refc++, (m)) 58 59/* 60 * Map cache types 61 * default, none, incremental, all, regexp 62 * MAPC_RE implies MAPC_ALL and must be numerically 63 * greater. 64 */ 65#define MAPC_DFLT 0x000 66#define MAPC_NONE 0x001 67#define MAPC_INC 0x002 68#define MAPC_ROOT 0x004 69#define MAPC_ALL 0x010 70#define MAPC_CACHE_MASK 0x0ff 71#define MAPC_SYNC 0x100 72 73#ifdef HAVE_REGEXEC 74# define MAPC_RE 0x020 75# define MAPC_ISRE(m) ((m)->alloc == MAPC_RE) 76#else /* not HAVE_REGEXEC */ 77# define MAPC_ISRE(m) FALSE 78#endif /* not HAVE_REGEXEC */ 79 80/* 81 * Lookup recursion 82 */ 83#define MREC_FULL 2 84#define MREC_PART 1 85#define MREC_NONE 0 86 87static struct opt_tab mapc_opt[] = 88{ 89 {"all", MAPC_ALL}, 90 {"default", MAPC_DFLT}, 91 {"inc", MAPC_INC}, 92 {"mapdefault", MAPC_DFLT}, 93 {"none", MAPC_NONE}, 94#ifdef HAVE_REGEXEC 95 {"re", MAPC_RE}, 96 {"regexp", MAPC_RE}, 97#endif /* HAVE_REGEXEC */ 98 {"sync", MAPC_SYNC}, 99 {0, 0} 100}; 101 102/* 103 * Wildcard key 104 */ 105static char wildcard[] = "*"; 106 107/* 108 * Map type 109 */ 110typedef struct map_type map_type; 111struct map_type { 112 char *name; /* Name of this map type */ 113 init_fn *init; /* Initialization */ 114 reload_fn *reload; /* Reload or fill */ 115 isup_fn *isup; /* Is service up or not? (1=up, 0=down) */ 116 search_fn *search; /* Search for new entry */ 117 mtime_fn *mtime; /* Find modify time */ 118 int def_alloc; /* Default allocation mode */ 119}; 120 121/* 122 * Map for root node 123 */ 124static mnt_map *root_map; 125 126/* 127 * List of known maps 128 */ 129qelem map_list_head = {&map_list_head, &map_list_head}; 130 131/* 132 * Configuration 133 */ 134 135/* forward definitions */ 136static const char *get_full_path(const char *map, const char *path, const char *type); 137static int mapc_meta_search(mnt_map *, char *, char **, int); 138static void mapc_sync(mnt_map *); 139static void mapc_clear(mnt_map *); 140 141/* ROOT MAP */ 142static int root_init(mnt_map *, char *, time_t *); 143 144/* ERROR MAP */ 145static int error_init(mnt_map *, char *, time_t *); 146static int error_reload(mnt_map *, char *, add_fn *); 147static int error_search(mnt_map *, char *, char *, char **, time_t *); 148static int error_mtime(mnt_map *, char *, time_t *); 149 150/* PASSWD MAPS */ 151#ifdef HAVE_MAP_PASSWD 152extern int passwd_init(mnt_map *, char *, time_t *); 153extern int passwd_search(mnt_map *, char *, char *, char **, time_t *); 154#endif /* HAVE_MAP_PASSWD */ 155 156/* HESIOD MAPS */ 157#ifdef HAVE_MAP_HESIOD 158extern int amu_hesiod_init(mnt_map *, char *map, time_t *tp); 159extern int hesiod_isup(mnt_map *, char *); 160extern int hesiod_search(mnt_map *, char *, char *, char **, time_t *); 161#endif /* HAVE_MAP_HESIOD */ 162 163/* LDAP MAPS */ 164#ifdef HAVE_MAP_LDAP 165extern int amu_ldap_init(mnt_map *, char *map, time_t *tp); 166extern int amu_ldap_search(mnt_map *, char *, char *, char **, time_t *); 167extern int amu_ldap_mtime(mnt_map *, char *, time_t *); 168#endif /* HAVE_MAP_LDAP */ 169 170/* UNION MAPS */ 171#ifdef HAVE_MAP_UNION 172extern int union_init(mnt_map *, char *, time_t *); 173extern int union_search(mnt_map *, char *, char *, char **, time_t *); 174extern int union_reload(mnt_map *, char *, add_fn *); 175#endif /* HAVE_MAP_UNION */ 176 177/* Network Information Service PLUS (NIS+) */ 178#ifdef HAVE_MAP_NISPLUS 179extern int nisplus_init(mnt_map *, char *, time_t *); 180extern int nisplus_reload(mnt_map *, char *, add_fn *); 181extern int nisplus_search(mnt_map *, char *, char *, char **, time_t *); 182extern int nisplus_mtime(mnt_map *, char *, time_t *); 183#endif /* HAVE_MAP_NISPLUS */ 184 185/* Network Information Service (YP, Yellow Pages) */ 186#ifdef HAVE_MAP_NIS 187extern int nis_init(mnt_map *, char *, time_t *); 188extern int nis_reload(mnt_map *, char *, add_fn *); 189extern int nis_isup(mnt_map *, char *); 190extern int nis_search(mnt_map *, char *, char *, char **, time_t *); 191extern int nis_mtime(mnt_map *, char *, time_t *); 192#endif /* HAVE_MAP_NIS */ 193 194/* NDBM MAPS */ 195#ifdef HAVE_MAP_NDBM 196extern int ndbm_init(mnt_map *, char *, time_t *); 197extern int ndbm_search(mnt_map *, char *, char *, char **, time_t *); 198extern int ndbm_mtime(mnt_map *, char *, time_t *); 199#endif /* HAVE_MAP_NDBM */ 200 201/* EXECUTABLE MAPS */ 202#ifdef HAVE_MAP_EXEC 203extern int exec_init(mnt_map *, char *, time_t *); 204extern int exec_search(mnt_map *, char *, char *, char **, time_t *); 205#endif /* HAVE_MAP_EXEC */ 206 207/* FILE MAPS */ 208#ifdef HAVE_MAP_FILE 209extern int file_init_or_mtime(mnt_map *, char *, time_t *); 210extern int file_reload(mnt_map *, char *, add_fn *); 211extern int file_search(mnt_map *, char *, char *, char **, time_t *); 212#endif /* HAVE_MAP_FILE */ 213 214 215/* note that the choice of MAPC_{INC,ALL} will affect browsable_dirs */ 216static map_type maptypes[] = 217{ 218 { 219 "root", 220 root_init, 221 error_reload, 222 NULL, /* isup function */ 223 error_search, 224 error_mtime, 225 MAPC_ROOT 226 }, 227#ifdef HAVE_MAP_PASSWD 228 { 229 "passwd", 230 passwd_init, 231 error_reload, 232 NULL, /* isup function */ 233 passwd_search, 234 error_mtime, 235 MAPC_INC 236 }, 237#endif /* HAVE_MAP_PASSWD */ 238#ifdef HAVE_MAP_HESIOD 239 { 240 "hesiod", 241 amu_hesiod_init, 242 error_reload, 243 hesiod_isup, /* is Hesiod up or not? */ 244 hesiod_search, 245 error_mtime, 246 MAPC_INC 247 }, 248#endif /* HAVE_MAP_HESIOD */ 249#ifdef HAVE_MAP_LDAP 250 { 251 "ldap", 252 amu_ldap_init, 253 error_reload, 254 NULL, /* isup function */ 255 amu_ldap_search, 256 amu_ldap_mtime, 257 MAPC_INC 258 }, 259#endif /* HAVE_MAP_LDAP */ 260#ifdef HAVE_MAP_UNION 261 { 262 "union", 263 union_init, 264 union_reload, 265 NULL, /* isup function */ 266 union_search, 267 error_mtime, 268 MAPC_ALL 269 }, 270#endif /* HAVE_MAP_UNION */ 271#ifdef HAVE_MAP_NISPLUS 272 { 273 "nisplus", 274 nisplus_init, 275 nisplus_reload, 276 NULL, /* isup function */ 277 nisplus_search, 278 nisplus_mtime, 279 MAPC_INC 280 }, 281#endif /* HAVE_MAP_NISPLUS */ 282#ifdef HAVE_MAP_NIS 283 { 284 "nis", 285 nis_init, 286 nis_reload, 287 nis_isup, /* is NIS up or not? */ 288 nis_search, 289 nis_mtime, 290 MAPC_ALL 291 }, 292#endif /* HAVE_MAP_NIS */ 293#ifdef HAVE_MAP_NDBM 294 { 295 "ndbm", 296 ndbm_init, 297 error_reload, 298 NULL, /* isup function */ 299 ndbm_search, 300 ndbm_mtime, 301 MAPC_INC 302 }, 303#endif /* HAVE_MAP_NDBM */ 304#ifdef HAVE_MAP_FILE 305 { 306 "file", 307 file_init_or_mtime, 308 file_reload, 309 NULL, /* isup function */ 310 file_search, 311 file_init_or_mtime, 312 MAPC_ALL 313 }, 314#endif /* HAVE_MAP_FILE */ 315#ifdef HAVE_MAP_EXEC 316 { 317 "exec", 318 exec_init, 319 error_reload, 320 NULL, /* isup function */ 321 exec_search, 322 error_mtime, 323 MAPC_INC 324 }, 325#endif /* HAVE_MAP_EXEC */ 326 { 327 "error", 328 error_init, 329 error_reload, 330 NULL, /* isup function */ 331 error_search, 332 error_mtime, 333 MAPC_NONE 334 }, 335}; 336 337 338/* 339 * Hash function 340 */ 341static u_int 342kvhash_of(char *key) 343{ 344 u_int i, j; 345 346 for (i = 0; (j = *key++); i += j) ; 347 348 return i % NKVHASH; 349} 350 351 352void 353mapc_showtypes(char *buf, size_t l) 354{ 355 map_type *mt=NULL, *lastmt; 356 int linesize = 0, i; 357 358 i = sizeof(maptypes) / sizeof(maptypes[0]); 359 lastmt = maptypes + i; 360 buf[0] = '\0'; 361 for (mt = maptypes; mt < lastmt; mt++) { 362 xstrlcat(buf, mt->name, l); 363 if (mt == (lastmt-1)) 364 break; /* if last one, don't do xstrlcat's that follows */ 365 linesize += strlen(mt->name); 366 if (--i > 0) { 367 xstrlcat(buf, ", ", l); 368 linesize += 2; 369 } 370 if (linesize > 54) { 371 linesize = 0; 372 xstrlcat(buf, "\n\t\t ", l); 373 } 374 } 375} 376 377 378/* 379 * Check if a map of a certain type exists. 380 * Return 1 (true) if exists, 0 (false) if not. 381 */ 382int 383mapc_type_exists(const char *type) 384{ 385 map_type *mt; 386 387 if (!type) 388 return 0; 389 for (mt = maptypes; 390 mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]); 391 mt++) { 392 if (STREQ(type, mt->name)) 393 return 1; 394 } 395 return 0; /* not found anywhere */ 396} 397 398 399/* 400 * Add key and val to the map m. 401 * key and val are assumed to be safe copies 402 */ 403void 404mapc_add_kv(mnt_map *m, char *key, char *val) 405{ 406 kv **h; 407 kv *n; 408 int hash = kvhash_of(key); 409#ifdef HAVE_REGEXEC 410 regex_t re; 411#endif /* HAVE_REGEXEC */ 412 413 dlog("add_kv: %s -> %s", key, val); 414 415#ifdef HAVE_REGEXEC 416 if (MAPC_ISRE(m)) { 417 char pattern[MAXPATHLEN]; 418 int retval; 419 420 /* 421 * Make sure the string is bound to the start and end 422 */ 423 xsnprintf(pattern, sizeof(pattern), "^%s$", key); 424 retval = regcomp(&re, pattern, REG_ICASE); 425 if (retval != 0) { 426 char errstr[256]; 427 428 /* XXX: this code was recently ported, and must be tested -Erez */ 429 errstr[0] = '\0'; 430 regerror(retval, &re, errstr, 256); 431 plog(XLOG_USER, "error compiling RE \"%s\": %s", pattern, errstr); 432 return; 433 } 434 } 435#endif /* HAVE_REGEXEC */ 436 437 h = &m->kvhash[hash]; 438 n = ALLOC(struct kv); 439 n->key = key; 440#ifdef HAVE_REGEXEC 441 memcpy(&n->re, &re, sizeof(regex_t)); 442#endif /* HAVE_REGEXEC */ 443 n->val = val; 444 n->next = *h; 445 *h = n; 446} 447 448 449static void 450mapc_repl_kv(mnt_map *m, char *key, char *val) 451{ 452 kv *k; 453 454 /* 455 * Compute the hash table offset 456 */ 457 k = m->kvhash[kvhash_of(key)]; 458 459 /* 460 * Scan the linked list for the key 461 */ 462 while (k && !FSTREQ(k->key, key)) 463 k = k->next; 464 465 if (k) { 466 XFREE(k->val); 467 k->val = val; 468 } else { 469 mapc_add_kv(m, key, val); 470 } 471} 472 473 474/* 475 * Search a map for a key. 476 * Calls map specific search routine. 477 * While map is out of date, keep re-syncing. 478 */ 479static int 480search_map(mnt_map *m, char *key, char **valp) 481{ 482 int rc; 483 484 do { 485 rc = (*m->search) (m, m->map_name, key, valp, &m->modify); 486 if (rc < 0) { 487 plog(XLOG_MAP, "Re-synchronizing cache for map %s", m->map_name); 488 mapc_sync(m); 489 } 490 } while (rc < 0); 491 492 return rc; 493} 494 495 496/* 497 * Do a wildcard lookup in the map and 498 * save the result. 499 */ 500static void 501mapc_find_wildcard(mnt_map *m) 502{ 503 /* 504 * Attempt to find the wildcard entry 505 */ 506 int rc = search_map(m, wildcard, &m->wildcard); 507 508 if (rc != 0) 509 m->wildcard = 0; 510} 511 512 513/* 514 * Do a map reload. 515 * Attempt to reload without losing current data by switching the hashes 516 * round. 517 * If reloading was needed and succeeded, return 1; else return 0. 518 */ 519static int 520mapc_reload_map(mnt_map *m) 521{ 522 int error, ret = 0; 523 kv *maphash[NKVHASH], *tmphash[NKVHASH]; 524 time_t t; 525 526 error = (*m->mtime) (m, m->map_name, &t); 527 if (error) { 528 t = m->modify; 529 } 530 531 /* 532 * skip reloading maps that have not been modified, unless 533 * amq -f was used (do_mapc_reload is 0) 534 */ 535 if (m->reloads != 0 && do_mapc_reload != 0) { 536 if (t <= m->modify) { 537 plog(XLOG_INFO, "reload of map %s is not needed (in sync)", m->map_name); 538 dlog("map %s last load time is %d, last modify time is %d", 539 m->map_name, (int) m->modify, (int) t); 540 return ret; 541 } 542 } 543 544 /* copy the old hash and zero the map */ 545 memcpy((voidp) maphash, (voidp) m->kvhash, sizeof(m->kvhash)); 546 memset((voidp) m->kvhash, 0, sizeof(m->kvhash)); 547 548 dlog("calling map reload on %s", m->map_name); 549 error = (*m->reload) (m, m->map_name, mapc_add_kv); 550 if (error) { 551 if (m->reloads == 0) 552 plog(XLOG_FATAL, "first time load of map %s failed!", m->map_name); 553 else 554 plog(XLOG_ERROR, "reload of map %s failed - using old values", 555 m->map_name); 556 mapc_clear(m); 557 memcpy((voidp) m->kvhash, (voidp) maphash, sizeof(m->kvhash)); 558 } else { 559 if (m->reloads++ == 0) 560 plog(XLOG_INFO, "first time load of map %s succeeded", m->map_name); 561 else 562 plog(XLOG_INFO, "reload #%d of map %s succeeded", 563 m->reloads, m->map_name); 564 memcpy((voidp) tmphash, (voidp) m->kvhash, sizeof(m->kvhash)); 565 memcpy((voidp) m->kvhash, (voidp) maphash, sizeof(m->kvhash)); 566 mapc_clear(m); 567 memcpy((voidp) m->kvhash, (voidp) tmphash, sizeof(m->kvhash)); 568 m->modify = t; 569 ret = 1; 570 } 571 m->wildcard = 0; 572 573 dlog("calling mapc_search for wildcard"); 574 error = mapc_search(m, wildcard, &m->wildcard); 575 if (error) 576 m->wildcard = 0; 577 return ret; 578} 579 580 581/* 582 * Create a new map 583 */ 584static mnt_map * 585mapc_create(char *map, char *opt, const char *type) 586{ 587 mnt_map *m = ALLOC(struct mnt_map); 588 map_type *mt; 589 time_t modify = 0; 590 int alloc = 0; 591 592 cmdoption(opt, mapc_opt, &alloc); 593 594 /* 595 * If using a configuration file, and the map_type is defined, then look 596 * for it, in the maptypes array. If found, initialize the map using that 597 * map_type. If not found, return error. If no map_type was defined, 598 * default to cycling through all maptypes. 599 */ 600 if (use_conf_file && type) { 601 /* find what type of map this one is */ 602 for (mt = maptypes; 603 mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]); 604 mt++) { 605 if (STREQ(type, mt->name)) { 606 plog(XLOG_INFO, "initializing amd.conf map %s of type %s", map, type); 607 if ((*mt->init) (m, map, &modify) == 0) { 608 break; 609 } else { 610 plog(XLOG_ERROR, "failed to initialize map %s", map); 611 error_init(m, map, &modify); 612 break; 613 } 614 } 615 } /* end of "for (mt =" loop */ 616 617 } else { /* cycle through all known maptypes */ 618 619 /* 620 * not using amd conf file or using it by w/o specifying map type 621 */ 622 for (mt = maptypes; 623 mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]); 624 mt++) { 625 dlog("trying to initialize map %s of type %s ...", map, mt->name); 626 if ((*mt->init) (m, map, &modify) == 0) { 627 break; 628 } 629 } 630 } /* end of "if (use_conf_file && (colpos = strchr ..." statement */ 631 632 /* assert: mt in maptypes */ 633 634 m->flags = alloc & ~MAPC_CACHE_MASK; 635 alloc &= MAPC_CACHE_MASK; 636 637 if (alloc == MAPC_DFLT) 638 alloc = mt->def_alloc; 639 640 switch (alloc) { 641 default: 642 plog(XLOG_USER, "Ambiguous map cache type \"%s\"; using \"inc\"", opt); 643 alloc = MAPC_INC; 644 /* fall-through... */ 645 case MAPC_NONE: 646 case MAPC_INC: 647 case MAPC_ROOT: 648 break; 649 650 case MAPC_ALL: 651 /* 652 * If there is no support for reload and it was requested 653 * then back off to incremental instead. 654 */ 655 if (mt->reload == error_reload) { 656 plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"all\"; using \"inc\"", mt->name); 657 alloc = MAPC_INC; 658 } 659 break; 660 661#ifdef HAVE_REGEXEC 662 case MAPC_RE: 663 if (mt->reload == error_reload) { 664 plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"re\"", mt->name); 665 mt = &maptypes[sizeof(maptypes) / sizeof(maptypes[0]) - 1]; 666 /* assert: mt->name == "error" */ 667 } 668 break; 669#endif /* HAVE_REGEXEC */ 670 } 671 672 dlog("Map for %s coming from maptype %s", map, mt->name); 673 674 m->alloc = alloc; 675 m->reload = mt->reload; 676 m->isup = mt->isup; 677 m->modify = modify; 678 m->search = alloc >= MAPC_ALL ? error_search : mt->search; 679 m->mtime = mt->mtime; 680 memset((voidp) m->kvhash, 0, sizeof(m->kvhash)); 681 m->map_name = strdup(map); 682 m->refc = 1; 683 m->wildcard = 0; 684 m->reloads = 0; 685 /* Unfortunately with current code structure, this cannot be initialized here */ 686 m->cfm = NULL; 687 688 /* 689 * synchronize cache with reality 690 */ 691 mapc_sync(m); 692 693 return m; 694} 695 696 697/* 698 * Free the cached data in a map 699 */ 700static void 701mapc_clear(mnt_map *m) 702{ 703 int i; 704 705 /* 706 * For each of the hash slots, chain 707 * along free'ing the data. 708 */ 709 for (i = 0; i < NKVHASH; i++) { 710 kv *k = m->kvhash[i]; 711 while (k) { 712 kv *n = k->next; 713 XFREE(k->key); 714 if (k->val) 715 XFREE(k->val); 716 XFREE(k); 717 k = n; 718 } 719 } 720 721 /* 722 * Zero the hash slots 723 */ 724 memset((voidp) m->kvhash, 0, sizeof(m->kvhash)); 725 726 /* 727 * Free the wildcard if it exists 728 */ 729 if (m->wildcard) { 730 XFREE(m->wildcard); 731 m->wildcard = 0; 732 } 733} 734 735 736/* 737 * Find a map, or create one if it does not exist 738 */ 739mnt_map * 740mapc_find(char *map, char *opt, const char *maptype) 741{ 742 mnt_map *m; 743 744 /* 745 * Search the list of known maps to see if 746 * it has already been loaded. If it is found 747 * then return a duplicate reference to it. 748 * Otherwise make a new map as required and 749 * add it to the list of maps 750 */ 751 ITER(m, mnt_map, &map_list_head) 752 if (STREQ(m->map_name, map)) 753 return mapc_dup(m); 754 m = mapc_create(map, opt, maptype); 755 ins_que(&m->hdr, &map_list_head); 756 757 return m; 758} 759 760 761/* 762 * Free a map. 763 */ 764void 765mapc_free(opaque_t arg) 766{ 767 mnt_map *m = (mnt_map *) arg; 768 769 /* 770 * Decrement the reference count. 771 * If the reference count hits zero 772 * then throw the map away. 773 */ 774 if (m && --m->refc == 0) { 775 mapc_clear(m); 776 XFREE(m->map_name); 777 rem_que(&m->hdr); 778 XFREE(m); 779 } 780} 781 782 783/* 784 * Search the map for the key. Put a safe (malloc'ed) copy in *pval or 785 * return an error code 786 */ 787static int 788mapc_meta_search(mnt_map *m, char *key, char **pval, int recurse) 789{ 790 int error = 0; 791 kv *k = 0; 792 793 /* 794 * Firewall 795 */ 796 if (!m) { 797 plog(XLOG_ERROR, "Null map request for %s", key); 798 return ENOENT; 799 } 800 801 if (m->flags & MAPC_SYNC) { 802 /* 803 * Get modify time... 804 */ 805 time_t t; 806 error = (*m->mtime) (m, m->map_name, &t); 807 if (error || t > m->modify) { 808 plog(XLOG_INFO, "Map %s is out of date", m->map_name); 809 mapc_sync(m); 810 } 811 } 812 813 if (!MAPC_ISRE(m)) { 814 /* 815 * Compute the hash table offset 816 */ 817 k = m->kvhash[kvhash_of(key)]; 818 819 /* 820 * Scan the linked list for the key 821 */ 822 while (k && !FSTREQ(k->key, key)) 823 k = k->next; 824 825 } 826 827#ifdef HAVE_REGEXEC 828 else if (recurse == MREC_FULL) { 829 /* 830 * Try for an RE match against the entire map. 831 * Note that this will be done in a "random" 832 * order. 833 */ 834 int i; 835 836 for (i = 0; i < NKVHASH; i++) { 837 k = m->kvhash[i]; 838 while (k) { 839 int retval; 840 841 /* XXX: this code was recently ported, and must be tested -Erez */ 842 retval = regexec(&k->re, key, 0, 0, 0); 843 if (retval == 0) { /* succeeded */ 844 break; 845 } else { /* failed to match, log error */ 846 char errstr[256]; 847 848 errstr[0] = '\0'; 849 regerror(retval, &k->re, errstr, 256); 850 plog(XLOG_USER, "error matching RE \"%s\" against \"%s\": %s", 851 key, k->key, errstr); 852 } 853 k = k->next; 854 } 855 if (k) 856 break; 857 } 858 } 859#endif /* HAVE_REGEXEC */ 860 861 /* 862 * If found then take a copy 863 */ 864 if (k) { 865 if (k->val) 866 *pval = strdup(k->val); 867 else 868 error = ENOENT; 869 } else if (m->alloc >= MAPC_ALL) { 870 /* 871 * If the entire map is cached then this 872 * key does not exist. 873 */ 874 error = ENOENT; 875 } else { 876 /* 877 * Otherwise search the map. If we are 878 * in incremental mode then add the key 879 * to the cache. 880 */ 881 error = search_map(m, key, pval); 882 if (!error && m->alloc == MAPC_INC) 883 mapc_add_kv(m, strdup(key), strdup(*pval)); 884 } 885 886 /* 887 * If an error, and a wildcard exists, 888 * and the key is not internal then 889 * return a copy of the wildcard. 890 */ 891 if (error > 0) { 892 if (recurse == MREC_FULL && !MAPC_ISRE(m)) { 893 char wildname[MAXPATHLEN]; 894 char *subp; 895 if (*key == '/') 896 return error; 897 /* 898 * Keep chopping sub-directories from the RHS 899 * and replacing with "/ *" and repeat the lookup. 900 * For example: 901 * "src/gnu/gcc" -> "src / gnu / *" -> "src / *" 902 */ 903 xstrlcpy(wildname, key, sizeof(wildname)); 904 while (error && (subp = strrchr(wildname, '/'))) { 905 /* 906 * sizeof space left in subp is sizeof wildname minus what's left 907 * after the strchr above returned a pointer inside wildname into 908 * subp. 909 */ 910 xstrlcpy(subp, "/*", sizeof(wildname) - (subp - wildname)); 911 dlog("mapc recurses on %s", wildname); 912 error = mapc_meta_search(m, wildname, pval, MREC_PART); 913 if (error) 914 *subp = 0; 915 } 916 917 if (error > 0 && m->wildcard) { 918 *pval = strdup(m->wildcard); 919 error = 0; 920 } 921 } 922 } 923 return error; 924} 925 926 927int 928mapc_search(mnt_map *m, char *key, char **pval) 929{ 930 return mapc_meta_search(m, key, pval, MREC_FULL); 931} 932 933 934/* 935 * Get map cache in sync with physical representation 936 */ 937static void 938mapc_sync(mnt_map *m) 939{ 940 int need_mtime_update = 0; 941 942 if (m->alloc == MAPC_ROOT) 943 return; /* nothing to do */ 944 945 /* do not clear map if map service is down */ 946 if (m->isup) { 947 if (!((*m->isup)(m, m->map_name))) { 948 plog(XLOG_ERROR, "mapc_sync: map %s is down: not clearing map", m->map_name); 949 return; 950 } 951 } 952 953 if (m->alloc >= MAPC_ALL) { 954 /* mapc_reload_map() always works */ 955 need_mtime_update = mapc_reload_map(m); 956 } else { 957 mapc_clear(m); 958 /* 959 * Attempt to find the wildcard entry 960 */ 961 mapc_find_wildcard(m); 962 need_mtime_update = 1; /* because mapc_clear always works */ 963 } 964 965 /* 966 * To be safe, update the mtime of the mnt_map's own node, so that the 967 * kernel will flush all of its cached entries. 968 */ 969 if (need_mtime_update && m->cfm) { 970 am_node *mp = find_ap(m->cfm->cfm_dir); 971 if (mp) { 972 clocktime(&mp->am_fattr.na_mtime); 973 } else { 974 plog(XLOG_ERROR, "cannot find map %s to update its mtime", 975 m->cfm->cfm_dir); 976 } 977 } 978} 979 980 981/* 982 * Reload all the maps 983 * Called when Amd gets hit by a SIGHUP. 984 */ 985void 986mapc_reload(void) 987{ 988 mnt_map *m; 989 990 /* 991 * For all the maps, 992 * Throw away the existing information. 993 * Do a reload 994 * Find the wildcard 995 */ 996 ITER(m, mnt_map, &map_list_head) 997 mapc_sync(m); 998} 999 1000 1001/* 1002 * Root map. 1003 * The root map is used to bootstrap amd. 1004 * All the require top-level mounts are added 1005 * into the root map and then the map is iterated 1006 * and a lookup is done on all the mount points. 1007 * This causes the top level mounts to be automounted. 1008 */ 1009static int 1010root_init(mnt_map *m, char *map, time_t *tp) 1011{ 1012 *tp = clocktime(NULL); 1013 return STREQ(map, ROOT_MAP) ? 0 : ENOENT; 1014} 1015 1016 1017/* 1018 * Add a new entry to the root map 1019 * 1020 * dir - directory (key) 1021 * opts - mount options 1022 * map - map name 1023 * cfm - optional amd configuration file map section structure 1024 */ 1025void 1026root_newmap(const char *dir, const char *opts, const char *map, const cf_map_t *cfm) 1027{ 1028 char str[MAXPATHLEN]; 1029 1030 /* 1031 * First make sure we have a root map to talk about... 1032 */ 1033 if (!root_map) 1034 root_map = mapc_find(ROOT_MAP, "mapdefault", NULL); 1035 1036 /* 1037 * Then add the entry... 1038 */ 1039 1040 /* 1041 * Here I plug in the code to process other amd.conf options like 1042 * map_type, search_path, and flags (browsable_dirs, mount_type). 1043 */ 1044 1045 if (cfm) { 1046 if (map) { 1047 xsnprintf(str, sizeof(str), 1048 "cache:=mapdefault;type:=toplvl;mount_type:=%s;fs:=\"%s\"", 1049 cfm->cfm_flags & CFM_MOUNT_TYPE_AUTOFS ? "autofs" : "nfs", 1050 get_full_path(map, cfm->cfm_search_path, cfm->cfm_type)); 1051 if (opts && opts[0] != '\0') { 1052 xstrlcat(str, ";", sizeof(str)); 1053 xstrlcat(str, opts, sizeof(str)); 1054 } 1055 if (cfm->cfm_flags & CFM_BROWSABLE_DIRS_FULL) 1056 xstrlcat(str, ";opts:=rw,fullybrowsable", sizeof(str)); 1057 if (cfm->cfm_flags & CFM_BROWSABLE_DIRS) 1058 xstrlcat(str, ";opts:=rw,browsable", sizeof(str)); 1059 if (cfm->cfm_type) { 1060 xstrlcat(str, ";maptype:=", sizeof(str)); 1061 xstrlcat(str, cfm->cfm_type, sizeof(str)); 1062 } 1063 } else { 1064 xstrlcpy(str, opts, sizeof(str)); 1065 } 1066 } else { 1067 if (map) 1068 xsnprintf(str, sizeof(str), 1069 "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s", 1070 map, opts ? opts : ""); 1071 else 1072 xstrlcpy(str, opts, sizeof(str)); 1073 } 1074 mapc_repl_kv(root_map, strdup((char *)dir), strdup(str)); 1075} 1076 1077 1078int 1079mapc_keyiter(mnt_map *m, key_fun *fn, opaque_t arg) 1080{ 1081 int i; 1082 int c = 0; 1083 1084 for (i = 0; i < NKVHASH; i++) { 1085 kv *k = m->kvhash[i]; 1086 while (k) { 1087 (*fn) (k->key, arg); 1088 k = k->next; 1089 c++; 1090 } 1091 } 1092 1093 return c; 1094} 1095 1096 1097/* 1098 * Iterate on the root map and call (*fn)() on the key of all the nodes. 1099 * Returns the number of entries in the root map. 1100 */ 1101int 1102root_keyiter(key_fun *fn, opaque_t arg) 1103{ 1104 if (root_map) { 1105 int c = mapc_keyiter(root_map, fn, arg); 1106 return c; 1107 } 1108 1109 return 0; 1110} 1111 1112 1113/* 1114 * Error map 1115 */ 1116static int 1117error_init(mnt_map *m, char *map, time_t *tp) 1118{ 1119 plog(XLOG_USER, "No source data for map %s", map); 1120 *tp = 0; 1121 1122 return 0; 1123} 1124 1125 1126static int 1127error_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) 1128{ 1129 return ENOENT; 1130} 1131 1132 1133static int 1134error_reload(mnt_map *m, char *map, add_fn *fn) 1135{ 1136 return ENOENT; 1137} 1138 1139 1140static int 1141error_mtime(mnt_map *m, char *map, time_t *tp) 1142{ 1143 *tp = 0; 1144 1145 return 0; 1146} 1147 1148 1149/* 1150 * Return absolute path of map, searched in a type-specific path. 1151 * Note: uses a static buffer for returned data. 1152 */ 1153static const char * 1154get_full_path(const char *map, const char *path, const char *type) 1155{ 1156 char component[MAXPATHLEN], *str; 1157 static char full_path[MAXPATHLEN]; 1158 int len; 1159 1160 /* for now, only file-type search paths are implemented */ 1161 if (type && !STREQ(type, "file")) 1162 return map; 1163 1164 /* if null map, return it */ 1165 if (!map) 1166 return map; 1167 1168 /* if map includes a '/', return it (absolute or relative path) */ 1169 if (strchr(map, '/')) 1170 return map; 1171 1172 /* if path is empty, return map */ 1173 if (!path) 1174 return map; 1175 1176 /* now break path into components, and search in each */ 1177 xstrlcpy(component, path, sizeof(component)); 1178 1179 str = strtok(component, ":"); 1180 do { 1181 xstrlcpy(full_path, str, sizeof(full_path)); 1182 len = strlen(full_path); 1183 if (full_path[len - 1] != '/') /* add trailing "/" if needed */ 1184 xstrlcat(full_path, "/", sizeof(full_path)); 1185 xstrlcat(full_path, map, sizeof(full_path)); 1186 if (access(full_path, R_OK) == 0) 1187 return full_path; 1188 str = strtok(NULL, ":"); 1189 } while (str); 1190 1191 return map; /* if found nothing, return map */ 1192} 1193