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