xref: /netbsd-src/usr.sbin/autofs/automountd.c (revision d7d7f74c03a83d5f4adb8334de8735eeffc16dac)
1*d7d7f74cSchristos /*	$NetBSD: automountd.c,v 1.2 2018/01/11 13:44:26 christos Exp $	*/
2b985414bSchristos 
3b985414bSchristos /*-
4b985414bSchristos  * Copyright (c) 2017 The NetBSD Foundation, Inc.
5b985414bSchristos  * Copyright (c) 2016 The DragonFly Project
6b985414bSchristos  * Copyright (c) 2014 The FreeBSD Foundation
7b985414bSchristos  * All rights reserved.
8b985414bSchristos  *
9b985414bSchristos  * This code is derived from software contributed to The NetBSD Foundation
10b985414bSchristos  * by Tomohiro Kusumi <kusumi.tomohiro@gmail.com>.
11b985414bSchristos  *
12b985414bSchristos  * This software was developed by Edward Tomasz Napierala under sponsorship
13b985414bSchristos  * from the FreeBSD Foundation.
14b985414bSchristos  *
15b985414bSchristos  * Redistribution and use in source and binary forms, with or without
16b985414bSchristos  * modification, are permitted provided that the following conditions
17b985414bSchristos  * are met:
18b985414bSchristos  * 1. Redistributions of source code must retain the above copyright
19b985414bSchristos  *    notice, this list of conditions and the following disclaimer.
20b985414bSchristos  * 2. Redistributions in binary form must reproduce the above copyright
21b985414bSchristos  *    notice, this list of conditions and the following disclaimer in the
22b985414bSchristos  *    documentation and/or other materials provided with the distribution.
23b985414bSchristos  *
24b985414bSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25b985414bSchristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26b985414bSchristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27b985414bSchristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28b985414bSchristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29b985414bSchristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30b985414bSchristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31b985414bSchristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32b985414bSchristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33b985414bSchristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34b985414bSchristos  * SUCH DAMAGE.
35b985414bSchristos  *
36b985414bSchristos  */
37b985414bSchristos #include <sys/cdefs.h>
38*d7d7f74cSchristos __RCSID("$NetBSD: automountd.c,v 1.2 2018/01/11 13:44:26 christos Exp $");
39b985414bSchristos 
40b985414bSchristos #include <sys/types.h>
41b985414bSchristos #include <sys/ioctl.h>
42b985414bSchristos #include <sys/module.h>
43b985414bSchristos #include <sys/wait.h>
44b985414bSchristos #include <assert.h>
45b985414bSchristos #include <errno.h>
46b985414bSchristos #include <fcntl.h>
47b985414bSchristos #include <signal.h>
48b985414bSchristos #include <stdio.h>
49b985414bSchristos #include <stdlib.h>
50b985414bSchristos #include <string.h>
51b985414bSchristos #include <unistd.h>
52b985414bSchristos #include <util.h>
53b985414bSchristos #include <fs/autofs/autofs_ioctl.h>
54b985414bSchristos 
55b985414bSchristos #include "common.h"
56b985414bSchristos 
57b985414bSchristos static int nchildren = 0;
58b985414bSchristos static int autofs_fd;
59b985414bSchristos static int request_id;
60b985414bSchristos static char nfs_def_retry[] = "1";
61b985414bSchristos 
62b985414bSchristos static void
done(int request_error,bool wildcards)63b985414bSchristos done(int request_error, bool wildcards)
64b985414bSchristos {
65b985414bSchristos 	struct autofs_daemon_done add;
66b985414bSchristos 	int error;
67b985414bSchristos 
68b985414bSchristos 	memset(&add, 0, sizeof(add));
69b985414bSchristos 	add.add_id = request_id;
70b985414bSchristos 	add.add_wildcards = wildcards;
71b985414bSchristos 	add.add_error = request_error;
72b985414bSchristos 
73b985414bSchristos 	log_debugx("completing request %d with error %d",
74b985414bSchristos 	    request_id, request_error);
75b985414bSchristos 
76b985414bSchristos 	error = ioctl(autofs_fd, AUTOFSDONE, &add);
77b985414bSchristos 	if (error != 0)
78b985414bSchristos 		log_warn("AUTOFSDONE");
79b985414bSchristos }
80b985414bSchristos 
81b985414bSchristos /*
82b985414bSchristos  * Remove "fstype=whatever" from optionsp and return the "whatever" part.
83b985414bSchristos  */
84b985414bSchristos static char *
pick_option(const char * option,char ** optionsp)85b985414bSchristos pick_option(const char *option, char **optionsp)
86b985414bSchristos {
87b985414bSchristos 	char *tofree, *pair, *newoptions;
88b985414bSchristos 	char *picked = NULL;
89b985414bSchristos 	bool first = true;
90b985414bSchristos 
91b985414bSchristos 	tofree = *optionsp;
92b985414bSchristos 
93b985414bSchristos 	newoptions = calloc(1, strlen(*optionsp) + 1);
94b985414bSchristos 	if (newoptions == NULL)
95b985414bSchristos 		log_err(1, "calloc");
96b985414bSchristos 
97b985414bSchristos 	size_t olen = strlen(option);
98b985414bSchristos 	while ((pair = strsep(optionsp, ",")) != NULL) {
99b985414bSchristos 		/*
100b985414bSchristos 		 * XXX: strncasecmp(3) perhaps?
101b985414bSchristos 		 */
102b985414bSchristos 		if (strncmp(pair, option, olen) == 0) {
103b985414bSchristos 			picked = checked_strdup(pair + olen);
104b985414bSchristos 		} else {
105b985414bSchristos 			if (first)
106b985414bSchristos 				first = false;
107b985414bSchristos 			else
108b985414bSchristos 				strcat(newoptions, ",");
109b985414bSchristos 			strcat(newoptions, pair);
110b985414bSchristos 		}
111b985414bSchristos 	}
112b985414bSchristos 
113b985414bSchristos 	free(tofree);
114b985414bSchristos 	*optionsp = newoptions;
115b985414bSchristos 
116b985414bSchristos 	return picked;
117b985414bSchristos }
118b985414bSchristos 
119b985414bSchristos static void
create_subtree(const struct node * node,bool incomplete)120b985414bSchristos create_subtree(const struct node *node, bool incomplete)
121b985414bSchristos {
122b985414bSchristos 	const struct node *child;
123b985414bSchristos 	char *path;
124b985414bSchristos 	bool wildcard_found = false;
125b985414bSchristos 
126b985414bSchristos 	/*
127b985414bSchristos 	 * Skip wildcard nodes.
128b985414bSchristos 	 */
129b985414bSchristos 	if (strcmp(node->n_key, "*") == 0)
130b985414bSchristos 		return;
131b985414bSchristos 
132b985414bSchristos 	path = node_path(node);
133b985414bSchristos 	log_debugx("creating subtree at %s", path);
134b985414bSchristos 	create_directory(path);
135b985414bSchristos 
136b985414bSchristos 	if (incomplete) {
137b985414bSchristos 		TAILQ_FOREACH(child, &node->n_children, n_next) {
138b985414bSchristos 			if (strcmp(child->n_key, "*") == 0) {
139b985414bSchristos 				wildcard_found = true;
140b985414bSchristos 				break;
141b985414bSchristos 			}
142b985414bSchristos 		}
143b985414bSchristos 
144b985414bSchristos 		if (wildcard_found) {
145b985414bSchristos 			log_debugx("node %s contains wildcard entry; "
146b985414bSchristos 			    "not creating its subdirectories due to -d flag",
147b985414bSchristos 			    path);
148b985414bSchristos 			free(path);
149b985414bSchristos 			return;
150b985414bSchristos 		}
151b985414bSchristos 	}
152b985414bSchristos 
153b985414bSchristos 	free(path);
154b985414bSchristos 
155b985414bSchristos 	TAILQ_FOREACH(child, &node->n_children, n_next)
156b985414bSchristos 		create_subtree(child, incomplete);
157b985414bSchristos }
158b985414bSchristos 
159b985414bSchristos static void
exit_callback(void)160b985414bSchristos exit_callback(void)
161b985414bSchristos {
162b985414bSchristos 
163b985414bSchristos 	done(EIO, true);
164b985414bSchristos }
165b985414bSchristos 
166*d7d7f74cSchristos __dead static void
handle_request(const struct autofs_daemon_request * adr,char * cmdline_options,bool incomplete_hierarchy)167b985414bSchristos handle_request(const struct autofs_daemon_request *adr, char *cmdline_options,
168b985414bSchristos     bool incomplete_hierarchy)
169b985414bSchristos {
170b985414bSchristos 	const char *map;
171b985414bSchristos 	struct node *root, *parent, *node;
172b985414bSchristos 	FILE *f;
173b985414bSchristos 	char *key, *options, *fstype, *nobrowse, *retrycnt, *tmp;
174b985414bSchristos 	int error;
175b985414bSchristos 	bool wildcards;
176b985414bSchristos 
177b985414bSchristos 	log_debugx("got request %d: from %s, path %s, prefix \"%s\", "
178b985414bSchristos 	    "key \"%s\", options \"%s\"", adr->adr_id, adr->adr_from,
179b985414bSchristos 	    adr->adr_path, adr->adr_prefix, adr->adr_key, adr->adr_options);
180b985414bSchristos 
181b985414bSchristos 	/*
182b985414bSchristos 	 * Try to notify the kernel about any problems.
183b985414bSchristos 	 */
184b985414bSchristos 	request_id = adr->adr_id;
185b985414bSchristos 	atexit(exit_callback);
186b985414bSchristos 
187b985414bSchristos 	if (strncmp(adr->adr_from, "map ", 4) != 0) {
188b985414bSchristos 		log_errx(1, "invalid mountfrom \"%s\"; failing request",
189b985414bSchristos 		    adr->adr_from);
190b985414bSchristos 	}
191b985414bSchristos 
192b985414bSchristos 	map = adr->adr_from + 4; /* 4 for strlen("map "); */
193b985414bSchristos 	root = node_new_root();
194b985414bSchristos 	if (adr->adr_prefix[0] == '\0' || strcmp(adr->adr_prefix, "/") == 0) {
195b985414bSchristos 		/*
196b985414bSchristos 		 * Direct map.  autofs(4) doesn't have a way to determine
197b985414bSchristos 		 * correct map key, but since it's a direct map, we can just
198b985414bSchristos 		 * use adr_path instead.
199b985414bSchristos 		 */
200b985414bSchristos 		parent = root;
201b985414bSchristos 		key = checked_strdup(adr->adr_path);
202b985414bSchristos 	} else {
203b985414bSchristos 		/*
204b985414bSchristos 		 * Indirect map.
205b985414bSchristos 		 */
206b985414bSchristos 		parent = node_new_map(root, checked_strdup(adr->adr_prefix),
207b985414bSchristos 		    NULL,  checked_strdup(map),
208b985414bSchristos 		    checked_strdup("[kernel request]"), lineno);
209b985414bSchristos 
210b985414bSchristos 		if (adr->adr_key[0] == '\0')
211b985414bSchristos 			key = NULL;
212b985414bSchristos 		else
213b985414bSchristos 			key = checked_strdup(adr->adr_key);
214b985414bSchristos 	}
215b985414bSchristos 
216b985414bSchristos 	/*
217b985414bSchristos 	 * "Wildcards" here actually means "make autofs(4) request
218b985414bSchristos 	 * automountd(8) action if the node being looked up does not
219b985414bSchristos 	 * exist, even though the parent is marked as cached".  This
220b985414bSchristos 	 * needs to be done for maps with wildcard entries, but also
221b985414bSchristos 	 * for special and executable maps.
222b985414bSchristos 	 */
223b985414bSchristos 	parse_map(parent, map, key, &wildcards);
224b985414bSchristos 	if (!wildcards)
225b985414bSchristos 		wildcards = node_has_wildcards(parent);
226b985414bSchristos 	if (wildcards)
227b985414bSchristos 		log_debugx("map may contain wildcard entries");
228b985414bSchristos 	else
229b985414bSchristos 		log_debugx("map does not contain wildcard entries");
230b985414bSchristos 
231b985414bSchristos 	if (key != NULL)
232b985414bSchristos 		node_expand_wildcard(root, key);
233b985414bSchristos 
234b985414bSchristos 	node = node_find(root, adr->adr_path);
235b985414bSchristos 	if (node == NULL) {
236b985414bSchristos 		log_errx(1, "map %s does not contain key for \"%s\"; "
237b985414bSchristos 		    "failing mount", map, adr->adr_path);
238b985414bSchristos 	}
239b985414bSchristos 
240b985414bSchristos 	options = node_options(node);
241b985414bSchristos 
242b985414bSchristos 	/*
243b985414bSchristos 	 * Append options from auto_master.
244b985414bSchristos 	 */
245b985414bSchristos 	options = concat(options, ',', adr->adr_options);
246b985414bSchristos 
247b985414bSchristos 	/*
248b985414bSchristos 	 * Prepend options passed via automountd(8) command line.
249b985414bSchristos 	 */
250b985414bSchristos 	options = concat(cmdline_options, ',', options);
251b985414bSchristos 
252b985414bSchristos 	if (node->n_location == NULL) {
253b985414bSchristos 		log_debugx("found node defined at %s:%d; not a mountpoint",
254b985414bSchristos 		    node->n_config_file, node->n_config_line);
255b985414bSchristos 
256b985414bSchristos 		nobrowse = pick_option("nobrowse", &options);
257b985414bSchristos 		if (nobrowse != NULL && key == NULL) {
258b985414bSchristos 			log_debugx("skipping map %s due to \"nobrowse\" "
259b985414bSchristos 			    "option; exiting", map);
260b985414bSchristos 			done(0, true);
261b985414bSchristos 
262b985414bSchristos 			/*
263b985414bSchristos 			 * Exit without calling exit_callback().
264b985414bSchristos 			 */
265*d7d7f74cSchristos 			quick_exit(EXIT_SUCCESS);
266b985414bSchristos 		}
267b985414bSchristos 
268b985414bSchristos 		/*
269b985414bSchristos 		 * Not a mountpoint; create directories in the autofs mount
270b985414bSchristos 		 * and complete the request.
271b985414bSchristos 		 */
272b985414bSchristos 		create_subtree(node, incomplete_hierarchy);
273b985414bSchristos 
274b985414bSchristos 		if (incomplete_hierarchy && key != NULL) {
275b985414bSchristos 			/*
276b985414bSchristos 			 * We still need to create the single subdirectory
277b985414bSchristos 			 * user is trying to access.
278b985414bSchristos 			 */
279b985414bSchristos 			tmp = concat(adr->adr_path, '/', key);
280b985414bSchristos 			node = node_find(root, tmp);
281b985414bSchristos 			if (node != NULL)
282b985414bSchristos 				create_subtree(node, false);
283b985414bSchristos 		}
284b985414bSchristos 
285b985414bSchristos 		log_debugx("nothing to mount; exiting");
286b985414bSchristos 		done(0, wildcards);
287b985414bSchristos 
288b985414bSchristos 		/*
289b985414bSchristos 		 * Exit without calling exit_callback().
290b985414bSchristos 		 */
291*d7d7f74cSchristos 		quick_exit(EXIT_SUCCESS);
292b985414bSchristos 	}
293b985414bSchristos 
294b985414bSchristos 	log_debugx("found node defined at %s:%d; it is a mountpoint",
295b985414bSchristos 	    node->n_config_file, node->n_config_line);
296b985414bSchristos 
297b985414bSchristos 	if (key != NULL)
298b985414bSchristos 		node_expand_ampersand(node, key);
299b985414bSchristos 	error = node_expand_defined(node);
300b985414bSchristos 	if (error != 0) {
301b985414bSchristos 		log_errx(1, "variable expansion failed for %s; "
302b985414bSchristos 		    "failing mount", adr->adr_path);
303b985414bSchristos 	}
304b985414bSchristos 
305b985414bSchristos 	/*
306b985414bSchristos 	 * Append "automounted".
307b985414bSchristos 	 */
308b985414bSchristos 	options = concat(options, ',', "automounted");
309b985414bSchristos 
310b985414bSchristos 	/*
311b985414bSchristos 	 * Remove "nobrowse", mount(8) doesn't understand it.
312b985414bSchristos 	 */
313b985414bSchristos 	pick_option("nobrowse", &options);
314b985414bSchristos 
315b985414bSchristos 	/*
316b985414bSchristos 	 * Figure out fstype.
317b985414bSchristos 	 */
318b985414bSchristos 	fstype = pick_option("fstype=", &options);
319b985414bSchristos 	if (fstype == NULL) {
320b985414bSchristos 		log_debugx("fstype not specified in options; "
321b985414bSchristos 		    "defaulting to \"nfs\"");
322b985414bSchristos 		fstype = checked_strdup("nfs");
323b985414bSchristos 	}
324b985414bSchristos 
325b985414bSchristos 	retrycnt = NULL;
326b985414bSchristos 	if (strcmp(fstype, "nfs") == 0) {
327b985414bSchristos 		/*
328b985414bSchristos 		 * The mount_nfs(8) command defaults to retry DEF_RETRY(10000).
329b985414bSchristos 		 * We do not want that behaviour, because it leaves mount_nfs(8)
330b985414bSchristos 		 * instances and automountd(8) children hanging forever.
331b985414bSchristos 		 * Disable retries unless the option was passed explicitly.
332b985414bSchristos 		 */
333b985414bSchristos 		retrycnt = pick_option("retrycnt=", &options);
334b985414bSchristos 		if (retrycnt == NULL) {
335b985414bSchristos 			log_debugx("retrycnt not specified in options; "
336b985414bSchristos 			    "defaulting to 1");
337b985414bSchristos 			retrycnt = nfs_def_retry;
338b985414bSchristos 		}
339b985414bSchristos 	}
340b985414bSchristos 
341b985414bSchristos 	/*
342b985414bSchristos 	 * NetBSD doesn't have -o retrycnt=... option which is available
343b985414bSchristos 	 * on FreeBSD and DragonFlyBSD, so use -R if the target type is NFS
344b985414bSchristos 	 * (or add -o retrycnt=... to mount_nfs(8)).
345b985414bSchristos 	 */
346b985414bSchristos 	if (retrycnt) {
347b985414bSchristos 		assert(!strcmp(fstype, "nfs"));
348b985414bSchristos 		f = auto_popen("mount_nfs", "-o", options, "-R", retrycnt,
349b985414bSchristos 		    node->n_location, adr->adr_path, NULL);
350b985414bSchristos 	} else {
351b985414bSchristos 		f = auto_popen("mount", "-t", fstype, "-o", options,
352b985414bSchristos 		    node->n_location, adr->adr_path, NULL);
353b985414bSchristos 	}
354b985414bSchristos 	assert(f != NULL);
355b985414bSchristos 	error = auto_pclose(f);
356b985414bSchristos 	if (error != 0)
357b985414bSchristos 		log_errx(1, "mount failed");
358b985414bSchristos 
359b985414bSchristos 	log_debugx("mount done; exiting");
360b985414bSchristos 	done(0, wildcards);
361b985414bSchristos 
362b985414bSchristos 	/*
363b985414bSchristos 	 * Exit without calling exit_callback().
364b985414bSchristos 	 */
365*d7d7f74cSchristos 	quick_exit(EXIT_SUCCESS);
366b985414bSchristos }
367b985414bSchristos 
368b985414bSchristos static void
sigchld_handler(int dummy __unused)369b985414bSchristos sigchld_handler(int dummy __unused)
370b985414bSchristos {
371b985414bSchristos 
372b985414bSchristos 	/*
373b985414bSchristos 	 * The only purpose of this handler is to make SIGCHLD
374b985414bSchristos 	 * interrupt the AUTOFSREQUEST ioctl(2), so we can call
375b985414bSchristos 	 * wait_for_children().
376b985414bSchristos 	 */
377b985414bSchristos }
378b985414bSchristos 
379b985414bSchristos static void
register_sigchld(void)380b985414bSchristos register_sigchld(void)
381b985414bSchristos {
382b985414bSchristos 	struct sigaction sa;
383b985414bSchristos 	int error;
384b985414bSchristos 
385b985414bSchristos 	bzero(&sa, sizeof(sa));
386b985414bSchristos 	sa.sa_handler = sigchld_handler;
387b985414bSchristos 	sigfillset(&sa.sa_mask);
388b985414bSchristos 	error = sigaction(SIGCHLD, &sa, NULL);
389b985414bSchristos 	if (error != 0)
390b985414bSchristos 		log_err(1, "sigaction");
391b985414bSchristos }
392b985414bSchristos 
393b985414bSchristos 
394b985414bSchristos static int
wait_for_children(bool block)395b985414bSchristos wait_for_children(bool block)
396b985414bSchristos {
397b985414bSchristos 	pid_t pid;
398b985414bSchristos 	int status;
399b985414bSchristos 	int num = 0;
400b985414bSchristos 
401b985414bSchristos 	for (;;) {
402b985414bSchristos 		/*
403b985414bSchristos 		 * If "block" is true, wait for at least one process.
404b985414bSchristos 		 */
405b985414bSchristos 		if (block && num == 0)
406b985414bSchristos 			pid = wait4(-1, &status, 0, NULL);
407b985414bSchristos 		else
408b985414bSchristos 			pid = wait4(-1, &status, WNOHANG, NULL);
409b985414bSchristos 		if (pid <= 0)
410b985414bSchristos 			break;
411b985414bSchristos 		if (WIFSIGNALED(status)) {
412b985414bSchristos 			log_warnx("child process %d terminated with signal %d",
413b985414bSchristos 			    pid, WTERMSIG(status));
414b985414bSchristos 		} else if (WEXITSTATUS(status) != 0) {
415b985414bSchristos 			log_debugx("child process %d terminated with exit "
416b985414bSchristos 			    "status %d", pid, WEXITSTATUS(status));
417b985414bSchristos 		} else {
418b985414bSchristos 			log_debugx("child process %d terminated gracefully",
419b985414bSchristos 			    pid);
420b985414bSchristos 		}
421b985414bSchristos 		num++;
422b985414bSchristos 	}
423b985414bSchristos 
424b985414bSchristos 	return num;
425b985414bSchristos }
426b985414bSchristos 
427*d7d7f74cSchristos __dead static void
usage_automountd(void)428b985414bSchristos usage_automountd(void)
429b985414bSchristos {
430b985414bSchristos 
431*d7d7f74cSchristos 	fprintf(stderr, "Usage: %s [-D name=value][-m maxproc]"
432*d7d7f74cSchristos 	    "[-o opts][-Tidv]\n", getprogname());
433*d7d7f74cSchristos 	exit(EXIT_FAILURE);
434b985414bSchristos }
435b985414bSchristos 
436b985414bSchristos static int
load_autofs(void)437b985414bSchristos load_autofs(void)
438b985414bSchristos {
439b985414bSchristos 	modctl_load_t args = {
440b985414bSchristos 		.ml_filename = "autofs",
441b985414bSchristos 		.ml_flags = MODCTL_NO_PROP,
442b985414bSchristos 		.ml_props = NULL,
443b985414bSchristos 		.ml_propslen = 0
444b985414bSchristos 	};
445b985414bSchristos 	int error;
446b985414bSchristos 
447b985414bSchristos 	error = modctl(MODCTL_LOAD, &args);
448b985414bSchristos 	if (error && errno != EEXIST)
449b985414bSchristos 		log_warn("failed to load %s: %s", args.ml_filename,
450b985414bSchristos 		    strerror(errno));
451b985414bSchristos 
452b985414bSchristos 	return error;
453b985414bSchristos }
454b985414bSchristos 
455b985414bSchristos int
main_automountd(int argc,char ** argv)456b985414bSchristos main_automountd(int argc, char **argv)
457b985414bSchristos {
458b985414bSchristos 	pid_t pid;
459b985414bSchristos 	char *options = NULL;
460b985414bSchristos 	struct autofs_daemon_request request;
461b985414bSchristos 	int ch, debug = 0, error, maxproc = 30, saved_errno;
462b985414bSchristos 	bool dont_daemonize = false, incomplete_hierarchy = false;
463b985414bSchristos 
464b985414bSchristos 	defined_init();
465b985414bSchristos 
466b985414bSchristos 	while ((ch = getopt(argc, argv, "D:Tdim:o:v")) != -1) {
467b985414bSchristos 		switch (ch) {
468b985414bSchristos 		case 'D':
469b985414bSchristos 			defined_parse_and_add(optarg);
470b985414bSchristos 			break;
471b985414bSchristos 		case 'T':
472b985414bSchristos 			/*
473b985414bSchristos 			 * For compatibility with other implementations,
474b985414bSchristos 			 * such as OS X.
475b985414bSchristos 			 */
476b985414bSchristos 			debug++;
477b985414bSchristos 			break;
478b985414bSchristos 		case 'd':
479b985414bSchristos 			dont_daemonize = true;
480b985414bSchristos 			debug++;
481b985414bSchristos 			break;
482b985414bSchristos 		case 'i':
483b985414bSchristos 			incomplete_hierarchy = true;
484b985414bSchristos 			break;
485b985414bSchristos 		case 'm':
486b985414bSchristos 			maxproc = atoi(optarg);
487b985414bSchristos 			break;
488b985414bSchristos 		case 'o':
489b985414bSchristos 			options = concat(options, ',', optarg);
490b985414bSchristos 			break;
491b985414bSchristos 		case 'v':
492b985414bSchristos 			debug++;
493b985414bSchristos 			break;
494b985414bSchristos 		case '?':
495b985414bSchristos 		default:
496b985414bSchristos 			usage_automountd();
497b985414bSchristos 		}
498b985414bSchristos 	}
499b985414bSchristos 	argc -= optind;
500b985414bSchristos 	if (argc != 0)
501b985414bSchristos 		usage_automountd();
502b985414bSchristos 
503b985414bSchristos 	log_init(debug);
504b985414bSchristos 
505b985414bSchristos 	/*
506b985414bSchristos 	 * XXX: Workaround for NetBSD.
507b985414bSchristos 	 * load_autofs() should be needed only if open(2) failed with ENXIO.
508b985414bSchristos 	 * We attempt to load autofs before open(2) to suppress below warning
509b985414bSchristos 	 * "module error: incompatible module class for `autofs' (3 != 2)",
510b985414bSchristos 	 * which comes from sys/miscfs/specfs/spec_vnops.c:spec_open().
511b985414bSchristos 	 * spec_open() tries to load autofs as MODULE_CLASS_DRIVER while autofs
512b985414bSchristos 	 * is of MODULE_CLASS_VFS.
513b985414bSchristos 	 */
514b985414bSchristos 	load_autofs();
515b985414bSchristos 
516b985414bSchristos 	/*
517b985414bSchristos 	 * NetBSD needs to check ENXIO here, but might not need ENOENT.
518b985414bSchristos 	 */
519b985414bSchristos 	autofs_fd = open(AUTOFS_PATH, O_RDWR | O_CLOEXEC);
520b985414bSchristos 	if (autofs_fd < 0 && (errno == ENOENT || errno == ENXIO)) {
521b985414bSchristos 		saved_errno = errno;
522b985414bSchristos 		if (!load_autofs())
523b985414bSchristos 			autofs_fd = open(AUTOFS_PATH, O_RDWR | O_CLOEXEC);
524b985414bSchristos 		else
525b985414bSchristos 			errno = saved_errno;
526b985414bSchristos 	}
527b985414bSchristos 	if (autofs_fd < 0)
528b985414bSchristos 		log_err(1, "failed to open %s", AUTOFS_PATH);
529b985414bSchristos 
530b985414bSchristos 	if (dont_daemonize == false) {
531b985414bSchristos 		if (daemon(0, 0) == -1) {
532b985414bSchristos 			log_warn("cannot daemonize");
533b985414bSchristos 			pidfile_clean();
534*d7d7f74cSchristos 			exit(EXIT_FAILURE);
535b985414bSchristos 		}
536b985414bSchristos 	} else {
537b985414bSchristos 		lesser_daemon();
538b985414bSchristos 	}
539b985414bSchristos 
540b985414bSchristos 	/*
541b985414bSchristos 	 * Call pidfile(3) after daemon(3).
542b985414bSchristos 	 */
543b985414bSchristos 	if (pidfile(NULL) == -1) {
544b985414bSchristos 		if (errno == EEXIST)
545b985414bSchristos 			log_errx(1, "daemon already running");
546b985414bSchristos 		else if (errno == ENAMETOOLONG)
547b985414bSchristos 			log_errx(1, "pidfile name too long");
548b985414bSchristos 		log_err(1, "cannot create pidfile");
549b985414bSchristos 	}
550b985414bSchristos 	if (pidfile_lock(NULL) == -1)
551b985414bSchristos 		log_err(1, "cannot lock pidfile");
552b985414bSchristos 
553b985414bSchristos 	register_sigchld();
554b985414bSchristos 
555b985414bSchristos 	for (;;) {
556b985414bSchristos 		log_debugx("waiting for request from the kernel");
557b985414bSchristos 
558b985414bSchristos 		memset(&request, 0, sizeof(request));
559b985414bSchristos 		error = ioctl(autofs_fd, AUTOFSREQUEST, &request);
560b985414bSchristos 		if (error != 0) {
561b985414bSchristos 			if (errno == EINTR) {
562b985414bSchristos 				nchildren -= wait_for_children(false);
563b985414bSchristos 				assert(nchildren >= 0);
564b985414bSchristos 				continue;
565b985414bSchristos 			}
566b985414bSchristos 
567b985414bSchristos 			log_err(1, "AUTOFSREQUEST");
568b985414bSchristos 		}
569b985414bSchristos 
570b985414bSchristos 		if (dont_daemonize) {
571b985414bSchristos 			log_debugx("not forking due to -d flag; "
572b985414bSchristos 			    "will exit after servicing a single request");
573b985414bSchristos 		} else {
574b985414bSchristos 			nchildren -= wait_for_children(false);
575b985414bSchristos 			assert(nchildren >= 0);
576b985414bSchristos 
577b985414bSchristos 			while (maxproc > 0 && nchildren >= maxproc) {
578b985414bSchristos 				log_debugx("maxproc limit of %d child processes"
579b985414bSchristos 				    " hit; waiting for child process to exit",
580b985414bSchristos 				    maxproc);
581b985414bSchristos 				nchildren -= wait_for_children(true);
582b985414bSchristos 				assert(nchildren >= 0);
583b985414bSchristos 			}
584b985414bSchristos 			log_debugx("got request; forking child process #%d",
585b985414bSchristos 			    nchildren);
586b985414bSchristos 			nchildren++;
587b985414bSchristos 
588b985414bSchristos 			pid = fork();
589b985414bSchristos 			if (pid < 0)
590b985414bSchristos 				log_err(1, "fork");
591b985414bSchristos 			if (pid > 0)
592b985414bSchristos 				continue;
593b985414bSchristos 		}
594b985414bSchristos 
595b985414bSchristos 		handle_request(&request, options, incomplete_hierarchy);
596b985414bSchristos 	}
597b985414bSchristos 
598b985414bSchristos 	pidfile_clean();
599b985414bSchristos 
600*d7d7f74cSchristos 	return EXIT_SUCCESS;
601b985414bSchristos }
602