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