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