1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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/types.h> 33 #include <sys/time.h> 34 #include <sys/ioctl.h> 35 #include <sys/param.h> 36 #include <sys/linker.h> 37 #include <sys/mount.h> 38 #include <sys/socket.h> 39 #include <sys/stat.h> 40 #include <sys/wait.h> 41 #include <sys/utsname.h> 42 #include <assert.h> 43 #include <ctype.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <libgen.h> 48 #include <libutil.h> 49 #include <netdb.h> 50 #include <paths.h> 51 #include <signal.h> 52 #include <stdbool.h> 53 #include <stdint.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 #include "autofs_ioctl.h" 60 61 #include "common.h" 62 63 extern FILE *yyin; 64 extern char *yytext; 65 extern int yylex(void); 66 67 static void parse_master_yyin(struct node *root, const char *master); 68 static void parse_map_yyin(struct node *parent, const char *map, 69 const char *executable_key); 70 71 char * 72 checked_strdup(const char *s) 73 { 74 char *c; 75 76 assert(s != NULL); 77 78 c = strdup(s); 79 if (c == NULL) 80 log_err(1, "strdup"); 81 return (c); 82 } 83 84 /* 85 * Concatenate two strings, inserting separator between them, unless not needed. 86 */ 87 char * 88 concat(const char *s1, char separator, const char *s2) 89 { 90 char *result; 91 char s1last, s2first; 92 int ret; 93 94 if (s1 == NULL) 95 s1 = ""; 96 if (s2 == NULL) 97 s2 = ""; 98 99 if (s1[0] == '\0') 100 s1last = '\0'; 101 else 102 s1last = s1[strlen(s1) - 1]; 103 104 s2first = s2[0]; 105 106 if (s1last == separator && s2first == separator) { 107 /* 108 * If s1 ends with the separator and s2 begins with 109 * it - skip the latter; otherwise concatenating "/" 110 * and "/foo" would end up returning "//foo". 111 */ 112 ret = asprintf(&result, "%s%s", s1, s2 + 1); 113 } else if (s1last == separator || s2first == separator || 114 s1[0] == '\0' || s2[0] == '\0') { 115 ret = asprintf(&result, "%s%s", s1, s2); 116 } else { 117 ret = asprintf(&result, "%s%c%s", s1, separator, s2); 118 } 119 if (ret < 0) 120 log_err(1, "asprintf"); 121 122 //log_debugx("%s: got %s and %s, returning %s", __func__, s1, s2, result); 123 124 return (result); 125 } 126 127 void 128 create_directory(const char *path) 129 { 130 char *component, *copy, *tofree, *partial, *tmp; 131 int error; 132 133 assert(path[0] == '/'); 134 135 /* 136 * +1 to skip the leading slash. 137 */ 138 copy = tofree = checked_strdup(path + 1); 139 140 partial = checked_strdup("/"); 141 for (;;) { 142 component = strsep(©, "/"); 143 if (component == NULL) 144 break; 145 tmp = concat(partial, '/', component); 146 free(partial); 147 partial = tmp; 148 //log_debugx("creating \"%s\"", partial); 149 error = mkdir(partial, 0755); 150 if (error != 0 && errno != EEXIST) { 151 log_warn("cannot create %s", partial); 152 return; 153 } 154 } 155 156 free(tofree); 157 } 158 159 struct node * 160 node_new_root(void) 161 { 162 struct node *n; 163 164 n = calloc(1, sizeof(*n)); 165 if (n == NULL) 166 log_err(1, "calloc"); 167 // XXX 168 n->n_key = checked_strdup("/"); 169 n->n_options = checked_strdup(""); 170 171 TAILQ_INIT(&n->n_children); 172 173 return (n); 174 } 175 176 struct node * 177 node_new(struct node *parent, char *key, char *options, char *location, 178 const char *config_file, int config_line) 179 { 180 struct node *n; 181 182 n = calloc(1, sizeof(*n)); 183 if (n == NULL) 184 log_err(1, "calloc"); 185 186 TAILQ_INIT(&n->n_children); 187 assert(key != NULL); 188 assert(key[0] != '\0'); 189 n->n_key = key; 190 if (options != NULL) 191 n->n_options = options; 192 else 193 n->n_options = strdup(""); 194 n->n_location = location; 195 assert(config_file != NULL); 196 n->n_config_file = config_file; 197 assert(config_line >= 0); 198 n->n_config_line = config_line; 199 200 assert(parent != NULL); 201 n->n_parent = parent; 202 TAILQ_INSERT_TAIL(&parent->n_children, n, n_next); 203 204 return (n); 205 } 206 207 struct node * 208 node_new_map(struct node *parent, char *key, char *options, char *map, 209 const char *config_file, int config_line) 210 { 211 struct node *n; 212 213 n = calloc(1, sizeof(*n)); 214 if (n == NULL) 215 log_err(1, "calloc"); 216 217 TAILQ_INIT(&n->n_children); 218 assert(key != NULL); 219 assert(key[0] != '\0'); 220 n->n_key = key; 221 if (options != NULL) 222 n->n_options = options; 223 else 224 n->n_options = strdup(""); 225 n->n_map = map; 226 assert(config_file != NULL); 227 n->n_config_file = config_file; 228 assert(config_line >= 0); 229 n->n_config_line = config_line; 230 231 assert(parent != NULL); 232 n->n_parent = parent; 233 TAILQ_INSERT_TAIL(&parent->n_children, n, n_next); 234 235 return (n); 236 } 237 238 static struct node * 239 node_duplicate(const struct node *o, struct node *parent) 240 { 241 const struct node *child; 242 struct node *n; 243 244 if (parent == NULL) 245 parent = o->n_parent; 246 247 n = node_new(parent, o->n_key, o->n_options, o->n_location, 248 o->n_config_file, o->n_config_line); 249 250 TAILQ_FOREACH(child, &o->n_children, n_next) 251 node_duplicate(child, n); 252 253 return (n); 254 } 255 256 static void 257 node_delete(struct node *n) 258 { 259 struct node *child, *tmp; 260 261 assert (n != NULL); 262 263 TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp) 264 node_delete(child); 265 266 if (n->n_parent != NULL) 267 TAILQ_REMOVE(&n->n_parent->n_children, n, n_next); 268 269 free(n); 270 } 271 272 /* 273 * Move (reparent) node 'n' to make it sibling of 'previous', placed 274 * just after it. 275 */ 276 static void 277 node_move_after(struct node *n, struct node *previous) 278 { 279 280 TAILQ_REMOVE(&n->n_parent->n_children, n, n_next); 281 n->n_parent = previous->n_parent; 282 TAILQ_INSERT_AFTER(&previous->n_parent->n_children, previous, n, n_next); 283 } 284 285 static void 286 node_expand_includes(struct node *root, bool is_master) 287 { 288 struct node *n, *n2, *tmp, *tmp2, *tmproot; 289 int error; 290 291 TAILQ_FOREACH_SAFE(n, &root->n_children, n_next, tmp) { 292 if (n->n_key[0] != '+') 293 continue; 294 295 error = access(AUTO_INCLUDE_PATH, F_OK); 296 if (error != 0) { 297 log_errx(1, "directory services not configured; " 298 "%s does not exist", AUTO_INCLUDE_PATH); 299 } 300 301 /* 302 * "+1" to skip leading "+". 303 */ 304 yyin = auto_popen(AUTO_INCLUDE_PATH, n->n_key + 1, NULL); 305 assert(yyin != NULL); 306 307 tmproot = node_new_root(); 308 if (is_master) 309 parse_master_yyin(tmproot, n->n_key); 310 else 311 parse_map_yyin(tmproot, n->n_key, NULL); 312 313 error = auto_pclose(yyin); 314 yyin = NULL; 315 if (error != 0) { 316 log_errx(1, "failed to handle include \"%s\"", 317 n->n_key); 318 } 319 320 /* 321 * Entries to be included are now in tmproot. We need to merge 322 * them with the rest, preserving their place and ordering. 323 */ 324 TAILQ_FOREACH_REVERSE_SAFE(n2, 325 &tmproot->n_children, nodehead, n_next, tmp2) { 326 node_move_after(n2, n); 327 } 328 329 node_delete(n); 330 node_delete(tmproot); 331 } 332 } 333 334 static char * 335 expand_ampersand(char *string, const char *key) 336 { 337 char c, *expanded; 338 int i, ret, before_len = 0; 339 bool backslashed = false; 340 341 assert(key[0] != '\0'); 342 343 expanded = checked_strdup(string); 344 345 for (i = 0; string[i] != '\0'; i++) { 346 c = string[i]; 347 if (c == '\\' && backslashed == false) { 348 backslashed = true; 349 continue; 350 } 351 if (backslashed) { 352 backslashed = false; 353 continue; 354 } 355 backslashed = false; 356 if (c != '&') 357 continue; 358 359 /* 360 * The 'before_len' variable contains the number 361 * of characters before the '&'. 362 */ 363 before_len = i; 364 //assert(i < (int)strlen(string)); 365 366 ret = asprintf(&expanded, "%.*s%s%s", 367 before_len, string, key, string + before_len + 1); 368 if (ret < 0) 369 log_err(1, "asprintf"); 370 371 //log_debugx("\"%s\" expanded with key \"%s\" to \"%s\"", 372 // string, key, expanded); 373 374 /* 375 * Figure out where to start searching for next variable. 376 */ 377 string = expanded; 378 i = before_len + strlen(key); 379 if (i == (int)strlen(string)) 380 break; 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 /* 1205 * Applicable to NFSv3 only, see rpc.umntall(8). 1206 */ 1207 void 1208 rpc_umntall(void) 1209 { 1210 FILE *f; 1211 1212 f = auto_popen("rpc.umntall", "-k", NULL); 1213 assert(f != NULL); 1214 auto_pclose(f); 1215 } 1216 1217 int 1218 main(int argc, char **argv) 1219 { 1220 char *cmdname; 1221 1222 if (argv[0] == NULL) 1223 log_errx(1, "NULL command name"); 1224 1225 cmdname = basename(argv[0]); 1226 1227 if (strcmp(cmdname, "automount") == 0) 1228 return (main_automount(argc, argv)); 1229 else if (strcmp(cmdname, "automountd") == 0) 1230 return (main_automountd(argc, argv)); 1231 else if (strcmp(cmdname, "autounmountd") == 0) 1232 return (main_autounmountd(argc, argv)); 1233 else 1234 log_errx(1, "binary name should be either \"automount\", " 1235 "\"automountd\", or \"autounmountd\""); 1236 } 1237