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