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