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