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