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