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