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(1, strlen(*optionsp) + 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");
378 wait_for_children(bool block)
386 * If "block" is true, wait for at least one process.
388 if (block && num == 0)
389 pid = wait4(-1, &status, 0, NULL);
391 pid = wait4(-1, &status, WNOHANG, NULL);
394 if (WIFSIGNALED(status)) {
395 log_warnx("child process %d terminated with signal %d",
396 pid, WTERMSIG(status));
397 } else if (WEXITSTATUS(status) != 0) {
398 log_debugx("child process %d terminated with exit status %d",
399 pid, WEXITSTATUS(status));
401 log_debugx("child process %d terminated gracefully", pid);
410 usage_automountd(void)
413 fprintf(stderr, "usage: automountd [-D name=value][-m maxproc]"
414 "[-o opts][-Tidv]\n");
419 main_automountd(int argc, char **argv)
423 const char *pidfile_path = AUTOMOUNTD_PIDFILE;
424 char *options = NULL;
425 struct autofs_daemon_request request;
426 int ch, debug = 0, error, maxproc = 30, retval, saved_errno;
427 bool dont_daemonize = false, incomplete_hierarchy = false;
431 while ((ch = getopt(argc, argv, "D:Tdim:o:v")) != -1) {
434 defined_parse_and_add(optarg);
438 * For compatibility with other implementations,
444 dont_daemonize = true;
448 incomplete_hierarchy = true;
451 maxproc = atoi(optarg);
454 options = concat(options, ',', optarg);
470 pidfh = pidfile_open(pidfile_path, 0600, &otherpid);
472 if (errno == EEXIST) {
473 log_errx(1, "daemon already running, pid: %jd.",
476 log_err(1, "cannot open or create pidfile \"%s\"",
480 autofs_fd = open(AUTOFS_PATH, O_RDWR | O_CLOEXEC);
481 if (autofs_fd < 0 && errno == ENOENT) {
483 retval = kldload("autofs");
485 autofs_fd = open(AUTOFS_PATH, O_RDWR | O_CLOEXEC);
490 log_err(1, "failed to open %s", AUTOFS_PATH);
492 if (dont_daemonize == false) {
493 if (daemon(0, 0) == -1) {
494 log_warn("cannot daemonize");
495 pidfile_remove(pidfh);
502 pidfile_write(pidfh);
507 log_debugx("waiting for request from the kernel");
509 memset(&request, 0, sizeof(request));
510 error = ioctl(autofs_fd, AUTOFSREQUEST, &request);
512 if (errno == EINTR) {
513 nchildren -= wait_for_children(false);
514 assert(nchildren >= 0);
518 log_err(1, "AUTOFSREQUEST");
521 if (dont_daemonize) {
522 log_debugx("not forking due to -d flag; "
523 "will exit after servicing a single request");
525 nchildren -= wait_for_children(false);
526 assert(nchildren >= 0);
528 while (maxproc > 0 && nchildren >= maxproc) {
529 log_debugx("maxproc limit of %d child processes hit; "
530 "waiting for child process to exit", maxproc);
531 nchildren -= wait_for_children(true);
532 assert(nchildren >= 0);
534 log_debugx("got request; forking child process #%d",
545 pidfile_close(pidfh);
546 handle_request(&request, options, incomplete_hierarchy);