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
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	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
153void
154create_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
188struct node *
189node_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
205struct node *
206node_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
236struct node *
237node_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
267static struct node *
268node_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
285static void
286node_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 */
305static void
306node_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
314static void
315node_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
363static char *
364expand_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 */
423void
424node_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 */
447void
448node_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
464int
465node_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
488bool
489node_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
506static void
507node_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
538static void
539node_expand_direct_maps(struct node *n)
540{
541
542	node_expand_maps(n, false);
543}
544
545void
546node_expand_indirect_maps(struct node *n)
547{
548
549	node_expand_maps(n, true);
550}
551
552static char *
553node_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 */
590char *
591node_path(const struct node *n)
592{
593
594	return (node_path_x(n, checked_strdup("")));
595}
596
597static char *
598node_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 */
614char *
615node_options(const struct node *n)
616{
617
618	return (node_options_x(n, checked_strdup("")));
619}
620
621static void
622node_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
662void
663node_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
671struct node *
672node_find(struct node *node, const char *path)
673{
674	struct node *child, *found;
675	char *tmp;
676	size_t tmplen;
677
678	//log_debugx("looking up %s in %s", path, node->n_key);
679
680	tmp = node_path(node);
681	tmplen = strlen(tmp);
682	if (strncmp(tmp, path, tmplen) != 0) {
683		free(tmp);
684		return (NULL);
685	}
686	if (path[tmplen] != '/' && path[tmplen] != '\0') {
687		/*
688		 * If we have two map entries like 'foo' and 'foobar', make
689		 * sure the search for 'foobar' won't match 'foo' instead.
690		 */
691		free(tmp);
692		return (NULL);
693	}
694	free(tmp);
695
696	TAILQ_FOREACH(child, &node->n_children, n_next) {
697		found = node_find(child, path);
698		if (found != NULL)
699			return (found);
700	}
701
702	return (node);
703}
704
705/*
706 * Canonical form of a map entry looks like this:
707 *
708 * key [-options] [ [/mountpoint] [-options2] location ... ]
709 *
710 * Entries for executable maps are slightly different, as they
711 * lack the 'key' field and are always single-line; the key field
712 * for those maps is taken from 'executable_key' argument.
713 *
714 * We parse it in such a way that a map always has two levels - first
715 * for key, and the second, for the mountpoint.
716 */
717static void
718parse_map_yyin(struct node *parent, const char *map, const char *executable_key)
719{
720	char *key = NULL, *options = NULL, *mountpoint = NULL,
721	    *options2 = NULL, *location = NULL;
722	int ret;
723	struct node *node;
724
725	lineno = 1;
726
727	if (executable_key != NULL)
728		key = checked_strdup(executable_key);
729
730	for (;;) {
731		ret = yylex();
732		if (ret == 0 || ret == NEWLINE) {
733			/*
734			 * In case of executable map, the key is always
735			 * non-NULL, even if the map is empty.  So, make sure
736			 * we don't fail empty maps here.
737			 */
738			if ((key != NULL && executable_key == NULL) ||
739			    options != NULL) {
740				log_errx(1, "truncated entry at %s, line %d",
741				    map, lineno);
742			}
743			if (ret == 0 || executable_key != NULL) {
744				/*
745				 * End of file.
746				 */
747				break;
748			} else {
749				key = options = NULL;
750				continue;
751			}
752		}
753		if (key == NULL) {
754			key = checked_strdup(yytext);
755			if (key[0] == '+') {
756				node_new(parent, key, NULL, NULL, map, lineno);
757				key = options = NULL;
758				continue;
759			}
760			continue;
761		} else if (yytext[0] == '-') {
762			if (options != NULL) {
763				log_errx(1, "duplicated options at %s, line %d",
764				    map, lineno);
765			}
766			/*
767			 * +1 to skip leading "-".
768			 */
769			options = checked_strdup(yytext + 1);
770			continue;
771		}
772
773		/*
774		 * We cannot properly handle a situation where the map key
775		 * is "/".  Ignore such entries.
776		 *
777		 * XXX: According to Piete Brooks, Linux automounter uses
778		 *	"/" as a wildcard character in LDAP maps.  Perhaps
779		 *	we should work around this braindamage by substituting
780		 *	"*" for "/"?
781		 */
782		if (strcmp(key, "/") == 0) {
783			log_warnx("nonsensical map key \"/\" at %s, line %d; "
784			    "ignoring map entry ", map, lineno);
785
786			/*
787			 * Skip the rest of the entry.
788			 */
789			do {
790				ret = yylex();
791			} while (ret != 0 && ret != NEWLINE);
792
793			key = options = NULL;
794			continue;
795		}
796
797		//log_debugx("adding map node, %s", key);
798		node = node_new(parent, key, options, NULL, map, lineno);
799		key = options = NULL;
800
801		for (;;) {
802			if (yytext[0] == '/') {
803				if (mountpoint != NULL) {
804					log_errx(1, "duplicated mountpoint "
805					    "in %s, line %d", map, lineno);
806				}
807				if (options2 != NULL || location != NULL) {
808					log_errx(1, "mountpoint out of order "
809					    "in %s, line %d", map, lineno);
810				}
811				mountpoint = checked_strdup(yytext);
812				goto again;
813			}
814
815			if (yytext[0] == '-') {
816				if (options2 != NULL) {
817					log_errx(1, "duplicated options "
818					    "in %s, line %d", map, lineno);
819				}
820				if (location != NULL) {
821					log_errx(1, "options out of order "
822					    "in %s, line %d", map, lineno);
823				}
824				options2 = checked_strdup(yytext + 1);
825				goto again;
826			}
827
828			if (location != NULL) {
829				log_errx(1, "too many arguments "
830				    "in %s, line %d", map, lineno);
831			}
832
833			/*
834			 * If location field starts with colon, e.g. ":/dev/cd0",
835			 * then strip it.
836			 */
837			if (yytext[0] == ':') {
838				location = checked_strdup(yytext + 1);
839				if (location[0] == '\0') {
840					log_errx(1, "empty location in %s, "
841					    "line %d", map, lineno);
842				}
843			} else {
844				location = checked_strdup(yytext);
845			}
846
847			if (mountpoint == NULL)
848				mountpoint = checked_strdup("/");
849			if (options2 == NULL)
850				options2 = checked_strdup("");
851
852#if 0
853			log_debugx("adding map node, %s %s %s",
854			    mountpoint, options2, location);
855#endif
856			node_new(node, mountpoint, options2, location,
857			    map, lineno);
858			mountpoint = options2 = location = NULL;
859again:
860			ret = yylex();
861			if (ret == 0 || ret == NEWLINE) {
862				if (mountpoint != NULL || options2 != NULL ||
863				    location != NULL) {
864					log_errx(1, "truncated entry "
865					    "in %s, line %d", map, lineno);
866				}
867				break;
868			}
869		}
870	}
871}
872
873/*
874 * Parse output of a special map called without argument.  It is a list
875 * of keys, separated by newlines.  They can contain whitespace, so use
876 * getline(3) instead of lexer used for maps.
877 */
878static void
879parse_map_keys_yyin(struct node *parent, const char *map)
880{
881	char *line = NULL, *key;
882	size_t linecap = 0;
883	ssize_t linelen;
884
885	lineno = 1;
886
887	for (;;) {
888		linelen = getline(&line, &linecap, yyin);
889		if (linelen < 0) {
890			/*
891			 * End of file.
892			 */
893			break;
894		}
895		if (linelen <= 1) {
896			/*
897			 * Empty line, consisting of just the newline.
898			 */
899			continue;
900		}
901
902		/*
903		 * "-1" to strip the trailing newline.
904		 */
905		key = strndup(line, linelen - 1);
906
907		log_debugx("adding key \"%s\"", key);
908		node_new(parent, key, NULL, NULL, map, lineno);
909		lineno++;
910	}
911	free(line);
912}
913
914static bool
915file_is_executable(const char *path)
916{
917	struct stat sb;
918	int error;
919
920	error = stat(path, &sb);
921	if (error != 0)
922		log_err(1, "cannot stat %s", path);
923	if ((sb.st_mode & S_IXUSR) || (sb.st_mode & S_IXGRP) ||
924	    (sb.st_mode & S_IXOTH))
925		return (true);
926	return (false);
927}
928
929/*
930 * Parse a special map, e.g. "-hosts".
931 */
932static void
933parse_special_map(struct node *parent, const char *map, const char *key)
934{
935	char *path;
936	int error, ret;
937
938	assert(map[0] == '-');
939
940	/*
941	 * +1 to skip leading "-" in map name.
942	 */
943	ret = asprintf(&path, "%s/special_%s", AUTO_SPECIAL_PREFIX, map + 1);
944	if (ret < 0)
945		log_err(1, "asprintf");
946
947	yyin = auto_popen(path, key, NULL);
948	assert(yyin != NULL);
949
950	if (key == NULL) {
951		parse_map_keys_yyin(parent, map);
952	} else {
953		parse_map_yyin(parent, map, key);
954	}
955
956	error = auto_pclose(yyin);
957	yyin = NULL;
958	if (error != 0)
959		log_errx(1, "failed to handle special map \"%s\"", map);
960
961	node_expand_includes(parent, false);
962	node_expand_direct_maps(parent);
963
964	free(path);
965}
966
967/*
968 * Retrieve and parse map from directory services, e.g. LDAP.
969 * Note that it is different from executable maps, in that
970 * the include script outputs the whole map to standard output
971 * (as opposed to executable maps that only output a single
972 * entry, without the key), and it takes the map name as an
973 * argument, instead of key.
974 */
975static void
976parse_included_map(struct node *parent, const char *map)
977{
978	int error;
979
980	assert(map[0] != '-');
981	assert(map[0] != '/');
982
983	error = access(AUTO_INCLUDE_PATH, F_OK);
984	if (error != 0) {
985		log_errx(1, "directory services not configured;"
986		    " %s does not exist", AUTO_INCLUDE_PATH);
987	}
988
989	yyin = auto_popen(AUTO_INCLUDE_PATH, map, NULL);
990	assert(yyin != NULL);
991
992	parse_map_yyin(parent, map, NULL);
993
994	error = auto_pclose(yyin);
995	yyin = NULL;
996	if (error != 0)
997		log_errx(1, "failed to handle remote map \"%s\"", map);
998
999	node_expand_includes(parent, false);
1000	node_expand_direct_maps(parent);
1001}
1002
1003void
1004parse_map(struct node *parent, const char *map, const char *key)
1005{
1006	char *path = NULL;
1007	int error, ret;
1008	bool executable;
1009
1010	assert(map != NULL);
1011	assert(map[0] != '\0');
1012
1013	log_debugx("parsing map \"%s\"", map);
1014
1015	if (map[0] == '-')
1016		return (parse_special_map(parent, map, key));
1017
1018	if (map[0] == '/') {
1019		path = checked_strdup(map);
1020	} else {
1021		ret = asprintf(&path, "%s/%s", AUTO_MAP_PREFIX, map);
1022		if (ret < 0)
1023			log_err(1, "asprintf");
1024		log_debugx("map \"%s\" maps to \"%s\"", map, path);
1025
1026		/*
1027		 * See if the file exists.  If not, try to obtain the map
1028		 * from directory services.
1029		 */
1030		error = access(path, F_OK);
1031		if (error != 0) {
1032			log_debugx("map file \"%s\" does not exist; falling "
1033			    "back to directory services", path);
1034			return (parse_included_map(parent, map));
1035		}
1036	}
1037
1038	executable = file_is_executable(path);
1039
1040	if (executable) {
1041		log_debugx("map \"%s\" is executable", map);
1042
1043		if (key != NULL) {
1044			yyin = auto_popen(path, key, NULL);
1045		} else {
1046			yyin = auto_popen(path, NULL);
1047		}
1048		assert(yyin != NULL);
1049	} else {
1050		yyin = fopen(path, "r");
1051		if (yyin == NULL)
1052			log_err(1, "unable to open \"%s\"", path);
1053	}
1054
1055	free(path);
1056	path = NULL;
1057
1058	parse_map_yyin(parent, map, executable ? key : NULL);
1059
1060	if (executable) {
1061		error = auto_pclose(yyin);
1062		yyin = NULL;
1063		if (error != 0) {
1064			log_errx(1, "failed to handle executable map \"%s\"",
1065			    map);
1066		}
1067	} else {
1068		fclose(yyin);
1069	}
1070	yyin = NULL;
1071
1072	log_debugx("done parsing map \"%s\"", map);
1073
1074	node_expand_includes(parent, false);
1075	node_expand_direct_maps(parent);
1076}
1077
1078static void
1079parse_master_yyin(struct node *root, const char *master)
1080{
1081	char *mountpoint = NULL, *map = NULL, *options = NULL;
1082	int ret;
1083
1084	/*
1085	 * XXX: 1 gives incorrect values; wtf?
1086	 */
1087	lineno = 0;
1088
1089	for (;;) {
1090		ret = yylex();
1091		if (ret == 0 || ret == NEWLINE) {
1092			if (mountpoint != NULL) {
1093				//log_debugx("adding map for %s", mountpoint);
1094				node_new_map(root, mountpoint, options, map,
1095				    master, lineno);
1096			}
1097			if (ret == 0) {
1098				break;
1099			} else {
1100				mountpoint = map = options = NULL;
1101				continue;
1102			}
1103		}
1104		if (mountpoint == NULL) {
1105			mountpoint = checked_strdup(yytext);
1106		} else if (map == NULL) {
1107			map = checked_strdup(yytext);
1108		} else if (options == NULL) {
1109			/*
1110			 * +1 to skip leading "-".
1111			 */
1112			options = checked_strdup(yytext + 1);
1113		} else {
1114			log_errx(1, "too many arguments at %s, line %d",
1115			    master, lineno);
1116		}
1117	}
1118}
1119
1120void
1121parse_master(struct node *root, const char *master)
1122{
1123
1124	log_debugx("parsing auto_master file at \"%s\"", master);
1125
1126	yyin = fopen(master, "r");
1127	if (yyin == NULL)
1128		err(1, "unable to open %s", master);
1129
1130	parse_master_yyin(root, master);
1131
1132	fclose(yyin);
1133	yyin = NULL;
1134
1135	log_debugx("done parsing \"%s\"", master);
1136
1137	node_expand_includes(root, true);
1138	node_expand_direct_maps(root);
1139}
1140
1141/*
1142 * Two things daemon(3) does, that we actually also want to do
1143 * when running in foreground, is closing the stdin and chdiring
1144 * to "/".  This is what we do here.
1145 */
1146void
1147lesser_daemon(void)
1148{
1149	int error, fd;
1150
1151	error = chdir("/");
1152	if (error != 0)
1153		log_warn("chdir");
1154
1155	fd = open(_PATH_DEVNULL, O_RDWR, 0);
1156	if (fd < 0) {
1157		log_warn("cannot open %s", _PATH_DEVNULL);
1158		return;
1159	}
1160
1161	error = dup2(fd, STDIN_FILENO);
1162	if (error != 0)
1163		log_warn("dup2");
1164
1165	error = close(fd);
1166	if (error != 0) {
1167		/* Bloody hell. */
1168		log_warn("close");
1169	}
1170}
1171
1172int
1173main(int argc, char **argv)
1174{
1175	char *cmdname;
1176
1177	if (argv[0] == NULL)
1178		log_errx(1, "NULL command name");
1179
1180	cmdname = basename(argv[0]);
1181
1182	if (strcmp(cmdname, "automount") == 0)
1183		return (main_automount(argc, argv));
1184	else if (strcmp(cmdname, "automountd") == 0)
1185		return (main_automountd(argc, argv));
1186	else if (strcmp(cmdname, "autounmountd") == 0)
1187		return (main_autounmountd(argc, argv));
1188	else
1189		log_errx(1, "binary name should be either \"automount\", "
1190		    "\"automountd\", or \"autounmountd\"");
1191}
1192