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