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