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