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