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