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