2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2016 The DragonFly Project
5 * Copyright (c) 2014 The FreeBSD Foundation
8 * This software was developed by Edward Tomasz Napierala under sponsorship
9 * from the FreeBSD Foundation.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #include <sys/linker.h>
47 #include <vfs/autofs/autofs_ioctl.h>
51 #define AUTOMOUNTD_PIDFILE "/var/run/automountd.pid"
53 static int nchildren
= 0;
55 static int request_id
;
58 done(int request_error
, bool wildcards
)
60 struct autofs_daemon_done add
;
63 memset(&add
, 0, sizeof(add
));
64 add
.add_id
= request_id
;
65 add
.add_wildcards
= wildcards
;
66 add
.add_error
= request_error
;
68 log_debugx("completing request %d with error %d",
69 request_id
, request_error
);
71 error
= ioctl(autofs_fd
, AUTOFSDONE
, &add
);
73 log_warn("AUTOFSDONE");
77 * Remove "fstype=whatever" from optionsp and return the "whatever" part.
80 pick_option(const char *option
, char **optionsp
)
82 char *tofree
, *pair
, *newoptions
;
88 newoptions
= calloc(1, strlen(*optionsp
) + 1);
89 if (newoptions
== NULL
)
92 while ((pair
= strsep(optionsp
, ",")) != NULL
) {
94 * XXX: strncasecmp(3) perhaps?
96 if (strncmp(pair
, option
, strlen(option
)) == 0) {
97 picked
= checked_strdup(pair
+ strlen(option
));
100 strcat(newoptions
, ",");
103 strcat(newoptions
, pair
);
108 *optionsp
= newoptions
;
114 create_subtree(const struct node
*node
, bool incomplete
)
116 const struct node
*child
;
118 bool wildcard_found
= false;
121 * Skip wildcard nodes.
123 if (strcmp(node
->n_key
, "*") == 0)
126 path
= node_path(node
);
127 log_debugx("creating subtree at %s", path
);
128 create_directory(path
);
131 TAILQ_FOREACH(child
, &node
->n_children
, n_next
) {
132 if (strcmp(child
->n_key
, "*") == 0) {
133 wildcard_found
= true;
138 if (wildcard_found
) {
139 log_debugx("node %s contains wildcard entry; "
140 "not creating its subdirectories due to -d flag",
149 TAILQ_FOREACH(child
, &node
->n_children
, n_next
)
150 create_subtree(child
, incomplete
);
161 handle_request(const struct autofs_daemon_request
*adr
, char *cmdline_options
,
162 bool incomplete_hierarchy
)
165 struct node
*root
, *parent
, *node
;
167 char *key
, *options
, *fstype
, *nobrowse
, *retrycnt
, *tmp
;
171 log_debugx("got request %d: from %s, path %s, prefix \"%s\", "
172 "key \"%s\", options \"%s\"", adr
->adr_id
, adr
->adr_from
,
173 adr
->adr_path
, adr
->adr_prefix
, adr
->adr_key
, adr
->adr_options
);
176 * Try to notify the kernel about any problems.
178 request_id
= adr
->adr_id
;
179 atexit(exit_callback
);
181 if (strncmp(adr
->adr_from
, "map ", 4) != 0) {
182 log_errx(1, "invalid mountfrom \"%s\"; failing request",
186 map
= adr
->adr_from
+ 4; /* 4 for strlen("map "); */
187 root
= node_new_root();
188 if (adr
->adr_prefix
[0] == '\0' || strcmp(adr
->adr_prefix
, "/") == 0) {
190 * Direct map. autofs(4) doesn't have a way to determine
191 * correct map key, but since it's a direct map, we can just
192 * use adr_path instead.
195 key
= checked_strdup(adr
->adr_path
);
200 parent
= node_new_map(root
, checked_strdup(adr
->adr_prefix
),
201 NULL
, checked_strdup(map
),
202 checked_strdup("[kernel request]"), lineno
);
204 if (adr
->adr_key
[0] == '\0')
207 key
= checked_strdup(adr
->adr_key
);
211 * "Wildcards" here actually means "make autofs(4) request
212 * automountd(8) action if the node being looked up does not
213 * exist, even though the parent is marked as cached". This
214 * needs to be done for maps with wildcard entries, but also
215 * for special and executable maps.
217 parse_map(parent
, map
, key
, &wildcards
);
219 wildcards
= node_has_wildcards(parent
);
221 log_debugx("map may contain wildcard entries");
223 log_debugx("map does not contain wildcard entries");
226 node_expand_wildcard(root
, key
);
228 node
= node_find(root
, adr
->adr_path
);
230 log_errx(1, "map %s does not contain key for \"%s\"; "
231 "failing mount", map
, adr
->adr_path
);
234 options
= node_options(node
);
237 * Append options from auto_master.
239 options
= concat(options
, ',', adr
->adr_options
);
242 * Prepend options passed via automountd(8) command line.
244 options
= concat(cmdline_options
, ',', options
);
246 if (node
->n_location
== NULL
) {
247 log_debugx("found node defined at %s:%d; not a mountpoint",
248 node
->n_config_file
, node
->n_config_line
);
250 nobrowse
= pick_option("nobrowse", &options
);
251 if (nobrowse
!= NULL
&& key
== NULL
) {
252 log_debugx("skipping map %s due to \"nobrowse\" "
253 "option; exiting", map
);
257 * Exit without calling exit_callback().
263 * Not a mountpoint; create directories in the autofs mount
264 * and complete the request.
266 create_subtree(node
, incomplete_hierarchy
);
268 if (incomplete_hierarchy
&& key
!= NULL
) {
270 * We still need to create the single subdirectory
271 * user is trying to access.
273 tmp
= concat(adr
->adr_path
, '/', key
);
274 node
= node_find(root
, tmp
);
276 create_subtree(node
, false);
279 log_debugx("nothing to mount; exiting");
283 * Exit without calling exit_callback().
288 log_debugx("found node defined at %s:%d; it is a mountpoint",
289 node
->n_config_file
, node
->n_config_line
);
292 node_expand_ampersand(node
, key
);
293 error
= node_expand_defined(node
);
295 log_errx(1, "variable expansion failed for %s; "
296 "failing mount", adr
->adr_path
);
300 * Append "automounted".
302 options
= concat(options
, ',', "automounted");
305 * Remove "nobrowse", mount(8) doesn't understand it.
307 pick_option("nobrowse", &options
);
312 fstype
= pick_option("fstype=", &options
);
313 if (fstype
== NULL
) {
314 log_debugx("fstype not specified in options; "
315 "defaulting to \"nfs\"");
316 fstype
= checked_strdup("nfs");
319 if (strcmp(fstype
, "nfs") == 0) {
321 * The mount_nfs(8) command defaults to retry undefinitely.
322 * We do not want that behaviour, because it leaves mount_nfs(8)
323 * instances and automountd(8) children hanging forever.
324 * Disable retries unless the option was passed explicitly.
326 retrycnt
= pick_option("retrycnt=", &options
);
327 if (retrycnt
== NULL
) {
328 log_debugx("retrycnt not specified in options; "
330 options
= concat(options
, ',', "retrycnt=1");
332 options
= concat(options
, ',',
333 concat("retrycnt", '=', retrycnt
));
337 f
= auto_popen("mount", "-t", fstype
, "-o", options
,
338 node
->n_location
, adr
->adr_path
, NULL
);
340 error
= auto_pclose(f
);
342 log_errx(1, "mount failed");
344 log_debugx("mount done; exiting");
348 * Exit without calling exit_callback().
354 sigchld_handler(int dummy __unused
)
358 * The only purpose of this handler is to make SIGCHLD
359 * interrupt the AUTOFSREQUEST ioctl(2), so we can call
360 * wait_for_children().
365 register_sigchld(void)
370 bzero(&sa
, sizeof(sa
));
371 sa
.sa_handler
= sigchld_handler
;
372 sigfillset(&sa
.sa_mask
);
373 error
= sigaction(SIGCHLD
, &sa
, NULL
);
375 log_err(1, "sigaction");
380 wait_for_children(bool block
)
388 * If "block" is true, wait for at least one process.
390 if (block
&& num
== 0)
391 pid
= wait4(-1, &status
, 0, NULL
);
393 pid
= wait4(-1, &status
, WNOHANG
, NULL
);
396 if (WIFSIGNALED(status
)) {
397 log_warnx("child process %d terminated with signal %d",
398 pid
, WTERMSIG(status
));
399 } else if (WEXITSTATUS(status
) != 0) {
400 log_debugx("child process %d terminated with exit status %d",
401 pid
, WEXITSTATUS(status
));
403 log_debugx("child process %d terminated gracefully", pid
);
412 usage_automountd(void)
415 fprintf(stderr
, "usage: automountd [-D name=value][-m maxproc]"
416 "[-o opts][-Tidv]\n");
421 main_automountd(int argc
, char **argv
)
425 const char *pidfile_path
= AUTOMOUNTD_PIDFILE
;
426 char *options
= NULL
;
427 struct autofs_daemon_request request
;
428 int ch
, debug
= 0, error
, maxproc
= 30, retval
, saved_errno
;
429 bool dont_daemonize
= false, incomplete_hierarchy
= false;
433 while ((ch
= getopt(argc
, argv
, "D:Tdim:o:v")) != -1) {
436 defined_parse_and_add(optarg
);
440 * For compatibility with other implementations,
446 dont_daemonize
= true;
450 incomplete_hierarchy
= true;
453 maxproc
= atoi(optarg
);
456 options
= concat(options
, ',', optarg
);
472 pidfh
= pidfile_open(pidfile_path
, 0600, &otherpid
);
474 if (errno
== EEXIST
) {
475 log_errx(1, "daemon already running, pid: %jd.",
478 log_err(1, "cannot open or create pidfile \"%s\"",
482 autofs_fd
= open(AUTOFS_PATH
, O_RDWR
| O_CLOEXEC
);
483 if (autofs_fd
< 0 && errno
== ENOENT
) {
485 retval
= kldload("autofs");
487 autofs_fd
= open(AUTOFS_PATH
, O_RDWR
| O_CLOEXEC
);
492 log_err(1, "failed to open %s", AUTOFS_PATH
);
494 if (dont_daemonize
== false) {
495 if (daemon(0, 0) == -1) {
496 log_warn("cannot daemonize");
497 pidfile_remove(pidfh
);
504 pidfile_write(pidfh
);
509 log_debugx("waiting for request from the kernel");
511 memset(&request
, 0, sizeof(request
));
512 error
= ioctl(autofs_fd
, AUTOFSREQUEST
, &request
);
514 if (errno
== EINTR
) {
515 nchildren
-= wait_for_children(false);
516 assert(nchildren
>= 0);
520 log_err(1, "AUTOFSREQUEST");
523 if (dont_daemonize
) {
524 log_debugx("not forking due to -d flag; "
525 "will exit after servicing a single request");
527 nchildren
-= wait_for_children(false);
528 assert(nchildren
>= 0);
530 while (maxproc
> 0 && nchildren
>= maxproc
) {
531 log_debugx("maxproc limit of %d child processes hit; "
532 "waiting for child process to exit", maxproc
);
533 nchildren
-= wait_for_children(true);
534 assert(nchildren
>= 0);
536 log_debugx("got request; forking child process #%d",
547 pidfile_close(pidfh
);
548 handle_request(&request
, options
, incomplete_hierarchy
);