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