common.c revision 270901
1/*- 2 * Copyright (c) 2014 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Edward Tomasz Napierala under sponsorship 6 * from the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: stable/10/usr.sbin/autofs/common.c 270901 2014-08-31 21:53:42Z trasz $"); 33 34#include <sys/types.h> 35#include <sys/time.h> 36#include <sys/ioctl.h> 37#include <sys/param.h> 38#include <sys/linker.h> 39#include <sys/mount.h> 40#include <sys/socket.h> 41#include <sys/stat.h> 42#include <sys/wait.h> 43#include <sys/utsname.h> 44#include <assert.h> 45#include <ctype.h> 46#include <err.h> 47#include <errno.h> 48#include <fcntl.h> 49#include <libgen.h> 50#include <netdb.h> 51#include <paths.h> 52#include <signal.h> 53#include <stdbool.h> 54#include <stdint.h> 55#include <stdio.h> 56#include <stdlib.h> 57#include <string.h> 58#include <unistd.h> 59 60#include <libutil.h> 61 62#include "autofs_ioctl.h" 63 64#include "common.h" 65 66extern FILE *yyin; 67extern char *yytext; 68extern int yylex(void); 69 70static void parse_master_yyin(struct node *root, const char *master); 71static void parse_map_yyin(struct node *parent, const char *map, 72 const char *executable_key); 73 74char * 75checked_strdup(const char *s) 76{ 77 char *c; 78 79 assert(s != NULL); 80 81 c = strdup(s); 82 if (c == NULL) 83 log_err(1, "strdup"); 84 return (c); 85} 86 87/* 88 * Take two pointers to strings, concatenate the contents with "/" in the 89 * middle, make the first pointer point to the result, the second pointer 90 * to NULL, and free the old strings. 91 * 92 * Concatenate pathnames, basically. 93 */ 94static void 95concat(char **p1, char **p2) 96{ 97 int ret; 98 char *path; 99 100 assert(p1 != NULL); 101 assert(p2 != NULL); 102 103 if (*p1 == NULL) 104 *p1 = checked_strdup(""); 105 106 if (*p2 == NULL) 107 *p2 = checked_strdup(""); 108 109 ret = asprintf(&path, "%s/%s", *p1, *p2); 110 if (ret < 0) 111 log_err(1, "asprintf"); 112 113 /* 114 * XXX 115 */ 116 //free(*p1); 117 //free(*p2); 118 119 *p1 = path; 120 *p2 = NULL; 121} 122 123/* 124 * Concatenate two strings, inserting separator between them, unless not needed. 125 * 126 * This function is very convenient to use when you do not care about freeing 127 * memory - which is okay here, because we are a short running process. 128 */ 129char * 130separated_concat(const char *s1, const char *s2, char separator) 131{ 132 char *result; 133 int ret; 134 135 assert(s1 != NULL); 136 assert(s2 != NULL); 137 138 if (s1[0] == '\0' || s2[0] == '\0' || 139 s1[strlen(s1) - 1] == separator || s2[0] == separator) { 140 ret = asprintf(&result, "%s%s", s1, s2); 141 } else { 142 ret = asprintf(&result, "%s%c%s", s1, separator, s2); 143 } 144 if (ret < 0) 145 log_err(1, "asprintf"); 146 147 //log_debugx("separated_concat: got %s and %s, returning %s", s1, s2, result); 148 149 return (result); 150} 151 152void 153create_directory(const char *path) 154{ 155 char *component, *copy, *tofree, *partial; 156 int error; 157 158 assert(path[0] == '/'); 159 160 /* 161 * +1 to skip the leading slash. 162 */ 163 copy = tofree = checked_strdup(path + 1); 164 165 partial = NULL; 166 for (;;) { 167 component = strsep(©, "/"); 168 if (component == NULL) 169 break; 170 concat(&partial, &component); 171 //log_debugx("checking \"%s\" for existence", partial); 172 error = access(partial, F_OK); 173 if (error == 0) 174 continue; 175 if (errno != ENOENT) 176 log_err(1, "cannot access %s", partial); 177 log_debugx("directory %s does not exist, creating", 178 partial); 179 error = mkdir(partial, 0755); 180 if (error != 0) 181 log_err(1, "cannot create %s", partial); 182 } 183 184 free(tofree); 185} 186 187struct node * 188node_new_root(void) 189{ 190 struct node *n; 191 192 n = calloc(1, sizeof(*n)); 193 if (n == NULL) 194 log_err(1, "calloc"); 195 // XXX 196 n->n_key = checked_strdup("/"); 197 n->n_options = checked_strdup(""); 198 199 TAILQ_INIT(&n->n_children); 200 201 return (n); 202} 203 204struct node * 205node_new(struct node *parent, char *key, char *options, char *location, 206 const char *config_file, int config_line) 207{ 208 struct node *n; 209 210 n = calloc(1, sizeof(*n)); 211 if (n == NULL) 212 log_err(1, "calloc"); 213 214 TAILQ_INIT(&n->n_children); 215 assert(key != NULL); 216 n->n_key = key; 217 if (options != NULL) 218 n->n_options = options; 219 else 220 n->n_options = strdup(""); 221 n->n_location = location; 222 assert(config_file != NULL); 223 n->n_config_file = config_file; 224 assert(config_line >= 0); 225 n->n_config_line = config_line; 226 227 assert(parent != NULL); 228 n->n_parent = parent; 229 TAILQ_INSERT_TAIL(&parent->n_children, n, n_next); 230 231 return (n); 232} 233 234struct node * 235node_new_map(struct node *parent, char *key, char *options, char *map, 236 const char *config_file, int config_line) 237{ 238 struct node *n; 239 240 n = calloc(1, sizeof(*n)); 241 if (n == NULL) 242 log_err(1, "calloc"); 243 244 TAILQ_INIT(&n->n_children); 245 assert(key != NULL); 246 n->n_key = key; 247 if (options != NULL) 248 n->n_options = options; 249 else 250 n->n_options = strdup(""); 251 n->n_map = map; 252 assert(config_file != NULL); 253 n->n_config_file = config_file; 254 assert(config_line >= 0); 255 n->n_config_line = config_line; 256 257 assert(parent != NULL); 258 n->n_parent = parent; 259 TAILQ_INSERT_TAIL(&parent->n_children, n, n_next); 260 261 return (n); 262} 263 264static struct node * 265node_duplicate(const struct node *o, struct node *parent) 266{ 267 const struct node *child; 268 struct node *n; 269 270 if (parent == NULL) 271 parent = o->n_parent; 272 273 n = node_new(parent, o->n_key, o->n_options, o->n_location, 274 o->n_config_file, o->n_config_line); 275 276 TAILQ_FOREACH(child, &o->n_children, n_next) 277 node_duplicate(child, n); 278 279 return (n); 280} 281 282static void 283node_delete(struct node *n) 284{ 285 struct node *child, *tmp; 286 287 assert (n != NULL); 288 289 TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp) 290 node_delete(child); 291 292 if (n->n_parent != NULL) 293 TAILQ_REMOVE(&n->n_parent->n_children, n, n_next); 294 295 free(n); 296} 297 298/* 299 * Move (reparent) node 'n' to make it sibling of 'previous', placed 300 * just after it. 301 */ 302static void 303node_move_after(struct node *n, struct node *previous) 304{ 305 306 TAILQ_REMOVE(&n->n_parent->n_children, n, n_next); 307 n->n_parent = previous->n_parent; 308 TAILQ_INSERT_AFTER(&previous->n_parent->n_children, previous, n, n_next); 309} 310 311static void 312node_expand_includes(struct node *root, bool is_master) 313{ 314 struct node *n, *n2, *tmp, *tmp2, *tmproot; 315 int error; 316 317 TAILQ_FOREACH_SAFE(n, &root->n_children, n_next, tmp) { 318 if (n->n_key[0] != '+') 319 continue; 320 321 error = access(AUTO_INCLUDE_PATH, F_OK); 322 if (error != 0) { 323 log_errx(1, "directory services not configured; " 324 "%s does not exist", AUTO_INCLUDE_PATH); 325 } 326 327 /* 328 * "+1" to skip leading "+". 329 */ 330 yyin = auto_popen(AUTO_INCLUDE_PATH, n->n_key + 1, NULL); 331 assert(yyin != NULL); 332 333 tmproot = node_new_root(); 334 if (is_master) 335 parse_master_yyin(tmproot, n->n_key); 336 else 337 parse_map_yyin(tmproot, n->n_key, NULL); 338 339 error = auto_pclose(yyin); 340 yyin = NULL; 341 if (error != 0) { 342 log_errx(1, "failed to handle include \"%s\"", 343 n->n_key); 344 } 345 346 /* 347 * Entries to be included are now in tmproot. We need to merge 348 * them with the rest, preserving their place and ordering. 349 */ 350 TAILQ_FOREACH_REVERSE_SAFE(n2, 351 &tmproot->n_children, nodehead, n_next, tmp2) { 352 node_move_after(n2, n); 353 } 354 355 node_delete(n); 356 node_delete(tmproot); 357 } 358} 359 360static char * 361expand_ampersand(char *string, const char *key) 362{ 363 char c, *expanded; 364 int i, ret, before_len = 0; 365 bool backslashed = false; 366 367 assert(key[0] != '\0'); 368 369 expanded = checked_strdup(string); 370 371 for (i = 0; string[i] != '\0'; i++) { 372 c = string[i]; 373 if (c == '\\' && backslashed == false) { 374 backslashed = true; 375 continue; 376 } 377 if (backslashed) { 378 backslashed = false; 379 continue; 380 } 381 backslashed = false; 382 if (c != '&') 383 continue; 384 385 /* 386 * The 'before_len' variable contains the number 387 * of characters before the '&'. 388 */ 389 before_len = i; 390 //assert(i + 1 < (int)strlen(string)); 391 392 ret = asprintf(&expanded, "%.*s%s%s", 393 before_len, string, key, string + before_len + 1); 394 if (ret < 0) 395 log_err(1, "asprintf"); 396 397 //log_debugx("\"%s\" expanded with key \"%s\" to \"%s\"", 398 // string, key, expanded); 399 400 /* 401 * Figure out where to start searching for next variable. 402 */ 403 string = expanded; 404 i = before_len + strlen(key); 405 backslashed = false; 406 //assert(i < (int)strlen(string)); 407 } 408 409 return (expanded); 410} 411 412/* 413 * Expand "&" in n_location. If the key is NULL, try to use 414 * key from map entries themselves. Keep in mind that maps 415 * consist of tho levels of node structures, the key is one 416 * level up. 417 * 418 * Variant with NULL key is for "automount -LL". 419 */ 420void 421node_expand_ampersand(struct node *n, const char *key) 422{ 423 struct node *child; 424 425 if (n->n_location != NULL) { 426 if (key == NULL) { 427 if (n->n_parent != NULL && 428 strcmp(n->n_parent->n_key, "*") != 0) { 429 n->n_location = expand_ampersand(n->n_location, 430 n->n_parent->n_key); 431 } 432 } else { 433 n->n_location = expand_ampersand(n->n_location, key); 434 } 435 } 436 437 TAILQ_FOREACH(child, &n->n_children, n_next) 438 node_expand_ampersand(child, key); 439} 440 441/* 442 * Expand "*" in n_key. 443 */ 444void 445node_expand_wildcard(struct node *n, const char *key) 446{ 447 struct node *child, *expanded; 448 449 assert(key != NULL); 450 451 if (strcmp(n->n_key, "*") == 0) { 452 expanded = node_duplicate(n, NULL); 453 expanded->n_key = checked_strdup(key); 454 node_move_after(expanded, n); 455 } 456 457 TAILQ_FOREACH(child, &n->n_children, n_next) 458 node_expand_wildcard(child, key); 459} 460 461int 462node_expand_defined(struct node *n) 463{ 464 struct node *child; 465 int error, cumulated_error = 0; 466 467 if (n->n_location != NULL) { 468 n->n_location = defined_expand(n->n_location); 469 if (n->n_location == NULL) { 470 log_warnx("failed to expand location for %s", 471 node_path(n)); 472 return (EINVAL); 473 } 474 } 475 476 TAILQ_FOREACH(child, &n->n_children, n_next) { 477 error = node_expand_defined(child); 478 if (error != 0 && cumulated_error == 0) 479 cumulated_error = error; 480 } 481 482 return (cumulated_error); 483} 484 485bool 486node_is_direct_map(const struct node *n) 487{ 488 489 for (;;) { 490 assert(n->n_parent != NULL); 491 if (n->n_parent->n_parent == NULL) 492 break; 493 n = n->n_parent; 494 } 495 496 assert(n->n_key != NULL); 497 if (strcmp(n->n_key, "/-") != 0) 498 return (false); 499 500 return (true); 501} 502 503static void 504node_expand_maps(struct node *n, bool indirect) 505{ 506 struct node *child, *tmp; 507 508 TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp) { 509 if (node_is_direct_map(child)) { 510 if (indirect) 511 continue; 512 } else { 513 if (indirect == false) 514 continue; 515 } 516 517 /* 518 * This is the first-level map node; the one that contains 519 * the key and subnodes with mountpoints and actual map names. 520 */ 521 if (child->n_map == NULL) 522 continue; 523 524 if (indirect) { 525 log_debugx("map \"%s\" is an indirect map, parsing", 526 child->n_map); 527 } else { 528 log_debugx("map \"%s\" is a direct map, parsing", 529 child->n_map); 530 } 531 parse_map(child, child->n_map, NULL); 532 } 533} 534 535static void 536node_expand_direct_maps(struct node *n) 537{ 538 539 node_expand_maps(n, false); 540} 541 542void 543node_expand_indirect_maps(struct node *n) 544{ 545 546 node_expand_maps(n, true); 547} 548 549static char * 550node_path_x(const struct node *n, char *x) 551{ 552 char *path; 553 size_t len; 554 555 if (n->n_parent == NULL) 556 return (x); 557 558 /* 559 * Return "/-" for direct maps only if we were asked for path 560 * to the "/-" node itself, not to any of its subnodes. 561 */ 562 if (n->n_parent->n_parent == NULL && 563 strcmp(n->n_key, "/-") == 0 && 564 x[0] != '\0') { 565 return (x); 566 } 567 568 path = separated_concat(n->n_key, x, '/'); 569 free(x); 570 571 /* 572 * Strip trailing slash. 573 */ 574 len = strlen(path); 575 assert(len > 0); 576 if (path[len - 1] == '/') 577 path[len - 1] = '\0'; 578 579 return (node_path_x(n->n_parent, path)); 580} 581 582/* 583 * Return full path for node, consisting of concatenated 584 * paths of node itself and all its parents, up to the root. 585 */ 586char * 587node_path(const struct node *n) 588{ 589 590 return (node_path_x(n, checked_strdup(""))); 591} 592 593static char * 594node_options_x(const struct node *n, char *x) 595{ 596 char *options; 597 598 options = separated_concat(x, n->n_options, ','); 599 if (n->n_parent == NULL) 600 return (options); 601 602 return (node_options_x(n->n_parent, options)); 603} 604 605/* 606 * Return options for node, consisting of concatenated 607 * options from the node itself and all its parents, 608 * up to the root. 609 */ 610char * 611node_options(const struct node *n) 612{ 613 614 return (node_options_x(n, checked_strdup(""))); 615} 616 617static void 618node_print_indent(const struct node *n, int indent) 619{ 620 const struct node *child, *first_child; 621 char *path, *options; 622 623 path = node_path(n); 624 options = node_options(n); 625 626 /* 627 * Do not show both parent and child node if they have the same 628 * mountpoint; only show the child node. This means the typical, 629 * "key location", map entries are shown in a single line; 630 * the "key mountpoint1 location2 mountpoint2 location2" entries 631 * take multiple lines. 632 */ 633 first_child = TAILQ_FIRST(&n->n_children); 634 if (first_child == NULL || TAILQ_NEXT(first_child, n_next) != NULL || 635 strcmp(path, node_path(first_child)) != 0) { 636 assert(n->n_location == NULL || n->n_map == NULL); 637 printf("%*.s%-*s %s%-*s %-*s # %s map %s at %s:%d\n", 638 indent, "", 639 25 - indent, 640 path, 641 options[0] != '\0' ? "-" : " ", 642 20, 643 options[0] != '\0' ? options : "", 644 20, 645 n->n_location != NULL ? n->n_location : n->n_map != NULL ? n->n_map : "", 646 node_is_direct_map(n) ? "direct" : "indirect", 647 indent == 0 ? "referenced" : "defined", 648 n->n_config_file, n->n_config_line); 649 } 650 651 free(path); 652 free(options); 653 654 TAILQ_FOREACH(child, &n->n_children, n_next) 655 node_print_indent(child, indent + 2); 656} 657 658void 659node_print(const struct node *n) 660{ 661 const struct node *child; 662 663 TAILQ_FOREACH(child, &n->n_children, n_next) 664 node_print_indent(child, 0); 665} 666 667struct node * 668node_find(struct node *node, const char *path) 669{ 670 struct node *child, *found; 671 char *tmp; 672 673 //log_debugx("looking up %s in %s", path, node->n_key); 674 675 tmp = node_path(node); 676 if (strncmp(tmp, path, strlen(tmp)) != 0) { 677 free(tmp); 678 return (NULL); 679 } 680 free(tmp); 681 682 TAILQ_FOREACH(child, &node->n_children, n_next) { 683 found = node_find(child, path); 684 if (found != NULL) 685 return (found); 686 } 687 688 return (node); 689} 690 691/* 692 * Canonical form of a map entry looks like this: 693 * 694 * key [-options] [ [/mountpoint] [-options2] location ... ] 695 * 696 * Entries for executable maps are slightly different, as they 697 * lack the 'key' field and are always single-line; the key field 698 * for those maps is taken from 'executable_key' argument. 699 * 700 * We parse it in such a way that a map always has two levels - first 701 * for key, and the second, for the mountpoint. 702 */ 703static void 704parse_map_yyin(struct node *parent, const char *map, const char *executable_key) 705{ 706 char *key = NULL, *options = NULL, *mountpoint = NULL, 707 *options2 = NULL, *location = NULL; 708 int ret; 709 struct node *node; 710 711 lineno = 1; 712 713 if (executable_key != NULL) 714 key = checked_strdup(executable_key); 715 716 for (;;) { 717 ret = yylex(); 718 if (ret == 0 || ret == NEWLINE) { 719 /* 720 * In case of executable map, the key is always 721 * non-NULL, even if the map is empty. So, make sure 722 * we don't fail empty maps here. 723 */ 724 if ((key != NULL && executable_key == NULL) || 725 options != NULL) { 726 log_errx(1, "truncated entry at %s, line %d", 727 map, lineno); 728 } 729 if (ret == 0 || executable_key != NULL) { 730 /* 731 * End of file. 732 */ 733 break; 734 } else { 735 key = options = NULL; 736 continue; 737 } 738 } 739 if (key == NULL) { 740 key = checked_strdup(yytext); 741 if (key[0] == '+') { 742 node_new(parent, key, NULL, NULL, map, lineno); 743 key = options = NULL; 744 continue; 745 } 746 continue; 747 } else if (yytext[0] == '-') { 748 if (options != NULL) { 749 log_errx(1, "duplicated options at %s, line %d", 750 map, lineno); 751 } 752 /* 753 * +1 to skip leading "-". 754 */ 755 options = checked_strdup(yytext + 1); 756 continue; 757 } 758 759 /* 760 * We cannot properly handle a situation where the map key 761 * is "/". Ignore such entries. 762 * 763 * XXX: According to Piete Brooks, Linux automounter uses 764 * "/" as a wildcard character in LDAP maps. Perhaps 765 * we should work around this braindamage by substituting 766 * "*" for "/"? 767 */ 768 if (strcmp(key, "/") == 0) { 769 log_warnx("nonsensical map key \"/\" at %s, line %d; " 770 "ignoring map entry ", map, lineno); 771 772 /* 773 * Skip the rest of the entry. 774 */ 775 do { 776 ret = yylex(); 777 } while (ret != 0 && ret != NEWLINE); 778 779 key = options = NULL; 780 continue; 781 } 782 783 //log_debugx("adding map node, %s", key); 784 node = node_new(parent, key, options, NULL, map, lineno); 785 key = options = NULL; 786 787 for (;;) { 788 if (yytext[0] == '/') { 789 if (mountpoint != NULL) { 790 log_errx(1, "duplicated mountpoint " 791 "in %s, line %d", map, lineno); 792 } 793 if (options2 != NULL || location != NULL) { 794 log_errx(1, "mountpoint out of order " 795 "in %s, line %d", map, lineno); 796 } 797 mountpoint = checked_strdup(yytext); 798 goto again; 799 } 800 801 if (yytext[0] == '-') { 802 if (options2 != NULL) { 803 log_errx(1, "duplicated options " 804 "in %s, line %d", map, lineno); 805 } 806 if (location != NULL) { 807 log_errx(1, "options out of order " 808 "in %s, line %d", map, lineno); 809 } 810 options2 = checked_strdup(yytext + 1); 811 goto again; 812 } 813 814 if (location != NULL) { 815 log_errx(1, "too many arguments " 816 "in %s, line %d", map, lineno); 817 } 818 819 /* 820 * If location field starts with colon, e.g. ":/dev/cd0", 821 * then strip it. 822 */ 823 if (yytext[0] == ':') { 824 location = checked_strdup(yytext + 1); 825 if (location[0] == '\0') { 826 log_errx(1, "empty location in %s, " 827 "line %d", map, lineno); 828 } 829 } else { 830 location = checked_strdup(yytext); 831 } 832 833 if (mountpoint == NULL) 834 mountpoint = checked_strdup("/"); 835 if (options2 == NULL) 836 options2 = checked_strdup(""); 837 838#if 0 839 log_debugx("adding map node, %s %s %s", 840 mountpoint, options2, location); 841#endif 842 node_new(node, mountpoint, options2, location, 843 map, lineno); 844 mountpoint = options2 = location = NULL; 845again: 846 ret = yylex(); 847 if (ret == 0 || ret == NEWLINE) { 848 if (mountpoint != NULL || options2 != NULL || 849 location != NULL) { 850 log_errx(1, "truncated entry " 851 "in %s, line %d", map, lineno); 852 } 853 break; 854 } 855 } 856 } 857} 858 859static bool 860file_is_executable(const char *path) 861{ 862 struct stat sb; 863 int error; 864 865 error = stat(path, &sb); 866 if (error != 0) 867 log_err(1, "cannot stat %s", path); 868 if ((sb.st_mode & S_IXUSR) || (sb.st_mode & S_IXGRP) || 869 (sb.st_mode & S_IXOTH)) 870 return (true); 871 return (false); 872} 873 874/* 875 * Parse a special map, e.g. "-hosts". 876 */ 877static void 878parse_special_map(struct node *parent, const char *map, const char *key) 879{ 880 char *path; 881 int error, ret; 882 883 assert(map[0] == '-'); 884 885 if (key == NULL) { 886 log_debugx("skipping map %s due to forced -nobrowse", map); 887 return; 888 } 889 890 /* 891 * +1 to skip leading "-" in map name. 892 */ 893 ret = asprintf(&path, "%s/special_%s", AUTO_SPECIAL_PREFIX, map + 1); 894 if (ret < 0) 895 log_err(1, "asprintf"); 896 897 yyin = auto_popen(path, key, NULL); 898 assert(yyin != NULL); 899 900 parse_map_yyin(parent, map, key); 901 902 error = auto_pclose(yyin); 903 yyin = NULL; 904 if (error != 0) 905 log_errx(1, "failed to handle special map \"%s\"", map); 906 907 node_expand_includes(parent, false); 908 node_expand_direct_maps(parent); 909 910 free(path); 911} 912 913/* 914 * Retrieve and parse map from directory services, e.g. LDAP. 915 * Note that it is different from executable maps, in that 916 * the include script outputs the whole map to standard output 917 * (as opposed to executable maps that only output a single 918 * entry, without the key), and it takes the map name as an 919 * argument, instead of key. 920 */ 921static void 922parse_included_map(struct node *parent, const char *map) 923{ 924 int error; 925 926 assert(map[0] != '-'); 927 assert(map[0] != '/'); 928 929 error = access(AUTO_INCLUDE_PATH, F_OK); 930 if (error != 0) { 931 log_errx(1, "directory services not configured;" 932 " %s does not exist", AUTO_INCLUDE_PATH); 933 } 934 935 yyin = auto_popen(AUTO_INCLUDE_PATH, map, NULL); 936 assert(yyin != NULL); 937 938 parse_map_yyin(parent, map, NULL); 939 940 error = auto_pclose(yyin); 941 yyin = NULL; 942 if (error != 0) 943 log_errx(1, "failed to handle remote map \"%s\"", map); 944 945 node_expand_includes(parent, false); 946 node_expand_direct_maps(parent); 947} 948 949void 950parse_map(struct node *parent, const char *map, const char *key) 951{ 952 char *path = NULL; 953 int error, ret; 954 bool executable; 955 956 assert(map != NULL); 957 assert(map[0] != '\0'); 958 959 log_debugx("parsing map \"%s\"", map); 960 961 if (map[0] == '-') 962 return (parse_special_map(parent, map, key)); 963 964 if (map[0] == '/') { 965 path = checked_strdup(map); 966 } else { 967 ret = asprintf(&path, "%s/%s", AUTO_MAP_PREFIX, map); 968 if (ret < 0) 969 log_err(1, "asprintf"); 970 log_debugx("map \"%s\" maps to \"%s\"", map, path); 971 972 /* 973 * See if the file exists. If not, try to obtain the map 974 * from directory services. 975 */ 976 error = access(path, F_OK); 977 if (error != 0) { 978 log_debugx("map file \"%s\" does not exist; falling " 979 "back to directory services", path); 980 return (parse_included_map(parent, map)); 981 } 982 } 983 984 executable = file_is_executable(path); 985 986 if (executable) { 987 log_debugx("map \"%s\" is executable", map); 988 989 if (key != NULL) { 990 yyin = auto_popen(path, key, NULL); 991 } else { 992 yyin = auto_popen(path, NULL); 993 } 994 assert(yyin != NULL); 995 } else { 996 yyin = fopen(path, "r"); 997 if (yyin == NULL) 998 log_err(1, "unable to open \"%s\"", path); 999 } 1000 1001 free(path); 1002 path = NULL; 1003 1004 parse_map_yyin(parent, map, executable ? key : NULL); 1005 1006 if (executable) { 1007 error = auto_pclose(yyin); 1008 yyin = NULL; 1009 if (error != 0) { 1010 log_errx(1, "failed to handle executable map \"%s\"", 1011 map); 1012 } 1013 } else { 1014 fclose(yyin); 1015 } 1016 yyin = NULL; 1017 1018 log_debugx("done parsing map \"%s\"", map); 1019 1020 node_expand_includes(parent, false); 1021 node_expand_direct_maps(parent); 1022} 1023 1024static void 1025parse_master_yyin(struct node *root, const char *master) 1026{ 1027 char *mountpoint = NULL, *map = NULL, *options = NULL; 1028 int ret; 1029 1030 /* 1031 * XXX: 1 gives incorrect values; wtf? 1032 */ 1033 lineno = 0; 1034 1035 for (;;) { 1036 ret = yylex(); 1037 if (ret == 0 || ret == NEWLINE) { 1038 if (mountpoint != NULL) { 1039 //log_debugx("adding map for %s", mountpoint); 1040 node_new_map(root, mountpoint, options, map, 1041 master, lineno); 1042 } 1043 if (ret == 0) { 1044 break; 1045 } else { 1046 mountpoint = map = options = NULL; 1047 continue; 1048 } 1049 } 1050 if (mountpoint == NULL) { 1051 mountpoint = checked_strdup(yytext); 1052 } else if (map == NULL) { 1053 map = checked_strdup(yytext); 1054 } else if (options == NULL) { 1055 /* 1056 * +1 to skip leading "-". 1057 */ 1058 options = checked_strdup(yytext + 1); 1059 } else { 1060 log_errx(1, "too many arguments at %s, line %d", 1061 master, lineno); 1062 } 1063 } 1064} 1065 1066void 1067parse_master(struct node *root, const char *master) 1068{ 1069 1070 log_debugx("parsing auto_master file at \"%s\"", master); 1071 1072 yyin = fopen(master, "r"); 1073 if (yyin == NULL) 1074 err(1, "unable to open %s", master); 1075 1076 parse_master_yyin(root, master); 1077 1078 fclose(yyin); 1079 yyin = NULL; 1080 1081 log_debugx("done parsing \"%s\"", master); 1082 1083 node_expand_includes(root, true); 1084 node_expand_direct_maps(root); 1085} 1086 1087/* 1088 * Two things daemon(3) does, that we actually also want to do 1089 * when running in foreground, is closing the stdin and chdiring 1090 * to "/". This is what we do here. 1091 */ 1092void 1093lesser_daemon(void) 1094{ 1095 int error, fd; 1096 1097 error = chdir("/"); 1098 if (error != 0) 1099 log_warn("chdir"); 1100 1101 fd = open(_PATH_DEVNULL, O_RDWR, 0); 1102 if (fd < 0) { 1103 log_warn("cannot open %s", _PATH_DEVNULL); 1104 return; 1105 } 1106 1107 error = dup2(fd, STDIN_FILENO); 1108 if (error != 0) 1109 log_warn("dup2"); 1110 1111 error = close(fd); 1112 if (error != 0) { 1113 /* Bloody hell. */ 1114 log_warn("close"); 1115 } 1116} 1117 1118int 1119main(int argc, char **argv) 1120{ 1121 char *cmdname; 1122 1123 if (argv[0] == NULL) 1124 log_errx(1, "NULL command name"); 1125 1126 cmdname = basename(argv[0]); 1127 1128 if (strcmp(cmdname, "automount") == 0) 1129 return (main_automount(argc, argv)); 1130 else if (strcmp(cmdname, "automountd") == 0) 1131 return (main_automountd(argc, argv)); 1132 else if (strcmp(cmdname, "autounmountd") == 0) 1133 return (main_autounmountd(argc, argv)); 1134 else 1135 log_errx(1, "binary name should be either \"automount\", " 1136 "\"automountd\", or \"autounmountd\""); 1137} 1138