1 /*- 2 * Copyright (c) 2014 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Edward Tomasz Napierala under sponsorship 6 * from the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <sys/types.h> 35 #include <sys/time.h> 36 #include <sys/ioctl.h> 37 #include <sys/param.h> 38 #include <sys/linker.h> 39 #include <sys/mount.h> 40 #include <sys/socket.h> 41 #include <sys/stat.h> 42 #include <sys/wait.h> 43 #include <sys/utsname.h> 44 #include <assert.h> 45 #include <ctype.h> 46 #include <err.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <libgen.h> 50 #include <libutil.h> 51 #include <netdb.h> 52 #include <paths.h> 53 #include <signal.h> 54 #include <stdbool.h> 55 #include <stdint.h> 56 #define _WITH_GETLINE 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 + 1 < (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 backslashed = false; 383 //assert(i < (int)strlen(string)); 384 } 385 386 return (expanded); 387 } 388 389 /* 390 * Expand "&" in n_location. If the key is NULL, try to use 391 * key from map entries themselves. Keep in mind that maps 392 * consist of tho levels of node structures, the key is one 393 * level up. 394 * 395 * Variant with NULL key is for "automount -LL". 396 */ 397 void 398 node_expand_ampersand(struct node *n, const char *key) 399 { 400 struct node *child; 401 402 if (n->n_location != NULL) { 403 if (key == NULL) { 404 if (n->n_parent != NULL && 405 strcmp(n->n_parent->n_key, "*") != 0) { 406 n->n_location = expand_ampersand(n->n_location, 407 n->n_parent->n_key); 408 } 409 } else { 410 n->n_location = expand_ampersand(n->n_location, key); 411 } 412 } 413 414 TAILQ_FOREACH(child, &n->n_children, n_next) 415 node_expand_ampersand(child, key); 416 } 417 418 /* 419 * Expand "*" in n_key. 420 */ 421 void 422 node_expand_wildcard(struct node *n, const char *key) 423 { 424 struct node *child, *expanded; 425 426 assert(key != NULL); 427 428 if (strcmp(n->n_key, "*") == 0) { 429 expanded = node_duplicate(n, NULL); 430 expanded->n_key = checked_strdup(key); 431 node_move_after(expanded, n); 432 } 433 434 TAILQ_FOREACH(child, &n->n_children, n_next) 435 node_expand_wildcard(child, key); 436 } 437 438 int 439 node_expand_defined(struct node *n) 440 { 441 struct node *child; 442 int error, cumulated_error = 0; 443 444 if (n->n_location != NULL) { 445 n->n_location = defined_expand(n->n_location); 446 if (n->n_location == NULL) { 447 log_warnx("failed to expand location for %s", 448 node_path(n)); 449 return (EINVAL); 450 } 451 } 452 453 TAILQ_FOREACH(child, &n->n_children, n_next) { 454 error = node_expand_defined(child); 455 if (error != 0 && cumulated_error == 0) 456 cumulated_error = error; 457 } 458 459 return (cumulated_error); 460 } 461 462 static bool 463 node_is_direct_key(const struct node *n) 464 { 465 466 if (n->n_parent != NULL && n->n_parent->n_parent == NULL && 467 strcmp(n->n_key, "/-") == 0) { 468 return (true); 469 } 470 471 return (false); 472 } 473 474 bool 475 node_is_direct_map(const struct node *n) 476 { 477 478 for (;;) { 479 assert(n->n_parent != NULL); 480 if (n->n_parent->n_parent == NULL) 481 break; 482 n = n->n_parent; 483 } 484 485 return (node_is_direct_key(n)); 486 } 487 488 bool 489 node_has_wildcards(const struct node *n) 490 { 491 const struct node *child; 492 493 TAILQ_FOREACH(child, &n->n_children, n_next) { 494 if (strcmp(child->n_key, "*") == 0) 495 return (true); 496 } 497 498 return (false); 499 } 500 501 static void 502 node_expand_maps(struct node *n, bool indirect) 503 { 504 struct node *child, *tmp; 505 506 TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp) { 507 if (node_is_direct_map(child)) { 508 if (indirect) 509 continue; 510 } else { 511 if (indirect == false) 512 continue; 513 } 514 515 /* 516 * This is the first-level map node; the one that contains 517 * the key and subnodes with mountpoints and actual map names. 518 */ 519 if (child->n_map == NULL) 520 continue; 521 522 if (indirect) { 523 log_debugx("map \"%s\" is an indirect map, parsing", 524 child->n_map); 525 } else { 526 log_debugx("map \"%s\" is a direct map, parsing", 527 child->n_map); 528 } 529 parse_map(child, child->n_map, NULL, NULL); 530 } 531 } 532 533 static void 534 node_expand_direct_maps(struct node *n) 535 { 536 537 node_expand_maps(n, false); 538 } 539 540 void 541 node_expand_indirect_maps(struct node *n) 542 { 543 544 node_expand_maps(n, true); 545 } 546 547 static char * 548 node_path_x(const struct node *n, char *x) 549 { 550 char *path; 551 552 if (n->n_parent == NULL) 553 return (x); 554 555 /* 556 * Return "/-" for direct maps only if we were asked for path 557 * to the "/-" node itself, not to any of its subnodes. 558 */ 559 if (node_is_direct_key(n) && x[0] != '\0') 560 return (x); 561 562 assert(n->n_key[0] != '\0'); 563 path = concat(n->n_key, '/', x); 564 free(x); 565 566 return (node_path_x(n->n_parent, path)); 567 } 568 569 /* 570 * Return full path for node, consisting of concatenated 571 * paths of node itself and all its parents, up to the root. 572 */ 573 char * 574 node_path(const struct node *n) 575 { 576 char *path; 577 size_t len; 578 579 path = node_path_x(n, checked_strdup("")); 580 581 /* 582 * Strip trailing slash, unless the whole path is "/". 583 */ 584 len = strlen(path); 585 if (len > 1 && path[len - 1] == '/') 586 path[len - 1] = '\0'; 587 588 return (path); 589 } 590 591 static char * 592 node_options_x(const struct node *n, char *x) 593 { 594 char *options; 595 596 if (n == NULL) 597 return (x); 598 599 options = concat(x, ',', n->n_options); 600 free(x); 601 602 return (node_options_x(n->n_parent, options)); 603 } 604 605 /* 606 * Return options for node, consisting of concatenated 607 * options from the node itself and all its parents, 608 * up to the root. 609 */ 610 char * 611 node_options(const struct node *n) 612 { 613 614 return (node_options_x(n, checked_strdup(""))); 615 } 616 617 static void 618 node_print_indent(const struct node *n, const char *cmdline_options, 619 int indent) 620 { 621 const struct node *child, *first_child; 622 char *path, *options, *tmp; 623 624 path = node_path(n); 625 tmp = node_options(n); 626 options = concat(cmdline_options, ',', tmp); 627 free(tmp); 628 629 /* 630 * Do not show both parent and child node if they have the same 631 * mountpoint; only show the child node. This means the typical, 632 * "key location", map entries are shown in a single line; 633 * the "key mountpoint1 location2 mountpoint2 location2" entries 634 * take multiple lines. 635 */ 636 first_child = TAILQ_FIRST(&n->n_children); 637 if (first_child == NULL || TAILQ_NEXT(first_child, n_next) != NULL || 638 strcmp(path, node_path(first_child)) != 0) { 639 assert(n->n_location == NULL || n->n_map == NULL); 640 printf("%*.s%-*s %s%-*s %-*s # %s map %s at %s:%d\n", 641 indent, "", 642 25 - indent, 643 path, 644 options[0] != '\0' ? "-" : " ", 645 20, 646 options[0] != '\0' ? options : "", 647 20, 648 n->n_location != NULL ? n->n_location : n->n_map != NULL ? n->n_map : "", 649 node_is_direct_map(n) ? "direct" : "indirect", 650 indent == 0 ? "referenced" : "defined", 651 n->n_config_file, n->n_config_line); 652 } 653 654 free(path); 655 free(options); 656 657 TAILQ_FOREACH(child, &n->n_children, n_next) 658 node_print_indent(child, cmdline_options, indent + 2); 659 } 660 661 /* 662 * Recursively print node with all its children. The cmdline_options 663 * argument is used for additional options to be prepended to all the 664 * others - usually those are the options passed by command line. 665 */ 666 void 667 node_print(const struct node *n, const char *cmdline_options) 668 { 669 const struct node *child; 670 671 TAILQ_FOREACH(child, &n->n_children, n_next) 672 node_print_indent(child, cmdline_options, 0); 673 } 674 675 static struct node * 676 node_find_x(struct node *node, const char *path) 677 { 678 struct node *child, *found; 679 char *tmp; 680 size_t tmplen; 681 682 //log_debugx("looking up %s in %s", path, node_path(node)); 683 684 if (!node_is_direct_key(node)) { 685 tmp = node_path(node); 686 tmplen = strlen(tmp); 687 if (strncmp(tmp, path, tmplen) != 0) { 688 free(tmp); 689 return (NULL); 690 } 691 if (path[tmplen] != '/' && path[tmplen] != '\0') { 692 /* 693 * If we have two map entries like 'foo' and 'foobar', make 694 * sure the search for 'foobar' won't match 'foo' instead. 695 */ 696 free(tmp); 697 return (NULL); 698 } 699 free(tmp); 700 } 701 702 TAILQ_FOREACH(child, &node->n_children, n_next) { 703 found = node_find_x(child, path); 704 if (found != NULL) 705 return (found); 706 } 707 708 if (node->n_parent == NULL || node_is_direct_key(node)) 709 return (NULL); 710 711 return (node); 712 } 713 714 struct node * 715 node_find(struct node *root, const char *path) 716 { 717 struct node *node; 718 719 assert(root->n_parent == NULL); 720 721 node = node_find_x(root, path); 722 if (node != NULL) 723 assert(node != root); 724 725 return (node); 726 } 727 728 /* 729 * Canonical form of a map entry looks like this: 730 * 731 * key [-options] [ [/mountpoint] [-options2] location ... ] 732 * 733 * Entries for executable maps are slightly different, as they 734 * lack the 'key' field and are always single-line; the key field 735 * for those maps is taken from 'executable_key' argument. 736 * 737 * We parse it in such a way that a map always has two levels - first 738 * for key, and the second, for the mountpoint. 739 */ 740 static void 741 parse_map_yyin(struct node *parent, const char *map, const char *executable_key) 742 { 743 char *key = NULL, *options = NULL, *mountpoint = NULL, 744 *options2 = NULL, *location = NULL; 745 int ret; 746 struct node *node; 747 748 lineno = 1; 749 750 if (executable_key != NULL) 751 key = checked_strdup(executable_key); 752 753 for (;;) { 754 ret = yylex(); 755 if (ret == 0 || ret == NEWLINE) { 756 /* 757 * In case of executable map, the key is always 758 * non-NULL, even if the map is empty. So, make sure 759 * we don't fail empty maps here. 760 */ 761 if ((key != NULL && executable_key == NULL) || 762 options != NULL) { 763 log_errx(1, "truncated entry at %s, line %d", 764 map, lineno); 765 } 766 if (ret == 0 || executable_key != NULL) { 767 /* 768 * End of file. 769 */ 770 break; 771 } else { 772 key = options = NULL; 773 continue; 774 } 775 } 776 if (key == NULL) { 777 key = checked_strdup(yytext); 778 if (key[0] == '+') { 779 node_new(parent, key, NULL, NULL, map, lineno); 780 key = options = NULL; 781 continue; 782 } 783 continue; 784 } else if (yytext[0] == '-') { 785 if (options != NULL) { 786 log_errx(1, "duplicated options at %s, line %d", 787 map, lineno); 788 } 789 /* 790 * +1 to skip leading "-". 791 */ 792 options = checked_strdup(yytext + 1); 793 continue; 794 } 795 796 /* 797 * We cannot properly handle a situation where the map key 798 * is "/". Ignore such entries. 799 * 800 * XXX: According to Piete Brooks, Linux automounter uses 801 * "/" as a wildcard character in LDAP maps. Perhaps 802 * we should work around this braindamage by substituting 803 * "*" for "/"? 804 */ 805 if (strcmp(key, "/") == 0) { 806 log_warnx("nonsensical map key \"/\" at %s, line %d; " 807 "ignoring map entry ", map, lineno); 808 809 /* 810 * Skip the rest of the entry. 811 */ 812 do { 813 ret = yylex(); 814 } while (ret != 0 && ret != NEWLINE); 815 816 key = options = NULL; 817 continue; 818 } 819 820 //log_debugx("adding map node, %s", key); 821 node = node_new(parent, key, options, NULL, map, lineno); 822 key = options = NULL; 823 824 for (;;) { 825 if (yytext[0] == '/') { 826 if (mountpoint != NULL) { 827 log_errx(1, "duplicated mountpoint " 828 "in %s, line %d", map, lineno); 829 } 830 if (options2 != NULL || location != NULL) { 831 log_errx(1, "mountpoint out of order " 832 "in %s, line %d", map, lineno); 833 } 834 mountpoint = checked_strdup(yytext); 835 goto again; 836 } 837 838 if (yytext[0] == '-') { 839 if (options2 != NULL) { 840 log_errx(1, "duplicated options " 841 "in %s, line %d", map, lineno); 842 } 843 if (location != NULL) { 844 log_errx(1, "options out of order " 845 "in %s, line %d", map, lineno); 846 } 847 options2 = checked_strdup(yytext + 1); 848 goto again; 849 } 850 851 if (location != NULL) { 852 log_errx(1, "too many arguments " 853 "in %s, line %d", map, lineno); 854 } 855 856 /* 857 * If location field starts with colon, e.g. ":/dev/cd0", 858 * then strip it. 859 */ 860 if (yytext[0] == ':') { 861 location = checked_strdup(yytext + 1); 862 if (location[0] == '\0') { 863 log_errx(1, "empty location in %s, " 864 "line %d", map, lineno); 865 } 866 } else { 867 location = checked_strdup(yytext); 868 } 869 870 if (mountpoint == NULL) 871 mountpoint = checked_strdup("/"); 872 if (options2 == NULL) 873 options2 = checked_strdup(""); 874 875 #if 0 876 log_debugx("adding map node, %s %s %s", 877 mountpoint, options2, location); 878 #endif 879 node_new(node, mountpoint, options2, location, 880 map, lineno); 881 mountpoint = options2 = location = NULL; 882 again: 883 ret = yylex(); 884 if (ret == 0 || ret == NEWLINE) { 885 if (mountpoint != NULL || options2 != NULL || 886 location != NULL) { 887 log_errx(1, "truncated entry " 888 "in %s, line %d", map, lineno); 889 } 890 break; 891 } 892 } 893 } 894 } 895 896 /* 897 * Parse output of a special map called without argument. It is a list 898 * of keys, separated by newlines. They can contain whitespace, so use 899 * getline(3) instead of lexer used for maps. 900 */ 901 static void 902 parse_map_keys_yyin(struct node *parent, const char *map) 903 { 904 char *line = NULL, *key; 905 size_t linecap = 0; 906 ssize_t linelen; 907 908 lineno = 1; 909 910 for (;;) { 911 linelen = getline(&line, &linecap, yyin); 912 if (linelen < 0) { 913 /* 914 * End of file. 915 */ 916 break; 917 } 918 if (linelen <= 1) { 919 /* 920 * Empty line, consisting of just the newline. 921 */ 922 continue; 923 } 924 925 /* 926 * "-1" to strip the trailing newline. 927 */ 928 key = strndup(line, linelen - 1); 929 930 log_debugx("adding key \"%s\"", key); 931 node_new(parent, key, NULL, NULL, map, lineno); 932 lineno++; 933 } 934 free(line); 935 } 936 937 static bool 938 file_is_executable(const char *path) 939 { 940 struct stat sb; 941 int error; 942 943 error = stat(path, &sb); 944 if (error != 0) 945 log_err(1, "cannot stat %s", path); 946 if ((sb.st_mode & S_IXUSR) || (sb.st_mode & S_IXGRP) || 947 (sb.st_mode & S_IXOTH)) 948 return (true); 949 return (false); 950 } 951 952 /* 953 * Parse a special map, e.g. "-hosts". 954 */ 955 static void 956 parse_special_map(struct node *parent, const char *map, const char *key) 957 { 958 char *path; 959 int error, ret; 960 961 assert(map[0] == '-'); 962 963 /* 964 * +1 to skip leading "-" in map name. 965 */ 966 ret = asprintf(&path, "%s/special_%s", AUTO_SPECIAL_PREFIX, map + 1); 967 if (ret < 0) 968 log_err(1, "asprintf"); 969 970 yyin = auto_popen(path, key, NULL); 971 assert(yyin != NULL); 972 973 if (key == NULL) { 974 parse_map_keys_yyin(parent, map); 975 } else { 976 parse_map_yyin(parent, map, key); 977 } 978 979 error = auto_pclose(yyin); 980 yyin = NULL; 981 if (error != 0) 982 log_errx(1, "failed to handle special map \"%s\"", map); 983 984 node_expand_includes(parent, false); 985 node_expand_direct_maps(parent); 986 987 free(path); 988 } 989 990 /* 991 * Retrieve and parse map from directory services, e.g. LDAP. 992 * Note that it is different from executable maps, in that 993 * the include script outputs the whole map to standard output 994 * (as opposed to executable maps that only output a single 995 * entry, without the key), and it takes the map name as an 996 * argument, instead of key. 997 */ 998 static void 999 parse_included_map(struct node *parent, const char *map) 1000 { 1001 int error; 1002 1003 assert(map[0] != '-'); 1004 assert(map[0] != '/'); 1005 1006 error = access(AUTO_INCLUDE_PATH, F_OK); 1007 if (error != 0) { 1008 log_errx(1, "directory services not configured;" 1009 " %s does not exist", AUTO_INCLUDE_PATH); 1010 } 1011 1012 yyin = auto_popen(AUTO_INCLUDE_PATH, map, NULL); 1013 assert(yyin != NULL); 1014 1015 parse_map_yyin(parent, map, NULL); 1016 1017 error = auto_pclose(yyin); 1018 yyin = NULL; 1019 if (error != 0) 1020 log_errx(1, "failed to handle remote map \"%s\"", map); 1021 1022 node_expand_includes(parent, false); 1023 node_expand_direct_maps(parent); 1024 } 1025 1026 void 1027 parse_map(struct node *parent, const char *map, const char *key, 1028 bool *wildcards) 1029 { 1030 char *path = NULL; 1031 int error, ret; 1032 bool executable; 1033 1034 assert(map != NULL); 1035 assert(map[0] != '\0'); 1036 1037 log_debugx("parsing map \"%s\"", map); 1038 1039 if (wildcards != NULL) 1040 *wildcards = false; 1041 1042 if (map[0] == '-') { 1043 if (wildcards != NULL) 1044 *wildcards = true; 1045 return (parse_special_map(parent, map, key)); 1046 } 1047 1048 if (map[0] == '/') { 1049 path = checked_strdup(map); 1050 } else { 1051 ret = asprintf(&path, "%s/%s", AUTO_MAP_PREFIX, map); 1052 if (ret < 0) 1053 log_err(1, "asprintf"); 1054 log_debugx("map \"%s\" maps to \"%s\"", map, path); 1055 1056 /* 1057 * See if the file exists. If not, try to obtain the map 1058 * from directory services. 1059 */ 1060 error = access(path, F_OK); 1061 if (error != 0) { 1062 log_debugx("map file \"%s\" does not exist; falling " 1063 "back to directory services", path); 1064 return (parse_included_map(parent, map)); 1065 } 1066 } 1067 1068 executable = file_is_executable(path); 1069 1070 if (executable) { 1071 log_debugx("map \"%s\" is executable", map); 1072 1073 if (wildcards != NULL) 1074 *wildcards = true; 1075 1076 if (key != NULL) { 1077 yyin = auto_popen(path, key, NULL); 1078 } else { 1079 yyin = auto_popen(path, NULL); 1080 } 1081 assert(yyin != NULL); 1082 } else { 1083 yyin = fopen(path, "r"); 1084 if (yyin == NULL) 1085 log_err(1, "unable to open \"%s\"", path); 1086 } 1087 1088 free(path); 1089 path = NULL; 1090 1091 parse_map_yyin(parent, map, executable ? key : NULL); 1092 1093 if (executable) { 1094 error = auto_pclose(yyin); 1095 yyin = NULL; 1096 if (error != 0) { 1097 log_errx(1, "failed to handle executable map \"%s\"", 1098 map); 1099 } 1100 } else { 1101 fclose(yyin); 1102 } 1103 yyin = NULL; 1104 1105 log_debugx("done parsing map \"%s\"", map); 1106 1107 node_expand_includes(parent, false); 1108 node_expand_direct_maps(parent); 1109 } 1110 1111 static void 1112 parse_master_yyin(struct node *root, const char *master) 1113 { 1114 char *mountpoint = NULL, *map = NULL, *options = NULL; 1115 int ret; 1116 1117 /* 1118 * XXX: 1 gives incorrect values; wtf? 1119 */ 1120 lineno = 0; 1121 1122 for (;;) { 1123 ret = yylex(); 1124 if (ret == 0 || ret == NEWLINE) { 1125 if (mountpoint != NULL) { 1126 //log_debugx("adding map for %s", mountpoint); 1127 node_new_map(root, mountpoint, options, map, 1128 master, lineno); 1129 } 1130 if (ret == 0) { 1131 break; 1132 } else { 1133 mountpoint = map = options = NULL; 1134 continue; 1135 } 1136 } 1137 if (mountpoint == NULL) { 1138 mountpoint = checked_strdup(yytext); 1139 } else if (map == NULL) { 1140 map = checked_strdup(yytext); 1141 } else if (options == NULL) { 1142 /* 1143 * +1 to skip leading "-". 1144 */ 1145 options = checked_strdup(yytext + 1); 1146 } else { 1147 log_errx(1, "too many arguments at %s, line %d", 1148 master, lineno); 1149 } 1150 } 1151 } 1152 1153 void 1154 parse_master(struct node *root, const char *master) 1155 { 1156 1157 log_debugx("parsing auto_master file at \"%s\"", master); 1158 1159 yyin = fopen(master, "r"); 1160 if (yyin == NULL) 1161 err(1, "unable to open %s", master); 1162 1163 parse_master_yyin(root, master); 1164 1165 fclose(yyin); 1166 yyin = NULL; 1167 1168 log_debugx("done parsing \"%s\"", master); 1169 1170 node_expand_includes(root, true); 1171 node_expand_direct_maps(root); 1172 } 1173 1174 /* 1175 * Two things daemon(3) does, that we actually also want to do 1176 * when running in foreground, is closing the stdin and chdiring 1177 * to "/". This is what we do here. 1178 */ 1179 void 1180 lesser_daemon(void) 1181 { 1182 int error, fd; 1183 1184 error = chdir("/"); 1185 if (error != 0) 1186 log_warn("chdir"); 1187 1188 fd = open(_PATH_DEVNULL, O_RDWR, 0); 1189 if (fd < 0) { 1190 log_warn("cannot open %s", _PATH_DEVNULL); 1191 return; 1192 } 1193 1194 error = dup2(fd, STDIN_FILENO); 1195 if (error != 0) 1196 log_warn("dup2"); 1197 1198 error = close(fd); 1199 if (error != 0) { 1200 /* Bloody hell. */ 1201 log_warn("close"); 1202 } 1203 } 1204 1205 int 1206 main(int argc, char **argv) 1207 { 1208 char *cmdname; 1209 1210 if (argv[0] == NULL) 1211 log_errx(1, "NULL command name"); 1212 1213 cmdname = basename(argv[0]); 1214 1215 if (strcmp(cmdname, "automount") == 0) 1216 return (main_automount(argc, argv)); 1217 else if (strcmp(cmdname, "automountd") == 0) 1218 return (main_automountd(argc, argv)); 1219 else if (strcmp(cmdname, "autounmountd") == 0) 1220 return (main_autounmountd(argc, argv)); 1221 else 1222 log_errx(1, "binary name should be either \"automount\", " 1223 "\"automountd\", or \"autounmountd\""); 1224 } 1225