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