1270096Strasz/*-
2270096Strasz * Copyright (c) 2014 The FreeBSD Foundation
3270096Strasz * All rights reserved.
4270096Strasz *
5270096Strasz * This software was developed by Edward Tomasz Napierala under sponsorship
6270096Strasz * from the FreeBSD Foundation.
7270096Strasz *
8270096Strasz * Redistribution and use in source and binary forms, with or without
9270096Strasz * modification, are permitted provided that the following conditions
10270096Strasz * are met:
11270096Strasz * 1. Redistributions of source code must retain the above copyright
12270096Strasz *    notice, this list of conditions and the following disclaimer.
13270096Strasz * 2. Redistributions in binary form must reproduce the above copyright
14270096Strasz *    notice, this list of conditions and the following disclaimer in the
15270096Strasz *    documentation and/or other materials provided with the distribution.
16270096Strasz *
17270096Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18270096Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19270096Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20270096Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21270096Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22270096Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23270096Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24270096Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25270096Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26270096Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27270096Strasz * SUCH DAMAGE.
28270096Strasz *
29270096Strasz */
30270096Strasz
31270897Strasz#include <sys/cdefs.h>
32270897Strasz__FBSDID("$FreeBSD: releng/10.3/usr.sbin/autofs/common.c 283240 2015-05-21 13:39:38Z trasz $");
33270897Strasz
34270096Strasz#include <sys/types.h>
35270096Strasz#include <sys/time.h>
36270096Strasz#include <sys/ioctl.h>
37270096Strasz#include <sys/param.h>
38270096Strasz#include <sys/linker.h>
39270096Strasz#include <sys/mount.h>
40270096Strasz#include <sys/socket.h>
41270096Strasz#include <sys/stat.h>
42270096Strasz#include <sys/wait.h>
43270096Strasz#include <sys/utsname.h>
44270096Strasz#include <assert.h>
45270096Strasz#include <ctype.h>
46270096Strasz#include <err.h>
47270096Strasz#include <errno.h>
48270096Strasz#include <fcntl.h>
49270096Strasz#include <libgen.h>
50270096Strasz#include <netdb.h>
51270096Strasz#include <paths.h>
52270096Strasz#include <signal.h>
53270096Strasz#include <stdbool.h>
54270096Strasz#include <stdint.h>
55270903Strasz#define	_WITH_GETLINE
56270096Strasz#include <stdio.h>
57270096Strasz#include <stdlib.h>
58270096Strasz#include <string.h>
59270096Strasz#include <unistd.h>
60270096Strasz
61270096Strasz#include <libutil.h>
62270096Strasz
63270096Strasz#include "autofs_ioctl.h"
64270096Strasz
65270096Strasz#include "common.h"
66270096Strasz
67270096Straszextern FILE *yyin;
68270096Straszextern char *yytext;
69270096Straszextern int yylex(void);
70270096Strasz
71270096Straszstatic void	parse_master_yyin(struct node *root, const char *master);
72270096Straszstatic void	parse_map_yyin(struct node *parent, const char *map,
73270096Strasz		    const char *executable_key);
74270096Strasz
75270096Straszchar *
76270096Straszchecked_strdup(const char *s)
77270096Strasz{
78270096Strasz	char *c;
79270096Strasz
80270096Strasz	assert(s != NULL);
81270096Strasz
82270096Strasz	c = strdup(s);
83270096Strasz	if (c == NULL)
84270096Strasz		log_err(1, "strdup");
85270096Strasz	return (c);
86270096Strasz}
87270096Strasz
88270096Strasz/*
89270096Strasz * Concatenate two strings, inserting separator between them, unless not needed.
90270096Strasz */
91270096Straszchar *
92283231Straszconcat(const char *s1, char separator, const char *s2)
93270096Strasz{
94270096Strasz	char *result;
95283240Strasz	char s1last, s2first;
96270096Strasz	int ret;
97270096Strasz
98283238Strasz	if (s1 == NULL)
99283238Strasz		s1 = "";
100283238Strasz	if (s2 == NULL)
101283238Strasz		s2 = "";
102270096Strasz
103283240Strasz	if (s1[0] == '\0')
104283240Strasz		s1last = '\0';
105283240Strasz	else
106283240Strasz		s1last = s1[strlen(s1) - 1];
107283228Strasz
108283240Strasz	s2first = s2[0];
109283240Strasz
110283240Strasz	if (s1last == separator && s2first == separator) {
111283240Strasz		/*
112283240Strasz		 * If s1 ends with the separator and s2 begins with
113283240Strasz		 * it - skip the latter; otherwise concatenating "/"
114283240Strasz		 * and "/foo" would end up returning "//foo".
115283240Strasz		 */
116283240Strasz		ret = asprintf(&result, "%s%s", s1, s2 + 1);
117283240Strasz	} else if (s1last == separator || s2first == separator ||
118283240Strasz	    s1[0] == '\0' || s2[0] == '\0') {
119270096Strasz		ret = asprintf(&result, "%s%s", s1, s2);
120270096Strasz	} else {
121270096Strasz		ret = asprintf(&result, "%s%c%s", s1, separator, s2);
122270096Strasz	}
123270096Strasz	if (ret < 0)
124270096Strasz		log_err(1, "asprintf");
125270096Strasz
126283230Strasz	//log_debugx("%s: got %s and %s, returning %s", __func__, s1, s2, result);
127270096Strasz
128270096Strasz	return (result);
129270096Strasz}
130270096Strasz
131270096Straszvoid
132270096Straszcreate_directory(const char *path)
133270096Strasz{
134283230Strasz	char *component, *copy, *tofree, *partial, *tmp;
135270096Strasz	int error;
136270096Strasz
137270096Strasz	assert(path[0] == '/');
138270096Strasz
139270096Strasz	/*
140270096Strasz	 * +1 to skip the leading slash.
141270096Strasz	 */
142270096Strasz	copy = tofree = checked_strdup(path + 1);
143270096Strasz
144283230Strasz	partial = checked_strdup("");
145270096Strasz	for (;;) {
146270096Strasz		component = strsep(&copy, "/");
147270096Strasz		if (component == NULL)
148270096Strasz			break;
149283231Strasz		tmp = concat(partial, '/', component);
150283230Strasz		free(partial);
151283230Strasz		partial = tmp;
152274500Strasz		//log_debugx("creating \"%s\"", partial);
153270096Strasz		error = mkdir(partial, 0755);
154274500Strasz		if (error != 0 && errno != EEXIST) {
155274500Strasz			log_warn("cannot create %s", partial);
156274500Strasz			return;
157274500Strasz		}
158270096Strasz	}
159270096Strasz
160270096Strasz	free(tofree);
161270096Strasz}
162270096Strasz
163270096Straszstruct node *
164270096Strasznode_new_root(void)
165270096Strasz{
166270096Strasz	struct node *n;
167270096Strasz
168270096Strasz	n = calloc(1, sizeof(*n));
169270096Strasz	if (n == NULL)
170270096Strasz		log_err(1, "calloc");
171270096Strasz	// XXX
172270096Strasz	n->n_key = checked_strdup("/");
173270096Strasz	n->n_options = checked_strdup("");
174270096Strasz
175270096Strasz	TAILQ_INIT(&n->n_children);
176270096Strasz
177270096Strasz	return (n);
178270096Strasz}
179270096Strasz
180270096Straszstruct node *
181270096Strasznode_new(struct node *parent, char *key, char *options, char *location,
182270096Strasz    const char *config_file, int config_line)
183270096Strasz{
184270096Strasz	struct node *n;
185270096Strasz
186270096Strasz	n = calloc(1, sizeof(*n));
187270096Strasz	if (n == NULL)
188270096Strasz		log_err(1, "calloc");
189270096Strasz
190270096Strasz	TAILQ_INIT(&n->n_children);
191270096Strasz	assert(key != NULL);
192270903Strasz	assert(key[0] != '\0');
193270096Strasz	n->n_key = key;
194270096Strasz	if (options != NULL)
195270096Strasz		n->n_options = options;
196270096Strasz	else
197270096Strasz		n->n_options = strdup("");
198270096Strasz	n->n_location = location;
199270096Strasz	assert(config_file != NULL);
200270096Strasz	n->n_config_file = config_file;
201270096Strasz	assert(config_line >= 0);
202270096Strasz	n->n_config_line = config_line;
203270096Strasz
204270096Strasz	assert(parent != NULL);
205270096Strasz	n->n_parent = parent;
206270096Strasz	TAILQ_INSERT_TAIL(&parent->n_children, n, n_next);
207270096Strasz
208270096Strasz	return (n);
209270096Strasz}
210270096Strasz
211270096Straszstruct node *
212270096Strasznode_new_map(struct node *parent, char *key, char *options, char *map,
213270096Strasz    const char *config_file, int config_line)
214270096Strasz{
215270096Strasz	struct node *n;
216270096Strasz
217270096Strasz	n = calloc(1, sizeof(*n));
218270096Strasz	if (n == NULL)
219270096Strasz		log_err(1, "calloc");
220270096Strasz
221270096Strasz	TAILQ_INIT(&n->n_children);
222270096Strasz	assert(key != NULL);
223270903Strasz	assert(key[0] != '\0');
224270096Strasz	n->n_key = key;
225270096Strasz	if (options != NULL)
226270096Strasz		n->n_options = options;
227270096Strasz	else
228270096Strasz		n->n_options = strdup("");
229270096Strasz	n->n_map = map;
230270096Strasz	assert(config_file != NULL);
231270096Strasz	n->n_config_file = config_file;
232270096Strasz	assert(config_line >= 0);
233270096Strasz	n->n_config_line = config_line;
234270096Strasz
235270096Strasz	assert(parent != NULL);
236270096Strasz	n->n_parent = parent;
237270096Strasz	TAILQ_INSERT_TAIL(&parent->n_children, n, n_next);
238270096Strasz
239270096Strasz	return (n);
240270096Strasz}
241270096Strasz
242270096Straszstatic struct node *
243270096Strasznode_duplicate(const struct node *o, struct node *parent)
244270096Strasz{
245270096Strasz	const struct node *child;
246270096Strasz	struct node *n;
247270096Strasz
248270096Strasz	if (parent == NULL)
249270096Strasz		parent = o->n_parent;
250270096Strasz
251270096Strasz	n = node_new(parent, o->n_key, o->n_options, o->n_location,
252270096Strasz	    o->n_config_file, o->n_config_line);
253270096Strasz
254270096Strasz	TAILQ_FOREACH(child, &o->n_children, n_next)
255270096Strasz		node_duplicate(child, n);
256270096Strasz
257270096Strasz	return (n);
258270096Strasz}
259270096Strasz
260270096Straszstatic void
261270096Strasznode_delete(struct node *n)
262270096Strasz{
263270096Strasz	struct node *child, *tmp;
264270096Strasz
265270096Strasz	assert (n != NULL);
266270096Strasz
267270096Strasz	TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp)
268270096Strasz		node_delete(child);
269270096Strasz
270270096Strasz	if (n->n_parent != NULL)
271270096Strasz		TAILQ_REMOVE(&n->n_parent->n_children, n, n_next);
272270096Strasz
273270096Strasz	free(n);
274270096Strasz}
275270096Strasz
276270096Strasz/*
277270096Strasz * Move (reparent) node 'n' to make it sibling of 'previous', placed
278270096Strasz * just after it.
279270096Strasz */
280270096Straszstatic void
281270096Strasznode_move_after(struct node *n, struct node *previous)
282270096Strasz{
283270096Strasz
284270096Strasz	TAILQ_REMOVE(&n->n_parent->n_children, n, n_next);
285270096Strasz	n->n_parent = previous->n_parent;
286270096Strasz	TAILQ_INSERT_AFTER(&previous->n_parent->n_children, previous, n, n_next);
287270096Strasz}
288270096Strasz
289270096Straszstatic void
290270096Strasznode_expand_includes(struct node *root, bool is_master)
291270096Strasz{
292270096Strasz	struct node *n, *n2, *tmp, *tmp2, *tmproot;
293270096Strasz	int error;
294270096Strasz
295270096Strasz	TAILQ_FOREACH_SAFE(n, &root->n_children, n_next, tmp) {
296270096Strasz		if (n->n_key[0] != '+')
297270096Strasz			continue;
298270096Strasz
299270096Strasz		error = access(AUTO_INCLUDE_PATH, F_OK);
300270096Strasz		if (error != 0) {
301270096Strasz			log_errx(1, "directory services not configured; "
302270096Strasz			    "%s does not exist", AUTO_INCLUDE_PATH);
303270096Strasz		}
304270096Strasz
305270096Strasz		/*
306270096Strasz		 * "+1" to skip leading "+".
307270096Strasz		 */
308270096Strasz		yyin = auto_popen(AUTO_INCLUDE_PATH, n->n_key + 1, NULL);
309270096Strasz		assert(yyin != NULL);
310270096Strasz
311270096Strasz		tmproot = node_new_root();
312270096Strasz		if (is_master)
313270096Strasz			parse_master_yyin(tmproot, n->n_key);
314270096Strasz		else
315270096Strasz			parse_map_yyin(tmproot, n->n_key, NULL);
316270096Strasz
317270096Strasz		error = auto_pclose(yyin);
318270096Strasz		yyin = NULL;
319270096Strasz		if (error != 0) {
320270096Strasz			log_errx(1, "failed to handle include \"%s\"",
321270096Strasz			    n->n_key);
322270096Strasz		}
323270096Strasz
324270096Strasz		/*
325270096Strasz		 * Entries to be included are now in tmproot.  We need to merge
326270096Strasz		 * them with the rest, preserving their place and ordering.
327270096Strasz		 */
328270096Strasz		TAILQ_FOREACH_REVERSE_SAFE(n2,
329270096Strasz		    &tmproot->n_children, nodehead, n_next, tmp2) {
330270096Strasz			node_move_after(n2, n);
331270096Strasz		}
332270096Strasz
333270096Strasz		node_delete(n);
334270096Strasz		node_delete(tmproot);
335270096Strasz	}
336270096Strasz}
337270096Strasz
338270096Straszstatic char *
339270096Straszexpand_ampersand(char *string, const char *key)
340270096Strasz{
341270096Strasz	char c, *expanded;
342270096Strasz	int i, ret, before_len = 0;
343270096Strasz	bool backslashed = false;
344270096Strasz
345270096Strasz	assert(key[0] != '\0');
346270096Strasz
347270096Strasz	expanded = checked_strdup(string);
348270096Strasz
349270096Strasz	for (i = 0; string[i] != '\0'; i++) {
350270096Strasz		c = string[i];
351270096Strasz		if (c == '\\' && backslashed == false) {
352270096Strasz			backslashed = true;
353270096Strasz			continue;
354270096Strasz		}
355270096Strasz		if (backslashed) {
356270096Strasz			backslashed = false;
357270096Strasz			continue;
358270096Strasz		}
359270096Strasz		backslashed = false;
360270096Strasz		if (c != '&')
361270096Strasz			continue;
362270096Strasz
363270096Strasz		/*
364270096Strasz		 * The 'before_len' variable contains the number
365270096Strasz		 * of characters before the '&'.
366270096Strasz		 */
367270096Strasz		before_len = i;
368270096Strasz		//assert(i + 1 < (int)strlen(string));
369270096Strasz
370270096Strasz		ret = asprintf(&expanded, "%.*s%s%s",
371270096Strasz		    before_len, string, key, string + before_len + 1);
372270096Strasz		if (ret < 0)
373270096Strasz			log_err(1, "asprintf");
374270096Strasz
375270096Strasz		//log_debugx("\"%s\" expanded with key \"%s\" to \"%s\"",
376270096Strasz		//    string, key, expanded);
377270096Strasz
378270096Strasz		/*
379270096Strasz		 * Figure out where to start searching for next variable.
380270096Strasz		 */
381270096Strasz		string = expanded;
382270096Strasz		i = before_len + strlen(key);
383270096Strasz		backslashed = false;
384270096Strasz		//assert(i < (int)strlen(string));
385270096Strasz	}
386270096Strasz
387270096Strasz	return (expanded);
388270096Strasz}
389270096Strasz
390270096Strasz/*
391270096Strasz * Expand "&" in n_location.  If the key is NULL, try to use
392270096Strasz * key from map entries themselves.  Keep in mind that maps
393270096Strasz * consist of tho levels of node structures, the key is one
394270096Strasz * level up.
395270096Strasz *
396270096Strasz * Variant with NULL key is for "automount -LL".
397270096Strasz */
398270096Straszvoid
399270096Strasznode_expand_ampersand(struct node *n, const char *key)
400270096Strasz{
401270096Strasz	struct node *child;
402270096Strasz
403270096Strasz	if (n->n_location != NULL) {
404270096Strasz		if (key == NULL) {
405270096Strasz			if (n->n_parent != NULL &&
406270096Strasz			    strcmp(n->n_parent->n_key, "*") != 0) {
407270096Strasz				n->n_location = expand_ampersand(n->n_location,
408270096Strasz				    n->n_parent->n_key);
409270096Strasz			}
410270096Strasz		} else {
411270096Strasz			n->n_location = expand_ampersand(n->n_location, key);
412270096Strasz		}
413270096Strasz	}
414270096Strasz
415270096Strasz	TAILQ_FOREACH(child, &n->n_children, n_next)
416270096Strasz		node_expand_ampersand(child, key);
417270096Strasz}
418270096Strasz
419270096Strasz/*
420270096Strasz * Expand "*" in n_key.
421270096Strasz */
422270096Straszvoid
423270096Strasznode_expand_wildcard(struct node *n, const char *key)
424270096Strasz{
425270096Strasz	struct node *child, *expanded;
426270096Strasz
427270096Strasz	assert(key != NULL);
428270096Strasz
429270096Strasz	if (strcmp(n->n_key, "*") == 0) {
430270096Strasz		expanded = node_duplicate(n, NULL);
431270096Strasz		expanded->n_key = checked_strdup(key);
432270096Strasz		node_move_after(expanded, n);
433270096Strasz	}
434270096Strasz
435270096Strasz	TAILQ_FOREACH(child, &n->n_children, n_next)
436270096Strasz		node_expand_wildcard(child, key);
437270096Strasz}
438270096Strasz
439270096Straszint
440270096Strasznode_expand_defined(struct node *n)
441270096Strasz{
442270096Strasz	struct node *child;
443270096Strasz	int error, cumulated_error = 0;
444270096Strasz
445270096Strasz	if (n->n_location != NULL) {
446270096Strasz		n->n_location = defined_expand(n->n_location);
447270096Strasz		if (n->n_location == NULL) {
448270096Strasz			log_warnx("failed to expand location for %s",
449270096Strasz			    node_path(n));
450270096Strasz			return (EINVAL);
451270096Strasz		}
452270096Strasz	}
453270096Strasz
454270096Strasz	TAILQ_FOREACH(child, &n->n_children, n_next) {
455270096Strasz		error = node_expand_defined(child);
456270096Strasz		if (error != 0 && cumulated_error == 0)
457270096Strasz			cumulated_error = error;
458270096Strasz	}
459270096Strasz
460270096Strasz	return (cumulated_error);
461270096Strasz}
462270096Strasz
463283232Straszstatic bool
464283232Strasznode_is_direct_key(const struct node *n)
465283232Strasz{
466283232Strasz
467283232Strasz	if (n->n_parent != NULL && n->n_parent->n_parent == NULL &&
468283232Strasz	    strcmp(n->n_key, "/-") == 0) {
469283232Strasz		return (true);
470283232Strasz	}
471283232Strasz
472283232Strasz	return (false);
473283232Strasz}
474283232Strasz
475270096Straszbool
476270096Strasznode_is_direct_map(const struct node *n)
477270096Strasz{
478270096Strasz
479270096Strasz	for (;;) {
480270096Strasz		assert(n->n_parent != NULL);
481270096Strasz		if (n->n_parent->n_parent == NULL)
482270096Strasz			break;
483270096Strasz		n = n->n_parent;
484270096Strasz	}
485270096Strasz
486283232Strasz	return (node_is_direct_key(n));
487270096Strasz}
488270096Strasz
489279741Straszbool
490279741Strasznode_has_wildcards(const struct node *n)
491279741Strasz{
492279741Strasz	const struct node *child;
493279741Strasz
494279741Strasz	TAILQ_FOREACH(child, &n->n_children, n_next) {
495279741Strasz		if (strcmp(child->n_key, "*") == 0)
496279741Strasz			return (true);
497279741Strasz	}
498279741Strasz
499279741Strasz	return (false);
500279741Strasz}
501279741Strasz
502270096Straszstatic void
503270096Strasznode_expand_maps(struct node *n, bool indirect)
504270096Strasz{
505270096Strasz	struct node *child, *tmp;
506270096Strasz
507270096Strasz	TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp) {
508270096Strasz		if (node_is_direct_map(child)) {
509270096Strasz			if (indirect)
510270096Strasz				continue;
511270096Strasz		} else {
512270096Strasz			if (indirect == false)
513270096Strasz				continue;
514270096Strasz		}
515270096Strasz
516270096Strasz		/*
517270096Strasz		 * This is the first-level map node; the one that contains
518270096Strasz		 * the key and subnodes with mountpoints and actual map names.
519270096Strasz		 */
520270096Strasz		if (child->n_map == NULL)
521270096Strasz			continue;
522270096Strasz
523270096Strasz		if (indirect) {
524270096Strasz			log_debugx("map \"%s\" is an indirect map, parsing",
525270096Strasz			    child->n_map);
526270096Strasz		} else {
527270096Strasz			log_debugx("map \"%s\" is a direct map, parsing",
528270096Strasz			    child->n_map);
529270096Strasz		}
530279741Strasz		parse_map(child, child->n_map, NULL, NULL);
531270096Strasz	}
532270096Strasz}
533270096Strasz
534270096Straszstatic void
535270096Strasznode_expand_direct_maps(struct node *n)
536270096Strasz{
537270096Strasz
538270096Strasz	node_expand_maps(n, false);
539270096Strasz}
540270096Strasz
541270096Straszvoid
542270096Strasznode_expand_indirect_maps(struct node *n)
543270096Strasz{
544270096Strasz
545270096Strasz	node_expand_maps(n, true);
546270096Strasz}
547270096Strasz
548270096Straszstatic char *
549270096Strasznode_path_x(const struct node *n, char *x)
550270096Strasz{
551270096Strasz	char *path;
552270096Strasz
553270096Strasz	if (n->n_parent == NULL)
554270096Strasz		return (x);
555270096Strasz
556270096Strasz	/*
557270096Strasz	 * Return "/-" for direct maps only if we were asked for path
558270096Strasz	 * to the "/-" node itself, not to any of its subnodes.
559270096Strasz	 */
560283232Strasz	if (node_is_direct_key(n) && x[0] != '\0')
561270096Strasz		return (x);
562270096Strasz
563270903Strasz	assert(n->n_key[0] != '\0');
564283231Strasz	path = concat(n->n_key, '/', x);
565270096Strasz	free(x);
566270096Strasz
567270096Strasz	return (node_path_x(n->n_parent, path));
568270096Strasz}
569270096Strasz
570270096Strasz/*
571270096Strasz * Return full path for node, consisting of concatenated
572270096Strasz * paths of node itself and all its parents, up to the root.
573270096Strasz */
574270096Straszchar *
575270096Strasznode_path(const struct node *n)
576270096Strasz{
577283227Strasz	char *path;
578283227Strasz	size_t len;
579270096Strasz
580283227Strasz	path = node_path_x(n, checked_strdup(""));
581283227Strasz
582283227Strasz	/*
583283227Strasz	 * Strip trailing slash, unless the whole path is "/".
584283227Strasz	 */
585283227Strasz	len = strlen(path);
586283227Strasz	if (len > 1 && path[len - 1] == '/')
587283227Strasz		path[len - 1] = '\0';
588283227Strasz
589283227Strasz	return (path);
590270096Strasz}
591270096Strasz
592270096Straszstatic char *
593270096Strasznode_options_x(const struct node *n, char *x)
594270096Strasz{
595270096Strasz	char *options;
596270096Strasz
597283229Strasz	if (n == NULL)
598283229Strasz		return (x);
599283229Strasz
600283231Strasz	options = concat(x, ',', n->n_options);
601283229Strasz	free(x);
602270096Strasz
603270096Strasz	return (node_options_x(n->n_parent, options));
604270096Strasz}
605270096Strasz
606270096Strasz/*
607270096Strasz * Return options for node, consisting of concatenated
608270096Strasz * options from the node itself and all its parents,
609270096Strasz * up to the root.
610270096Strasz */
611270096Straszchar *
612270096Strasznode_options(const struct node *n)
613270096Strasz{
614270096Strasz
615270096Strasz	return (node_options_x(n, checked_strdup("")));
616270096Strasz}
617270096Strasz
618270096Straszstatic void
619283239Strasznode_print_indent(const struct node *n, const char *cmdline_options,
620283239Strasz    int indent)
621270096Strasz{
622270096Strasz	const struct node *child, *first_child;
623283239Strasz	char *path, *options, *tmp;
624270096Strasz
625270096Strasz	path = node_path(n);
626283239Strasz	tmp = node_options(n);
627283239Strasz	options = concat(cmdline_options, ',', tmp);
628283239Strasz	free(tmp);
629270096Strasz
630270096Strasz	/*
631270096Strasz	 * Do not show both parent and child node if they have the same
632270096Strasz	 * mountpoint; only show the child node.  This means the typical,
633270096Strasz	 * "key location", map entries are shown in a single line;
634270096Strasz	 * the "key mountpoint1 location2 mountpoint2 location2" entries
635270096Strasz	 * take multiple lines.
636270096Strasz	 */
637270096Strasz	first_child = TAILQ_FIRST(&n->n_children);
638270096Strasz	if (first_child == NULL || TAILQ_NEXT(first_child, n_next) != NULL ||
639270096Strasz	    strcmp(path, node_path(first_child)) != 0) {
640270096Strasz		assert(n->n_location == NULL || n->n_map == NULL);
641270096Strasz		printf("%*.s%-*s %s%-*s %-*s # %s map %s at %s:%d\n",
642270096Strasz		    indent, "",
643270096Strasz		    25 - indent,
644270096Strasz		    path,
645270096Strasz		    options[0] != '\0' ? "-" : " ",
646270096Strasz		    20,
647270096Strasz		    options[0] != '\0' ? options : "",
648270096Strasz		    20,
649270096Strasz		    n->n_location != NULL ? n->n_location : n->n_map != NULL ? n->n_map : "",
650270096Strasz		    node_is_direct_map(n) ? "direct" : "indirect",
651270096Strasz		    indent == 0 ? "referenced" : "defined",
652270096Strasz		    n->n_config_file, n->n_config_line);
653270096Strasz	}
654270096Strasz
655270096Strasz	free(path);
656270096Strasz	free(options);
657270096Strasz
658270096Strasz	TAILQ_FOREACH(child, &n->n_children, n_next)
659283239Strasz		node_print_indent(child, cmdline_options, indent + 2);
660270096Strasz}
661270096Strasz
662283239Strasz/*
663283239Strasz * Recursively print node with all its children.  The cmdline_options
664283239Strasz * argument is used for additional options to be prepended to all the
665283239Strasz * others - usually those are the options passed by command line.
666283239Strasz */
667270096Straszvoid
668283239Strasznode_print(const struct node *n, const char *cmdline_options)
669270096Strasz{
670270096Strasz	const struct node *child;
671270096Strasz
672270096Strasz	TAILQ_FOREACH(child, &n->n_children, n_next)
673283239Strasz		node_print_indent(child, cmdline_options, 0);
674270096Strasz}
675270096Strasz
676279744Straszstatic struct node *
677279744Strasznode_find_x(struct node *node, const char *path)
678270096Strasz{
679270096Strasz	struct node *child, *found;
680270096Strasz	char *tmp;
681272117Strasz	size_t tmplen;
682270096Strasz
683283233Strasz	//log_debugx("looking up %s in %s", path, node_path(node));
684270096Strasz
685283233Strasz	if (!node_is_direct_key(node)) {
686283233Strasz		tmp = node_path(node);
687283233Strasz		tmplen = strlen(tmp);
688283233Strasz		if (strncmp(tmp, path, tmplen) != 0) {
689283233Strasz			free(tmp);
690283233Strasz			return (NULL);
691283233Strasz		}
692283233Strasz		if (path[tmplen] != '/' && path[tmplen] != '\0') {
693283233Strasz			/*
694283233Strasz			 * If we have two map entries like 'foo' and 'foobar', make
695283233Strasz			 * sure the search for 'foobar' won't match 'foo' instead.
696283233Strasz			 */
697283233Strasz			free(tmp);
698283233Strasz			return (NULL);
699283233Strasz		}
700270096Strasz		free(tmp);
701270096Strasz	}
702270096Strasz
703270096Strasz	TAILQ_FOREACH(child, &node->n_children, n_next) {
704279744Strasz		found = node_find_x(child, path);
705270096Strasz		if (found != NULL)
706270096Strasz			return (found);
707270096Strasz	}
708270096Strasz
709283233Strasz	if (node->n_parent == NULL || node_is_direct_key(node))
710283233Strasz		return (NULL);
711283233Strasz
712270096Strasz	return (node);
713270096Strasz}
714270096Strasz
715279744Straszstruct node *
716279744Strasznode_find(struct node *root, const char *path)
717279744Strasz{
718279744Strasz	struct node *node;
719279744Strasz
720283233Strasz	assert(root->n_parent == NULL);
721283233Strasz
722279744Strasz	node = node_find_x(root, path);
723283233Strasz	if (node != NULL)
724283233Strasz		assert(node != root);
725283233Strasz
726279744Strasz	return (node);
727279744Strasz}
728279744Strasz
729270096Strasz/*
730270096Strasz * Canonical form of a map entry looks like this:
731270096Strasz *
732270096Strasz * key [-options] [ [/mountpoint] [-options2] location ... ]
733270096Strasz *
734270096Strasz * Entries for executable maps are slightly different, as they
735270096Strasz * lack the 'key' field and are always single-line; the key field
736270096Strasz * for those maps is taken from 'executable_key' argument.
737270096Strasz *
738270096Strasz * We parse it in such a way that a map always has two levels - first
739270096Strasz * for key, and the second, for the mountpoint.
740270096Strasz */
741270096Straszstatic void
742270096Straszparse_map_yyin(struct node *parent, const char *map, const char *executable_key)
743270096Strasz{
744270096Strasz	char *key = NULL, *options = NULL, *mountpoint = NULL,
745270096Strasz	    *options2 = NULL, *location = NULL;
746270096Strasz	int ret;
747270096Strasz	struct node *node;
748270096Strasz
749270096Strasz	lineno = 1;
750270096Strasz
751270096Strasz	if (executable_key != NULL)
752270096Strasz		key = checked_strdup(executable_key);
753270096Strasz
754270096Strasz	for (;;) {
755270096Strasz		ret = yylex();
756270096Strasz		if (ret == 0 || ret == NEWLINE) {
757270901Strasz			/*
758270901Strasz			 * In case of executable map, the key is always
759270901Strasz			 * non-NULL, even if the map is empty.  So, make sure
760270901Strasz			 * we don't fail empty maps here.
761270901Strasz			 */
762270901Strasz			if ((key != NULL && executable_key == NULL) ||
763270901Strasz			    options != NULL) {
764270096Strasz				log_errx(1, "truncated entry at %s, line %d",
765270096Strasz				    map, lineno);
766270096Strasz			}
767270096Strasz			if (ret == 0 || executable_key != NULL) {
768270096Strasz				/*
769270096Strasz				 * End of file.
770270096Strasz				 */
771270096Strasz				break;
772270096Strasz			} else {
773270096Strasz				key = options = NULL;
774270096Strasz				continue;
775270096Strasz			}
776270096Strasz		}
777270096Strasz		if (key == NULL) {
778270096Strasz			key = checked_strdup(yytext);
779270096Strasz			if (key[0] == '+') {
780270096Strasz				node_new(parent, key, NULL, NULL, map, lineno);
781270096Strasz				key = options = NULL;
782270096Strasz				continue;
783270096Strasz			}
784270096Strasz			continue;
785270096Strasz		} else if (yytext[0] == '-') {
786270096Strasz			if (options != NULL) {
787270096Strasz				log_errx(1, "duplicated options at %s, line %d",
788270096Strasz				    map, lineno);
789270096Strasz			}
790270096Strasz			/*
791270096Strasz			 * +1 to skip leading "-".
792270096Strasz			 */
793270096Strasz			options = checked_strdup(yytext + 1);
794270096Strasz			continue;
795270096Strasz		}
796270096Strasz
797270096Strasz		/*
798270096Strasz		 * We cannot properly handle a situation where the map key
799270096Strasz		 * is "/".  Ignore such entries.
800270096Strasz		 *
801270096Strasz		 * XXX: According to Piete Brooks, Linux automounter uses
802270096Strasz		 *	"/" as a wildcard character in LDAP maps.  Perhaps
803270096Strasz		 *	we should work around this braindamage by substituting
804270096Strasz		 *	"*" for "/"?
805270096Strasz		 */
806270096Strasz		if (strcmp(key, "/") == 0) {
807270096Strasz			log_warnx("nonsensical map key \"/\" at %s, line %d; "
808270096Strasz			    "ignoring map entry ", map, lineno);
809270096Strasz
810270096Strasz			/*
811270096Strasz			 * Skip the rest of the entry.
812270096Strasz			 */
813270096Strasz			do {
814270096Strasz				ret = yylex();
815270096Strasz			} while (ret != 0 && ret != NEWLINE);
816270096Strasz
817270096Strasz			key = options = NULL;
818270096Strasz			continue;
819270096Strasz		}
820270096Strasz
821270096Strasz		//log_debugx("adding map node, %s", key);
822270096Strasz		node = node_new(parent, key, options, NULL, map, lineno);
823270096Strasz		key = options = NULL;
824270096Strasz
825270096Strasz		for (;;) {
826270096Strasz			if (yytext[0] == '/') {
827270096Strasz				if (mountpoint != NULL) {
828270096Strasz					log_errx(1, "duplicated mountpoint "
829270096Strasz					    "in %s, line %d", map, lineno);
830270096Strasz				}
831270096Strasz				if (options2 != NULL || location != NULL) {
832270096Strasz					log_errx(1, "mountpoint out of order "
833270096Strasz					    "in %s, line %d", map, lineno);
834270096Strasz				}
835270096Strasz				mountpoint = checked_strdup(yytext);
836270096Strasz				goto again;
837270096Strasz			}
838270096Strasz
839270096Strasz			if (yytext[0] == '-') {
840270096Strasz				if (options2 != NULL) {
841270096Strasz					log_errx(1, "duplicated options "
842270096Strasz					    "in %s, line %d", map, lineno);
843270096Strasz				}
844270096Strasz				if (location != NULL) {
845270096Strasz					log_errx(1, "options out of order "
846270096Strasz					    "in %s, line %d", map, lineno);
847270096Strasz				}
848270096Strasz				options2 = checked_strdup(yytext + 1);
849270096Strasz				goto again;
850270096Strasz			}
851270096Strasz
852270096Strasz			if (location != NULL) {
853270096Strasz				log_errx(1, "too many arguments "
854270096Strasz				    "in %s, line %d", map, lineno);
855270096Strasz			}
856270096Strasz
857270096Strasz			/*
858270096Strasz			 * If location field starts with colon, e.g. ":/dev/cd0",
859270096Strasz			 * then strip it.
860270096Strasz			 */
861270096Strasz			if (yytext[0] == ':') {
862270096Strasz				location = checked_strdup(yytext + 1);
863270096Strasz				if (location[0] == '\0') {
864270096Strasz					log_errx(1, "empty location in %s, "
865270096Strasz					    "line %d", map, lineno);
866270096Strasz				}
867270096Strasz			} else {
868270096Strasz				location = checked_strdup(yytext);
869270096Strasz			}
870270096Strasz
871270096Strasz			if (mountpoint == NULL)
872270096Strasz				mountpoint = checked_strdup("/");
873270096Strasz			if (options2 == NULL)
874270096Strasz				options2 = checked_strdup("");
875270096Strasz
876270096Strasz#if 0
877270096Strasz			log_debugx("adding map node, %s %s %s",
878270096Strasz			    mountpoint, options2, location);
879270096Strasz#endif
880270096Strasz			node_new(node, mountpoint, options2, location,
881270096Strasz			    map, lineno);
882270096Strasz			mountpoint = options2 = location = NULL;
883270096Straszagain:
884270096Strasz			ret = yylex();
885270096Strasz			if (ret == 0 || ret == NEWLINE) {
886270096Strasz				if (mountpoint != NULL || options2 != NULL ||
887270096Strasz				    location != NULL) {
888270096Strasz					log_errx(1, "truncated entry "
889270096Strasz					    "in %s, line %d", map, lineno);
890270096Strasz				}
891270096Strasz				break;
892270096Strasz			}
893270096Strasz		}
894270096Strasz	}
895270096Strasz}
896270096Strasz
897270902Strasz/*
898270903Strasz * Parse output of a special map called without argument.  It is a list
899270903Strasz * of keys, separated by newlines.  They can contain whitespace, so use
900270903Strasz * getline(3) instead of lexer used for maps.
901270902Strasz */
902270902Straszstatic void
903270902Straszparse_map_keys_yyin(struct node *parent, const char *map)
904270902Strasz{
905270903Strasz	char *line = NULL, *key;
906270903Strasz	size_t linecap = 0;
907270903Strasz	ssize_t linelen;
908270902Strasz
909270902Strasz	lineno = 1;
910270902Strasz
911270902Strasz	for (;;) {
912270903Strasz		linelen = getline(&line, &linecap, yyin);
913270903Strasz		if (linelen < 0) {
914270902Strasz			/*
915270902Strasz			 * End of file.
916270902Strasz			 */
917270902Strasz			break;
918270902Strasz		}
919270903Strasz		if (linelen <= 1) {
920270903Strasz			/*
921270903Strasz			 * Empty line, consisting of just the newline.
922270903Strasz			 */
923270903Strasz			continue;
924270903Strasz		}
925270902Strasz
926270903Strasz		/*
927270903Strasz		 * "-1" to strip the trailing newline.
928270903Strasz		 */
929270903Strasz		key = strndup(line, linelen - 1);
930270903Strasz
931270903Strasz		log_debugx("adding key \"%s\"", key);
932270902Strasz		node_new(parent, key, NULL, NULL, map, lineno);
933270903Strasz		lineno++;
934270902Strasz	}
935270903Strasz	free(line);
936270902Strasz}
937270902Strasz
938270096Straszstatic bool
939270096Straszfile_is_executable(const char *path)
940270096Strasz{
941270096Strasz	struct stat sb;
942270096Strasz	int error;
943270096Strasz
944270096Strasz	error = stat(path, &sb);
945270096Strasz	if (error != 0)
946270096Strasz		log_err(1, "cannot stat %s", path);
947270096Strasz	if ((sb.st_mode & S_IXUSR) || (sb.st_mode & S_IXGRP) ||
948270096Strasz	    (sb.st_mode & S_IXOTH))
949270096Strasz		return (true);
950270096Strasz	return (false);
951270096Strasz}
952270096Strasz
953270096Strasz/*
954270096Strasz * Parse a special map, e.g. "-hosts".
955270096Strasz */
956270096Straszstatic void
957270096Straszparse_special_map(struct node *parent, const char *map, const char *key)
958270096Strasz{
959270096Strasz	char *path;
960270096Strasz	int error, ret;
961270096Strasz
962270096Strasz	assert(map[0] == '-');
963270096Strasz
964270096Strasz	/*
965270096Strasz	 * +1 to skip leading "-" in map name.
966270096Strasz	 */
967270096Strasz	ret = asprintf(&path, "%s/special_%s", AUTO_SPECIAL_PREFIX, map + 1);
968270096Strasz	if (ret < 0)
969270096Strasz		log_err(1, "asprintf");
970270096Strasz
971270096Strasz	yyin = auto_popen(path, key, NULL);
972270096Strasz	assert(yyin != NULL);
973270096Strasz
974270902Strasz	if (key == NULL) {
975270902Strasz		parse_map_keys_yyin(parent, map);
976270902Strasz	} else {
977270902Strasz		parse_map_yyin(parent, map, key);
978270902Strasz	}
979270096Strasz
980270096Strasz	error = auto_pclose(yyin);
981270096Strasz	yyin = NULL;
982270096Strasz	if (error != 0)
983270096Strasz		log_errx(1, "failed to handle special map \"%s\"", map);
984270096Strasz
985270096Strasz	node_expand_includes(parent, false);
986270096Strasz	node_expand_direct_maps(parent);
987270096Strasz
988270096Strasz	free(path);
989270096Strasz}
990270096Strasz
991270096Strasz/*
992270096Strasz * Retrieve and parse map from directory services, e.g. LDAP.
993270096Strasz * Note that it is different from executable maps, in that
994270096Strasz * the include script outputs the whole map to standard output
995270096Strasz * (as opposed to executable maps that only output a single
996270096Strasz * entry, without the key), and it takes the map name as an
997270096Strasz * argument, instead of key.
998270096Strasz */
999270096Straszstatic void
1000270096Straszparse_included_map(struct node *parent, const char *map)
1001270096Strasz{
1002270096Strasz	int error;
1003270096Strasz
1004270096Strasz	assert(map[0] != '-');
1005270096Strasz	assert(map[0] != '/');
1006270096Strasz
1007270096Strasz	error = access(AUTO_INCLUDE_PATH, F_OK);
1008270096Strasz	if (error != 0) {
1009270096Strasz		log_errx(1, "directory services not configured;"
1010270096Strasz		    " %s does not exist", AUTO_INCLUDE_PATH);
1011270096Strasz	}
1012270096Strasz
1013270096Strasz	yyin = auto_popen(AUTO_INCLUDE_PATH, map, NULL);
1014270096Strasz	assert(yyin != NULL);
1015270096Strasz
1016270096Strasz	parse_map_yyin(parent, map, NULL);
1017270096Strasz
1018270096Strasz	error = auto_pclose(yyin);
1019270096Strasz	yyin = NULL;
1020270096Strasz	if (error != 0)
1021270096Strasz		log_errx(1, "failed to handle remote map \"%s\"", map);
1022270096Strasz
1023270096Strasz	node_expand_includes(parent, false);
1024270096Strasz	node_expand_direct_maps(parent);
1025270096Strasz}
1026270096Strasz
1027270096Straszvoid
1028279741Straszparse_map(struct node *parent, const char *map, const char *key,
1029279741Strasz    bool *wildcards)
1030270096Strasz{
1031270096Strasz	char *path = NULL;
1032270096Strasz	int error, ret;
1033270096Strasz	bool executable;
1034270096Strasz
1035270096Strasz	assert(map != NULL);
1036270096Strasz	assert(map[0] != '\0');
1037270096Strasz
1038270096Strasz	log_debugx("parsing map \"%s\"", map);
1039270096Strasz
1040279741Strasz	if (wildcards != NULL)
1041279741Strasz		*wildcards = false;
1042279741Strasz
1043279741Strasz	if (map[0] == '-') {
1044279741Strasz		if (wildcards != NULL)
1045279741Strasz			*wildcards = true;
1046270096Strasz		return (parse_special_map(parent, map, key));
1047279741Strasz	}
1048270096Strasz
1049270096Strasz	if (map[0] == '/') {
1050270096Strasz		path = checked_strdup(map);
1051270096Strasz	} else {
1052270096Strasz		ret = asprintf(&path, "%s/%s", AUTO_MAP_PREFIX, map);
1053270096Strasz		if (ret < 0)
1054270096Strasz			log_err(1, "asprintf");
1055270096Strasz		log_debugx("map \"%s\" maps to \"%s\"", map, path);
1056270096Strasz
1057270096Strasz		/*
1058270096Strasz		 * See if the file exists.  If not, try to obtain the map
1059270096Strasz		 * from directory services.
1060270096Strasz		 */
1061270096Strasz		error = access(path, F_OK);
1062270096Strasz		if (error != 0) {
1063270096Strasz			log_debugx("map file \"%s\" does not exist; falling "
1064270096Strasz			    "back to directory services", path);
1065270096Strasz			return (parse_included_map(parent, map));
1066270096Strasz		}
1067270096Strasz	}
1068270096Strasz
1069270096Strasz	executable = file_is_executable(path);
1070270096Strasz
1071270096Strasz	if (executable) {
1072270096Strasz		log_debugx("map \"%s\" is executable", map);
1073270096Strasz
1074279741Strasz		if (wildcards != NULL)
1075279741Strasz			*wildcards = true;
1076279741Strasz
1077270096Strasz		if (key != NULL) {
1078270096Strasz			yyin = auto_popen(path, key, NULL);
1079270096Strasz		} else {
1080270096Strasz			yyin = auto_popen(path, NULL);
1081270096Strasz		}
1082270096Strasz		assert(yyin != NULL);
1083270096Strasz	} else {
1084270096Strasz		yyin = fopen(path, "r");
1085270096Strasz		if (yyin == NULL)
1086270096Strasz			log_err(1, "unable to open \"%s\"", path);
1087270096Strasz	}
1088270096Strasz
1089270096Strasz	free(path);
1090270096Strasz	path = NULL;
1091270096Strasz
1092270096Strasz	parse_map_yyin(parent, map, executable ? key : NULL);
1093270096Strasz
1094270096Strasz	if (executable) {
1095270096Strasz		error = auto_pclose(yyin);
1096270096Strasz		yyin = NULL;
1097270096Strasz		if (error != 0) {
1098270096Strasz			log_errx(1, "failed to handle executable map \"%s\"",
1099270096Strasz			    map);
1100270096Strasz		}
1101270096Strasz	} else {
1102270096Strasz		fclose(yyin);
1103270096Strasz	}
1104270096Strasz	yyin = NULL;
1105270096Strasz
1106270096Strasz	log_debugx("done parsing map \"%s\"", map);
1107270096Strasz
1108270096Strasz	node_expand_includes(parent, false);
1109270096Strasz	node_expand_direct_maps(parent);
1110270096Strasz}
1111270096Strasz
1112270096Straszstatic void
1113270096Straszparse_master_yyin(struct node *root, const char *master)
1114270096Strasz{
1115270096Strasz	char *mountpoint = NULL, *map = NULL, *options = NULL;
1116270096Strasz	int ret;
1117270096Strasz
1118270096Strasz	/*
1119270096Strasz	 * XXX: 1 gives incorrect values; wtf?
1120270096Strasz	 */
1121270096Strasz	lineno = 0;
1122270096Strasz
1123270096Strasz	for (;;) {
1124270096Strasz		ret = yylex();
1125270096Strasz		if (ret == 0 || ret == NEWLINE) {
1126270096Strasz			if (mountpoint != NULL) {
1127270096Strasz				//log_debugx("adding map for %s", mountpoint);
1128270096Strasz				node_new_map(root, mountpoint, options, map,
1129270096Strasz				    master, lineno);
1130270096Strasz			}
1131270096Strasz			if (ret == 0) {
1132270096Strasz				break;
1133270096Strasz			} else {
1134270096Strasz				mountpoint = map = options = NULL;
1135270096Strasz				continue;
1136270096Strasz			}
1137270096Strasz		}
1138270096Strasz		if (mountpoint == NULL) {
1139270096Strasz			mountpoint = checked_strdup(yytext);
1140270096Strasz		} else if (map == NULL) {
1141270096Strasz			map = checked_strdup(yytext);
1142270096Strasz		} else if (options == NULL) {
1143270096Strasz			/*
1144270096Strasz			 * +1 to skip leading "-".
1145270096Strasz			 */
1146270096Strasz			options = checked_strdup(yytext + 1);
1147270096Strasz		} else {
1148270096Strasz			log_errx(1, "too many arguments at %s, line %d",
1149270096Strasz			    master, lineno);
1150270096Strasz		}
1151270096Strasz	}
1152270096Strasz}
1153270096Strasz
1154270096Straszvoid
1155270096Straszparse_master(struct node *root, const char *master)
1156270096Strasz{
1157270096Strasz
1158270096Strasz	log_debugx("parsing auto_master file at \"%s\"", master);
1159270096Strasz
1160270096Strasz	yyin = fopen(master, "r");
1161270096Strasz	if (yyin == NULL)
1162270096Strasz		err(1, "unable to open %s", master);
1163270096Strasz
1164270096Strasz	parse_master_yyin(root, master);
1165270096Strasz
1166270096Strasz	fclose(yyin);
1167270096Strasz	yyin = NULL;
1168270096Strasz
1169270096Strasz	log_debugx("done parsing \"%s\"", master);
1170270096Strasz
1171270096Strasz	node_expand_includes(root, true);
1172270096Strasz	node_expand_direct_maps(root);
1173270096Strasz}
1174270096Strasz
1175270096Strasz/*
1176270096Strasz * Two things daemon(3) does, that we actually also want to do
1177270096Strasz * when running in foreground, is closing the stdin and chdiring
1178270096Strasz * to "/".  This is what we do here.
1179270096Strasz */
1180270096Straszvoid
1181270096Straszlesser_daemon(void)
1182270096Strasz{
1183270096Strasz	int error, fd;
1184270096Strasz
1185270096Strasz	error = chdir("/");
1186270096Strasz	if (error != 0)
1187270096Strasz		log_warn("chdir");
1188270096Strasz
1189270096Strasz	fd = open(_PATH_DEVNULL, O_RDWR, 0);
1190270096Strasz	if (fd < 0) {
1191270096Strasz		log_warn("cannot open %s", _PATH_DEVNULL);
1192270096Strasz		return;
1193270096Strasz	}
1194270096Strasz
1195270096Strasz	error = dup2(fd, STDIN_FILENO);
1196270096Strasz	if (error != 0)
1197270096Strasz		log_warn("dup2");
1198270096Strasz
1199270096Strasz	error = close(fd);
1200270096Strasz	if (error != 0) {
1201270096Strasz		/* Bloody hell. */
1202270096Strasz		log_warn("close");
1203270096Strasz	}
1204270096Strasz}
1205270096Strasz
1206270096Straszint
1207270096Straszmain(int argc, char **argv)
1208270096Strasz{
1209270096Strasz	char *cmdname;
1210270096Strasz
1211270096Strasz	if (argv[0] == NULL)
1212270096Strasz		log_errx(1, "NULL command name");
1213270096Strasz
1214270096Strasz	cmdname = basename(argv[0]);
1215270096Strasz
1216270096Strasz	if (strcmp(cmdname, "automount") == 0)
1217270096Strasz		return (main_automount(argc, argv));
1218270096Strasz	else if (strcmp(cmdname, "automountd") == 0)
1219270096Strasz		return (main_automountd(argc, argv));
1220270096Strasz	else if (strcmp(cmdname, "autounmountd") == 0)
1221270096Strasz		return (main_autounmountd(argc, argv));
1222270096Strasz	else
1223270096Strasz		log_errx(1, "binary name should be either \"automount\", "
1224270096Strasz		    "\"automountd\", or \"autounmountd\"");
1225270096Strasz}
1226