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