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