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