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