2 * Copyright (c) 2016 The DragonFly Project
3 * Copyright (c) 2014 The FreeBSD Foundation
6 * This software was developed by Edward Tomasz Napierala under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/types.h>
33 #include <sys/ioctl.h>
34 #include <sys/linker.h>
45 #include <vfs/autofs/autofs_ioctl.h>
49 #define AUTOMOUNTD_PIDFILE "/var/run/automountd.pid"
51 static int nchildren
= 0;
53 static int request_id
;
56 done(int request_error
, bool wildcards
)
58 struct autofs_daemon_done add
;
61 memset(&add
, 0, sizeof(add
));
62 add
.add_id
= request_id
;
63 add
.add_wildcards
= wildcards
;
64 add
.add_error
= request_error
;
66 log_debugx("completing request %d with error %d",
67 request_id
, request_error
);
69 error
= ioctl(autofs_fd
, AUTOFSDONE
, &add
);
71 log_warn("AUTOFSDONE");
75 * Remove "fstype=whatever" from optionsp and return the "whatever" part.
78 pick_option(const char *option
, char **optionsp
)
80 char *tofree
, *pair
, *newoptions
;
86 newoptions
= calloc(strlen(*optionsp
) + 1, 1);
87 if (newoptions
== NULL
)
90 while ((pair
= strsep(optionsp
, ",")) != NULL
) {
92 * XXX: strncasecmp(3) perhaps?
94 if (strncmp(pair
, option
, strlen(option
)) == 0) {
95 picked
= checked_strdup(pair
+ strlen(option
));
98 strcat(newoptions
, ",");
101 strcat(newoptions
, pair
);
106 *optionsp
= newoptions
;
112 create_subtree(const struct node
*node
, bool incomplete
)
114 const struct node
*child
;
116 bool wildcard_found
= false;
119 * Skip wildcard nodes.
121 if (strcmp(node
->n_key
, "*") == 0)
124 path
= node_path(node
);
125 log_debugx("creating subtree at %s", path
);
126 create_directory(path
);
129 TAILQ_FOREACH(child
, &node
->n_children
, n_next
) {
130 if (strcmp(child
->n_key
, "*") == 0) {
131 wildcard_found
= true;
136 if (wildcard_found
) {
137 log_debugx("node %s contains wildcard entry; "
138 "not creating its subdirectories due to -d flag",
147 TAILQ_FOREACH(child
, &node
->n_children
, n_next
)
148 create_subtree(child
, incomplete
);
159 handle_request(const struct autofs_daemon_request
*adr
, char *cmdline_options
,
160 bool incomplete_hierarchy
)
163 struct node
*root
, *parent
, *node
;
165 char *key
, *options
, *fstype
, *nobrowse
, *retrycnt
, *tmp
;
169 log_debugx("got request %d: from %s, path %s, prefix \"%s\", "
170 "key \"%s\", options \"%s\"", adr
->adr_id
, adr
->adr_from
,
171 adr
->adr_path
, adr
->adr_prefix
, adr
->adr_key
, adr
->adr_options
);
174 * Try to notify the kernel about any problems.
176 request_id
= adr
->adr_id
;
177 atexit(exit_callback
);
179 if (strncmp(adr
->adr_from
, "map ", 4) != 0) {
180 log_errx(1, "invalid mountfrom \"%s\"; failing request",
184 map
= adr
->adr_from
+ 4; /* 4 for strlen("map "); */
185 root
= node_new_root();
186 if (adr
->adr_prefix
[0] == '\0' || strcmp(adr
->adr_prefix
, "/") == 0) {
188 * Direct map. autofs(4) doesn't have a way to determine
189 * correct map key, but since it's a direct map, we can just
190 * use adr_path instead.
193 key
= checked_strdup(adr
->adr_path
);
198 parent
= node_new_map(root
, checked_strdup(adr
->adr_prefix
),
199 NULL
, checked_strdup(map
),
200 checked_strdup("[kernel request]"), lineno
);
202 if (adr
->adr_key
[0] == '\0')
205 key
= checked_strdup(adr
->adr_key
);
209 * "Wildcards" here actually means "make autofs(4) request
210 * automountd(8) action if the node being looked up does not
211 * exist, even though the parent is marked as cached". This
212 * needs to be done for maps with wildcard entries, but also
213 * for special and executable maps.
215 parse_map(parent
, map
, key
, &wildcards
);
217 wildcards
= node_has_wildcards(parent
);
219 log_debugx("map may contain wildcard entries");
221 log_debugx("map does not contain wildcard entries");
224 node_expand_wildcard(root
, key
);
226 node
= node_find(root
, adr
->adr_path
);
228 log_errx(1, "map %s does not contain key for \"%s\"; "
229 "failing mount", map
, adr
->adr_path
);
232 options
= node_options(node
);
235 * Append options from auto_master.
237 options
= concat(options
, ',', adr
->adr_options
);
240 * Prepend options passed via automountd(8) command line.
242 options
= concat(cmdline_options
, ',', options
);
244 if (node
->n_location
== NULL
) {
245 log_debugx("found node defined at %s:%d; not a mountpoint",
246 node
->n_config_file
, node
->n_config_line
);
248 nobrowse
= pick_option("nobrowse", &options
);
249 if (nobrowse
!= NULL
&& key
== NULL
) {
250 log_debugx("skipping map %s due to \"nobrowse\" "
251 "option; exiting", map
);
255 * Exit without calling exit_callback().
261 * Not a mountpoint; create directories in the autofs mount
262 * and complete the request.
264 create_subtree(node
, incomplete_hierarchy
);
266 if (incomplete_hierarchy
&& key
!= NULL
) {
268 * We still need to create the single subdirectory
269 * user is trying to access.
271 tmp
= concat(adr
->adr_path
, '/', key
);
272 node
= node_find(root
, tmp
);
274 create_subtree(node
, false);
277 log_debugx("nothing to mount; exiting");
281 * Exit without calling exit_callback().
286 log_debugx("found node defined at %s:%d; it is a mountpoint",
287 node
->n_config_file
, node
->n_config_line
);
290 node_expand_ampersand(node
, key
);
291 error
= node_expand_defined(node
);
293 log_errx(1, "variable expansion failed for %s; "
294 "failing mount", adr
->adr_path
);
298 * Append "automounted".
300 options
= concat(options
, ',', "automounted");
303 * Remove "nobrowse", mount(8) doesn't understand it.
305 pick_option("nobrowse", &options
);
310 fstype
= pick_option("fstype=", &options
);
311 if (fstype
== NULL
) {
312 log_debugx("fstype not specified in options; "
313 "defaulting to \"nfs\"");
314 fstype
= checked_strdup("nfs");
317 if (strcmp(fstype
, "nfs") == 0) {
319 * The mount_nfs(8) command defaults to retry undefinitely.
320 * We do not want that behaviour, because it leaves mount_nfs(8)
321 * instances and automountd(8) children hanging forever.
322 * Disable retries unless the option was passed explicitly.
324 retrycnt
= pick_option("retrycnt=", &options
);
325 if (retrycnt
== NULL
) {
326 log_debugx("retrycnt not specified in options; "
328 options
= concat(options
, ',', "retrycnt=1");
330 options
= concat(options
, ',',
331 concat("retrycnt", '=', retrycnt
));
335 f
= auto_popen("mount", "-t", fstype
, "-o", options
,
336 node
->n_location
, adr
->adr_path
, NULL
);
338 error
= auto_pclose(f
);
340 log_errx(1, "mount failed");
342 log_debugx("mount done; exiting");
346 * Exit without calling exit_callback().
352 sigchld_handler(int dummy __unused
)
356 * The only purpose of this handler is to make SIGCHLD
357 * interrupt the AUTOFSREQUEST ioctl(2), so we can call
358 * wait_for_children().
363 register_sigchld(void)
368 bzero(&sa
, sizeof(sa
));
369 sa
.sa_handler
= sigchld_handler
;
370 sigfillset(&sa
.sa_mask
);
371 error
= sigaction(SIGCHLD
, &sa
, NULL
);
373 log_err(1, "sigaction");
379 wait_for_children(bool block
)
387 * If "block" is true, wait for at least one process.
389 if (block
&& num
== 0)
390 pid
= wait4(-1, &status
, 0, NULL
);
392 pid
= wait4(-1, &status
, WNOHANG
, NULL
);
395 if (WIFSIGNALED(status
)) {
396 log_warnx("child process %d terminated with signal %d",
397 pid
, WTERMSIG(status
));
398 } else if (WEXITSTATUS(status
) != 0) {
399 log_debugx("child process %d terminated with exit status %d",
400 pid
, WEXITSTATUS(status
));
402 log_debugx("child process %d terminated gracefully", pid
);
411 usage_automountd(void)
414 fprintf(stderr
, "usage: automountd [-D name=value][-m maxproc]"
415 "[-o opts][-Tidv]\n");
420 main_automountd(int argc
, char **argv
)
424 const char *pidfile_path
= AUTOMOUNTD_PIDFILE
;
425 char *options
= NULL
;
426 struct autofs_daemon_request request
;
427 int ch
, debug
= 0, error
, maxproc
= 30, retval
, saved_errno
;
428 bool dont_daemonize
= false, incomplete_hierarchy
= false;
432 while ((ch
= getopt(argc
, argv
, "D:Tdim:o:v")) != -1) {
435 defined_parse_and_add(optarg
);
439 * For compatibility with other implementations,
445 dont_daemonize
= true;
449 incomplete_hierarchy
= true;
452 maxproc
= atoi(optarg
);
455 options
= concat(options
, ',', optarg
);
471 pidfh
= pidfile_open(pidfile_path
, 0600, &otherpid
);
473 if (errno
== EEXIST
) {
474 log_errx(1, "daemon already running, pid: %jd.",
477 log_err(1, "cannot open or create pidfile \"%s\"",
481 autofs_fd
= open(AUTOFS_PATH
, O_RDWR
| O_CLOEXEC
);
482 if (autofs_fd
< 0 && errno
== ENOENT
) {
484 retval
= kldload("autofs");
486 autofs_fd
= open(AUTOFS_PATH
, O_RDWR
| O_CLOEXEC
);
491 log_err(1, "failed to open %s", AUTOFS_PATH
);
493 if (dont_daemonize
== false) {
494 if (daemon(0, 0) == -1) {
495 log_warn("cannot daemonize");
496 pidfile_remove(pidfh
);
503 pidfile_write(pidfh
);
508 log_debugx("waiting for request from the kernel");
510 memset(&request
, 0, sizeof(request
));
511 error
= ioctl(autofs_fd
, AUTOFSREQUEST
, &request
);
513 if (errno
== EINTR
) {
514 nchildren
-= wait_for_children(false);
515 assert(nchildren
>= 0);
519 log_err(1, "AUTOFSREQUEST");
522 if (dont_daemonize
) {
523 log_debugx("not forking due to -d flag; "
524 "will exit after servicing a single request");
526 nchildren
-= wait_for_children(false);
527 assert(nchildren
>= 0);
529 while (maxproc
> 0 && nchildren
>= maxproc
) {
530 log_debugx("maxproc limit of %d child processes hit; "
531 "waiting for child process to exit", maxproc
);
532 nchildren
-= wait_for_children(true);
533 assert(nchildren
>= 0);
535 log_debugx("got request; forking child process #%d",
546 pidfile_close(pidfh
);
547 handle_request(&request
, options
, incomplete_hierarchy
);
550 pidfile_close(pidfh
);