Home | History | Annotate | Line # | Download | only in autofs
common.c revision 1.2
      1 /*	$NetBSD: common.c,v 1.2 2019/11/16 11:56:29 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.2 2019/11/16 11:56:29 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 + 1 < 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 		backslashed = false;
    376 		//assert(i < strlen(string));
    377 	}
    378 
    379 	return expanded;
    380 }
    381 
    382 /*
    383  * Expand "&" in n_location.  If the key is NULL, try to use
    384  * key from map entries themselves.  Keep in mind that maps
    385  * consist of tho levels of node structures, the key is one
    386  * level up.
    387  *
    388  * Variant with NULL key is for "automount -LL".
    389  */
    390 void
    391 node_expand_ampersand(struct node *n, const char *key)
    392 {
    393 	struct node *child;
    394 
    395 	if (n->n_location != NULL) {
    396 		if (key == NULL) {
    397 			if (n->n_parent != NULL &&
    398 			    strcmp(n->n_parent->n_key, "*") != 0) {
    399 				n->n_location = expand_ampersand(n->n_location,
    400 				    n->n_parent->n_key);
    401 			}
    402 		} else {
    403 			n->n_location = expand_ampersand(n->n_location, key);
    404 		}
    405 	}
    406 
    407 	TAILQ_FOREACH(child, &n->n_children, n_next)
    408 		node_expand_ampersand(child, key);
    409 }
    410 
    411 /*
    412  * Expand "*" in n_key.
    413  */
    414 void
    415 node_expand_wildcard(struct node *n, const char *key)
    416 {
    417 	struct node *child, *expanded;
    418 
    419 	assert(key != NULL);
    420 
    421 	if (strcmp(n->n_key, "*") == 0) {
    422 		expanded = node_duplicate(n, NULL);
    423 		expanded->n_key = checked_strdup(key);
    424 		node_move_after(expanded, n);
    425 	}
    426 
    427 	TAILQ_FOREACH(child, &n->n_children, n_next)
    428 		node_expand_wildcard(child, key);
    429 }
    430 
    431 int
    432 node_expand_defined(struct node *n)
    433 {
    434 	struct node *child;
    435 	int error, cummulative_error = 0;
    436 
    437 	if (n->n_location != NULL) {
    438 		n->n_location = defined_expand(n->n_location);
    439 		if (n->n_location == NULL) {
    440 			log_warnx("failed to expand location for %s",
    441 			    node_path(n));
    442 			return EINVAL;
    443 		}
    444 	}
    445 
    446 	TAILQ_FOREACH(child, &n->n_children, n_next) {
    447 		error = node_expand_defined(child);
    448 		if (error != 0 && cummulative_error == 0)
    449 			cummulative_error = error;
    450 	}
    451 
    452 	return cummulative_error;
    453 }
    454 
    455 static bool
    456 node_is_direct_key(const struct node *n)
    457 {
    458 
    459 	return n->n_parent != NULL && n->n_parent->n_parent == NULL &&
    460 	    strcmp(n->n_key, "/-") == 0;
    461 }
    462 
    463 bool
    464 node_is_direct_map(const struct node *n)
    465 {
    466 
    467 	for (;;) {
    468 		assert(n->n_parent != NULL);
    469 		if (n->n_parent->n_parent == NULL)
    470 			break;
    471 		n = n->n_parent;
    472 	}
    473 
    474 	return node_is_direct_key(n);
    475 }
    476 
    477 bool
    478 node_has_wildcards(const struct node *n)
    479 {
    480 	const struct node *child;
    481 
    482 	TAILQ_FOREACH(child, &n->n_children, n_next) {
    483 		if (strcmp(child->n_key, "*") == 0)
    484 			return true;
    485 	}
    486 
    487 	return false;
    488 }
    489 
    490 static void
    491 node_expand_maps(struct node *n, bool indirect)
    492 {
    493 	struct node *child, *tmp;
    494 
    495 	TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp) {
    496 		if (node_is_direct_map(child)) {
    497 			if (indirect)
    498 				continue;
    499 		} else {
    500 			if (indirect == false)
    501 				continue;
    502 		}
    503 
    504 		/*
    505 		 * This is the first-level map node; the one that contains
    506 		 * the key and subnodes with mountpoints and actual map names.
    507 		 */
    508 		if (child->n_map == NULL)
    509 			continue;
    510 
    511 		if (indirect) {
    512 			log_debugx("map \"%s\" is an indirect map, parsing",
    513 			    child->n_map);
    514 		} else {
    515 			log_debugx("map \"%s\" is a direct map, parsing",
    516 			    child->n_map);
    517 		}
    518 		parse_map(child, child->n_map, NULL, NULL);
    519 	}
    520 }
    521 
    522 static void
    523 node_expand_direct_maps(struct node *n)
    524 {
    525 
    526 	node_expand_maps(n, false);
    527 }
    528 
    529 void
    530 node_expand_indirect_maps(struct node *n)
    531 {
    532 
    533 	node_expand_maps(n, true);
    534 }
    535 
    536 static char *
    537 node_path_x(const struct node *n, char *x)
    538 {
    539 	char *path;
    540 
    541 	if (n->n_parent == NULL)
    542 		return x;
    543 
    544 	/*
    545 	 * Return "/-" for direct maps only if we were asked for path
    546 	 * to the "/-" node itself, not to any of its subnodes.
    547 	 */
    548 	if (node_is_direct_key(n) && x[0] != '\0')
    549 		return x;
    550 
    551 	assert(n->n_key[0] != '\0');
    552 	path = concat(n->n_key, '/', x);
    553 	free(x);
    554 
    555 	return node_path_x(n->n_parent, path);
    556 }
    557 
    558 /*
    559  * Return full path for node, consisting of concatenated
    560  * paths of node itself and all its parents, up to the root.
    561  */
    562 char *
    563 node_path(const struct node *n)
    564 {
    565 	char *path;
    566 	size_t len;
    567 
    568 	path = node_path_x(n, checked_strdup(""));
    569 
    570 	/*
    571 	 * Strip trailing slash, unless the whole path is "/".
    572 	 */
    573 	len = strlen(path);
    574 	if (len > 1 && path[len - 1] == '/')
    575 		path[len - 1] = '\0';
    576 
    577 	return path;
    578 }
    579 
    580 static char *
    581 node_options_x(const struct node *n, char *x)
    582 {
    583 	char *options;
    584 
    585 	if (n == NULL)
    586 		return x;
    587 
    588 	options = concat(x, ',', n->n_options);
    589 	free(x);
    590 
    591 	return node_options_x(n->n_parent, options);
    592 }
    593 
    594 /*
    595  * Return options for node, consisting of concatenated
    596  * options from the node itself and all its parents,
    597  * up to the root.
    598  */
    599 char *
    600 node_options(const struct node *n)
    601 {
    602 
    603 	return node_options_x(n, checked_strdup(""));
    604 }
    605 
    606 static void
    607 node_print_indent(const struct node *n, const char *cmdline_options,
    608     int indent)
    609 {
    610 	const struct node *child, *first_child;
    611 	char *path, *options, *tmp;
    612 
    613 	path = node_path(n);
    614 	tmp = node_options(n);
    615 	options = concat(cmdline_options, ',', tmp);
    616 	free(tmp);
    617 
    618 	/*
    619 	 * Do not show both parent and child node if they have the same
    620 	 * mountpoint; only show the child node.  This means the typical,
    621 	 * "key location", map entries are shown in a single line;
    622 	 * the "key mountpoint1 location2 mountpoint2 location2" entries
    623 	 * take multiple lines.
    624 	 */
    625 	first_child = TAILQ_FIRST(&n->n_children);
    626 	if (first_child == NULL || TAILQ_NEXT(first_child, n_next) != NULL ||
    627 	    strcmp(path, node_path(first_child)) != 0) {
    628 		assert(n->n_location == NULL || n->n_map == NULL);
    629 		printf("%*.s%-*s %s%-*s %-*s # %s map %s at %s:%d\n",
    630 		    indent, "",
    631 		    25 - indent,
    632 		    path,
    633 		    options[0] != '\0' ? "-" : " ",
    634 		    20,
    635 		    options[0] != '\0' ? options : "",
    636 		    20,
    637 		    n->n_location != NULL ? n->n_location : n->n_map != NULL ? n->n_map : "",
    638 		    node_is_direct_map(n) ? "direct" : "indirect",
    639 		    indent == 0 ? "referenced" : "defined",
    640 		    n->n_config_file, n->n_config_line);
    641 	}
    642 
    643 	free(path);
    644 	free(options);
    645 
    646 	TAILQ_FOREACH(child, &n->n_children, n_next)
    647 		node_print_indent(child, cmdline_options, indent + 2);
    648 }
    649 
    650 /*
    651  * Recursively print node with all its children.  The cmdline_options
    652  * argument is used for additional options to be prepended to all the
    653  * others - usually those are the options passed by command line.
    654  */
    655 void
    656 node_print(const struct node *n, const char *cmdline_options)
    657 {
    658 	const struct node *child;
    659 
    660 	TAILQ_FOREACH(child, &n->n_children, n_next)
    661 		node_print_indent(child, cmdline_options, 0);
    662 }
    663 
    664 static struct node *
    665 node_find_x(struct node *node, const char *path)
    666 {
    667 	struct node *child, *found;
    668 	char *tmp;
    669 	size_t tmplen;
    670 
    671 	//log_debugx("looking up %s in %s", path, node_path(node));
    672 
    673 	if (!node_is_direct_key(node)) {
    674 		tmp = node_path(node);
    675 		tmplen = strlen(tmp);
    676 		if (strncmp(tmp, path, tmplen) != 0) {
    677 			free(tmp);
    678 			return NULL;
    679 		}
    680 		if (path[tmplen] != '/' && path[tmplen] != '\0') {
    681 			/*
    682 			 * If we have two map entries like 'foo' and 'foobar', make
    683 			 * sure the search for 'foobar' won't match 'foo' instead.
    684 			 */
    685 			free(tmp);
    686 			return NULL;
    687 		}
    688 		free(tmp);
    689 	}
    690 
    691 	TAILQ_FOREACH(child, &node->n_children, n_next) {
    692 		found = node_find_x(child, path);
    693 		if (found != NULL)
    694 			return found;
    695 	}
    696 
    697 	if (node->n_parent == NULL || node_is_direct_key(node))
    698 		return NULL;
    699 
    700 	return node;
    701 }
    702 
    703 struct node *
    704 node_find(struct node *root, const char *path)
    705 {
    706 	struct node *node;
    707 
    708 	assert(root->n_parent == NULL);
    709 
    710 	node = node_find_x(root, path);
    711 	if (node != NULL)
    712 		assert(node != root);
    713 
    714 	return node;
    715 }
    716 
    717 /*
    718  * Canonical form of a map entry looks like this:
    719  *
    720  * key [-options] [ [/mountpoint] [-options2] location ... ]
    721  *
    722  * Entries for executable maps are slightly different, as they
    723  * lack the 'key' field and are always single-line; the key field
    724  * for those maps is taken from 'executable_key' argument.
    725  *
    726  * We parse it in such a way that a map always has two levels - first
    727  * for key, and the second, for the mountpoint.
    728  */
    729 static void
    730 parse_map_yyin(struct node *parent, const char *map, const char *executable_key)
    731 {
    732 	char *key = NULL, *options = NULL, *mountpoint = NULL,
    733 	    *options2 = NULL, *location = NULL;
    734 	int ret;
    735 	struct node *node;
    736 
    737 	lineno = 1;
    738 
    739 	if (executable_key != NULL)
    740 		key = checked_strdup(executable_key);
    741 
    742 	for (;;) {
    743 		ret = yylex();
    744 		if (ret == 0 || ret == NEWLINE) {
    745 			/*
    746 			 * In case of executable map, the key is always
    747 			 * non-NULL, even if the map is empty.  So, make sure
    748 			 * we don't fail empty maps here.
    749 			 */
    750 			if ((key != NULL && executable_key == NULL) ||
    751 			    options != NULL) {
    752 				log_errx(1, "truncated entry at %s, line %d",
    753 				    map, lineno);
    754 			}
    755 			if (ret == 0 || executable_key != NULL) {
    756 				/*
    757 				 * End of file.
    758 				 */
    759 				break;
    760 			} else {
    761 				key = options = NULL;
    762 				continue;
    763 			}
    764 		}
    765 		if (key == NULL) {
    766 			key = checked_strdup(yytext);
    767 			if (key[0] == '+') {
    768 				node_new(parent, key, NULL, NULL, map, lineno);
    769 				key = options = NULL;
    770 				continue;
    771 			}
    772 			continue;
    773 		} else if (yytext[0] == '-') {
    774 			if (options != NULL) {
    775 				log_errx(1, "duplicated options at %s, line %d",
    776 				    map, lineno);
    777 			}
    778 			/*
    779 			 * +1 to skip leading "-".
    780 			 */
    781 			options = checked_strdup(yytext + 1);
    782 			continue;
    783 		}
    784 
    785 		/*
    786 		 * We cannot properly handle a situation where the map key
    787 		 * is "/".  Ignore such entries.
    788 		 *
    789 		 * XXX: According to Piete Brooks, Linux automounter uses
    790 		 *	"/" as a wildcard character in LDAP maps.  Perhaps
    791 		 *	we should work around this braindamage by substituting
    792 		 *	"*" for "/"?
    793 		 */
    794 		if (strcmp(key, "/") == 0) {
    795 			log_warnx("nonsensical map key \"/\" at %s, line %d; "
    796 			    "ignoring map entry ", map, lineno);
    797 
    798 			/*
    799 			 * Skip the rest of the entry.
    800 			 */
    801 			do {
    802 				ret = yylex();
    803 			} while (ret != 0 && ret != NEWLINE);
    804 
    805 			key = options = NULL;
    806 			continue;
    807 		}
    808 
    809 		//log_debugx("adding map node, %s", key);
    810 		node = node_new(parent, key, options, NULL, map, lineno);
    811 		key = options = NULL;
    812 
    813 		for (;;) {
    814 			if (yytext[0] == '/') {
    815 				if (mountpoint != NULL) {
    816 					log_errx(1, "duplicated mountpoint "
    817 					    "in %s, line %d", map, lineno);
    818 				}
    819 				if (options2 != NULL || location != NULL) {
    820 					log_errx(1, "mountpoint out of order "
    821 					    "in %s, line %d", map, lineno);
    822 				}
    823 				mountpoint = checked_strdup(yytext);
    824 				goto again;
    825 			}
    826 
    827 			if (yytext[0] == '-') {
    828 				if (options2 != NULL) {
    829 					log_errx(1, "duplicated options "
    830 					    "in %s, line %d", map, lineno);
    831 				}
    832 				if (location != NULL) {
    833 					log_errx(1, "options out of order "
    834 					    "in %s, line %d", map, lineno);
    835 				}
    836 				options2 = checked_strdup(yytext + 1);
    837 				goto again;
    838 			}
    839 
    840 			if (location != NULL) {
    841 				log_errx(1, "too many arguments "
    842 				    "in %s, line %d", map, lineno);
    843 			}
    844 
    845 			/*
    846 			 * If location field starts with colon, e.g. ":/dev/cd0",
    847 			 * then strip it.
    848 			 */
    849 			if (yytext[0] == ':') {
    850 				location = checked_strdup(yytext + 1);
    851 				if (location[0] == '\0') {
    852 					log_errx(1, "empty location in %s, "
    853 					    "line %d", map, lineno);
    854 				}
    855 			} else {
    856 				location = checked_strdup(yytext);
    857 			}
    858 
    859 			if (mountpoint == NULL)
    860 				mountpoint = checked_strdup("/");
    861 			if (options2 == NULL)
    862 				options2 = checked_strdup("");
    863 
    864 #if 0
    865 			log_debugx("adding map node, %s %s %s",
    866 			    mountpoint, options2, location);
    867 #endif
    868 			node_new(node, mountpoint, options2, location,
    869 			    map, lineno);
    870 			mountpoint = options2 = location = NULL;
    871 again:
    872 			ret = yylex();
    873 			if (ret == 0 || ret == NEWLINE) {
    874 				if (mountpoint != NULL || options2 != NULL ||
    875 				    location != NULL) {
    876 					log_errx(1, "truncated entry "
    877 					    "in %s, line %d", map, lineno);
    878 				}
    879 				break;
    880 			}
    881 		}
    882 	}
    883 }
    884 
    885 /*
    886  * Parse output of a special map called without argument.  It is a list
    887  * of keys, separated by newlines.  They can contain whitespace, so use
    888  * getline(3) instead of lexer used for maps.
    889  */
    890 static void
    891 parse_map_keys_yyin(struct node *parent, const char *map)
    892 {
    893 	char *line = NULL, *key;
    894 	size_t linecap = 0;
    895 	ssize_t linelen;
    896 
    897 	lineno = 1;
    898 
    899 	for (;;) {
    900 		linelen = getline(&line, &linecap, yyin);
    901 		if (linelen < 0) {
    902 			/*
    903 			 * End of file.
    904 			 */
    905 			break;
    906 		}
    907 		if (linelen <= 1) {
    908 			/*
    909 			 * Empty line, consisting of just the newline.
    910 			 */
    911 			continue;
    912 		}
    913 
    914 		/*
    915 		 * "-1" to strip the trailing newline.
    916 		 */
    917 		key = strndup(line, (size_t)linelen - 1);
    918 
    919 		log_debugx("adding key \"%s\"", key);
    920 		node_new(parent, key, NULL, NULL, map, lineno);
    921 		lineno++;
    922 	}
    923 	free(line);
    924 }
    925 
    926 static bool
    927 file_is_executable(const char *path)
    928 {
    929 	struct stat sb;
    930 	int error;
    931 
    932 	error = stat(path, &sb);
    933 	if (error != 0)
    934 		log_err(1, "cannot stat %s", path);
    935 	return (sb.st_mode & S_IXUSR) || (sb.st_mode & S_IXGRP) ||
    936 	    (sb.st_mode & S_IXOTH);
    937 }
    938 
    939 /*
    940  * Parse a special map, e.g. "-hosts".
    941  */
    942 static void
    943 parse_special_map(struct node *parent, const char *map, const char *key)
    944 {
    945 	char *path;
    946 	int error, ret;
    947 
    948 	assert(map[0] == '-');
    949 
    950 	/*
    951 	 * +1 to skip leading "-" in map name.
    952 	 */
    953 	ret = asprintf(&path, "%s/special_%s", AUTO_SPECIAL_PREFIX, map + 1);
    954 	if (ret < 0)
    955 		log_err(1, "asprintf");
    956 
    957 	yyin = auto_popen(path, key, NULL);
    958 	assert(yyin != NULL);
    959 
    960 	if (key == NULL) {
    961 		parse_map_keys_yyin(parent, map);
    962 	} else {
    963 		parse_map_yyin(parent, map, key);
    964 	}
    965 
    966 	error = auto_pclose(yyin);
    967 	yyin = NULL;
    968 	if (error != 0)
    969 		log_errx(1, "failed to handle special map \"%s\"", map);
    970 
    971 	node_expand_includes(parent, false);
    972 	node_expand_direct_maps(parent);
    973 
    974 	free(path);
    975 }
    976 
    977 /*
    978  * Retrieve and parse map from directory services, e.g. LDAP.
    979  * Note that it is different from executable maps, in that
    980  * the include script outputs the whole map to standard output
    981  * (as opposed to executable maps that only output a single
    982  * entry, without the key), and it takes the map name as an
    983  * argument, instead of key.
    984  */
    985 static void
    986 parse_included_map(struct node *parent, const char *map)
    987 {
    988 	int error;
    989 
    990 	assert(map[0] != '-');
    991 	assert(map[0] != '/');
    992 
    993 	error = access(AUTO_INCLUDE_PATH, F_OK);
    994 	if (error != 0) {
    995 		log_errx(1, "directory services not configured;"
    996 		    " %s does not exist", AUTO_INCLUDE_PATH);
    997 	}
    998 
    999 	yyin = auto_popen(AUTO_INCLUDE_PATH, map, NULL);
   1000 	assert(yyin != NULL);
   1001 
   1002 	parse_map_yyin(parent, map, NULL);
   1003 
   1004 	error = auto_pclose(yyin);
   1005 	yyin = NULL;
   1006 	if (error != 0)
   1007 		log_errx(1, "failed to handle remote map \"%s\"", map);
   1008 
   1009 	node_expand_includes(parent, false);
   1010 	node_expand_direct_maps(parent);
   1011 }
   1012 
   1013 void
   1014 parse_map(struct node *parent, const char *map, const char *key,
   1015     bool *wildcards)
   1016 {
   1017 	char *path = NULL;
   1018 	int error, ret;
   1019 	bool executable;
   1020 
   1021 	assert(map != NULL);
   1022 	assert(map[0] != '\0');
   1023 
   1024 	log_debugx("parsing map \"%s\"", map);
   1025 
   1026 	if (wildcards != NULL)
   1027 		*wildcards = false;
   1028 
   1029 	if (map[0] == '-') {
   1030 		if (wildcards != NULL)
   1031 			*wildcards = true;
   1032 		return parse_special_map(parent, map, key);
   1033 	}
   1034 
   1035 	if (map[0] == '/') {
   1036 		path = checked_strdup(map);
   1037 	} else {
   1038 		ret = asprintf(&path, "%s/%s", AUTO_MAP_PREFIX, map);
   1039 		if (ret < 0)
   1040 			log_err(1, "asprintf");
   1041 		log_debugx("map \"%s\" maps to \"%s\"", map, path);
   1042 
   1043 		/*
   1044 		 * See if the file exists.  If not, try to obtain the map
   1045 		 * from directory services.
   1046 		 */
   1047 		error = access(path, F_OK);
   1048 		if (error != 0) {
   1049 			log_debugx("map file \"%s\" does not exist; falling "
   1050 			    "back to directory services", path);
   1051 			return parse_included_map(parent, map);
   1052 		}
   1053 	}
   1054 
   1055 	executable = file_is_executable(path);
   1056 
   1057 	if (executable) {
   1058 		log_debugx("map \"%s\" is executable", map);
   1059 
   1060 		if (wildcards != NULL)
   1061 			*wildcards = true;
   1062 
   1063 		if (key != NULL) {
   1064 			yyin = auto_popen(path, key, NULL);
   1065 		} else {
   1066 			yyin = auto_popen(path, NULL);
   1067 		}
   1068 		assert(yyin != NULL);
   1069 	} else {
   1070 		yyin = fopen(path, "r");
   1071 		if (yyin == NULL)
   1072 			log_err(1, "unable to open \"%s\"", path);
   1073 	}
   1074 
   1075 	free(path);
   1076 	path = NULL;
   1077 
   1078 	parse_map_yyin(parent, map, executable ? key : NULL);
   1079 
   1080 	if (executable) {
   1081 		error = auto_pclose(yyin);
   1082 		yyin = NULL;
   1083 		if (error != 0) {
   1084 			log_errx(1, "failed to handle executable map \"%s\"",
   1085 			    map);
   1086 		}
   1087 	} else {
   1088 		fclose(yyin);
   1089 	}
   1090 	yyin = NULL;
   1091 
   1092 	log_debugx("done parsing map \"%s\"", map);
   1093 
   1094 	node_expand_includes(parent, false);
   1095 	node_expand_direct_maps(parent);
   1096 }
   1097 
   1098 static void
   1099 parse_master_yyin(struct node *root, const char *master)
   1100 {
   1101 	char *mountpoint = NULL, *map = NULL, *options = NULL;
   1102 	int ret;
   1103 
   1104 	/*
   1105 	 * XXX: 1 gives incorrect values; wtf?
   1106 	 */
   1107 	lineno = 0;
   1108 
   1109 	for (;;) {
   1110 		ret = yylex();
   1111 		if (ret == 0 || ret == NEWLINE) {
   1112 			if (mountpoint != NULL) {
   1113 				//log_debugx("adding map for %s", mountpoint);
   1114 				node_new_map(root, mountpoint, options, map,
   1115 				    master, lineno);
   1116 			}
   1117 			if (ret == 0) {
   1118 				break;
   1119 			} else {
   1120 				mountpoint = map = options = NULL;
   1121 				continue;
   1122 			}
   1123 		}
   1124 		if (mountpoint == NULL) {
   1125 			mountpoint = checked_strdup(yytext);
   1126 		} else if (map == NULL) {
   1127 			map = checked_strdup(yytext);
   1128 		} else if (options == NULL) {
   1129 			/*
   1130 			 * +1 to skip leading "-".
   1131 			 */
   1132 			options = checked_strdup(yytext + 1);
   1133 		} else {
   1134 			log_errx(1, "too many arguments at %s, line %d",
   1135 			    master, lineno);
   1136 		}
   1137 	}
   1138 }
   1139 
   1140 void
   1141 parse_master(struct node *root, const char *master)
   1142 {
   1143 
   1144 	log_debugx("parsing auto_master file at \"%s\"", master);
   1145 
   1146 	yyin = fopen(master, "r");
   1147 	if (yyin == NULL)
   1148 		err(1, "unable to open %s", master);
   1149 
   1150 	parse_master_yyin(root, master);
   1151 
   1152 	fclose(yyin);
   1153 	yyin = NULL;
   1154 
   1155 	log_debugx("done parsing \"%s\"", master);
   1156 
   1157 	node_expand_includes(root, true);
   1158 	node_expand_direct_maps(root);
   1159 }
   1160 
   1161 /*
   1162  * Two things daemon(3) does, that we actually also want to do
   1163  * when running in foreground, is closing the stdin and chdiring
   1164  * to "/".  This is what we do here.
   1165  */
   1166 void
   1167 lesser_daemon(void)
   1168 {
   1169 	int error, fd;
   1170 
   1171 	error = chdir("/");
   1172 	if (error != 0)
   1173 		log_warn("chdir");
   1174 
   1175 	fd = open(_PATH_DEVNULL, O_RDWR, 0);
   1176 	if (fd < 0) {
   1177 		log_warn("cannot open %s", _PATH_DEVNULL);
   1178 		return;
   1179 	}
   1180 
   1181 	error = dup2(fd, STDIN_FILENO);
   1182 	if (error != 0)
   1183 		log_warn("dup2");
   1184 
   1185 	error = close(fd);
   1186 	if (error != 0) {
   1187 		/* Bloody hell. */
   1188 		log_warn("close");
   1189 	}
   1190 }
   1191 
   1192 int
   1193 main(int argc, char **argv)
   1194 {
   1195 	char *cmdname;
   1196 
   1197 	if (argv[0] == NULL)
   1198 		log_errx(1, "NULL command name");
   1199 
   1200 	cmdname = basename(argv[0]);
   1201 
   1202 	if (strcmp(cmdname, "automount") == 0)
   1203 		return main_automount(argc, argv);
   1204 	else if (strcmp(cmdname, "automountd") == 0)
   1205 		return main_automountd(argc, argv);
   1206 	else if (strcmp(cmdname, "autounmountd") == 0)
   1207 		return main_autounmountd(argc, argv);
   1208 	else
   1209 		log_errx(1, "binary name should be either \"automount\", "
   1210 		    "\"automountd\", or \"autounmountd\"");
   1211 }
   1212