Home | History | Annotate | Line # | Download | only in autofs
common.c revision 1.1
      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 (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.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(&copy, "/");
    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