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