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