]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/autofs/automountd.c
Import the skein hashing algorithm, based on the threefish block cipher
[FreeBSD/FreeBSD.git] / usr.sbin / autofs / automountd.c
1 /*-
2  * Copyright (c) 2014 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Edward Tomasz Napierala under sponsorship
6  * from the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/types.h>
35 #include <sys/time.h>
36 #include <sys/ioctl.h>
37 #include <sys/param.h>
38 #include <sys/linker.h>
39 #include <sys/mount.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <sys/wait.h>
43 #include <sys/utsname.h>
44 #include <assert.h>
45 #include <ctype.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <libgen.h>
49 #include <libutil.h>
50 #include <netdb.h>
51 #include <signal.h>
52 #include <stdbool.h>
53 #include <stdint.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58
59 #include "autofs_ioctl.h"
60
61 #include "common.h"
62
63 #define AUTOMOUNTD_PIDFILE      "/var/run/automountd.pid"
64
65 static int nchildren = 0;
66 static int autofs_fd;
67 static int request_id;
68
69 static void
70 done(int request_error, bool wildcards)
71 {
72         struct autofs_daemon_done add;
73         int error;
74
75         memset(&add, 0, sizeof(add));
76         add.add_id = request_id;
77         add.add_wildcards = wildcards;
78         add.add_error = request_error;
79
80         log_debugx("completing request %d with error %d",
81             request_id, request_error);
82
83         error = ioctl(autofs_fd, AUTOFSDONE, &add);
84         if (error != 0)
85                 log_warn("AUTOFSDONE");
86 }
87
88 /*
89  * Remove "fstype=whatever" from optionsp and return the "whatever" part.
90  */
91 static char *
92 pick_option(const char *option, char **optionsp)
93 {
94         char *tofree, *pair, *newoptions;
95         char *picked = NULL;
96         bool first = true;
97
98         tofree = *optionsp;
99
100         newoptions = calloc(strlen(*optionsp) + 1, 1);
101         if (newoptions == NULL)
102                 log_err(1, "calloc");
103
104         while ((pair = strsep(optionsp, ",")) != NULL) {
105                 /*
106                  * XXX: strncasecmp(3) perhaps?
107                  */
108                 if (strncmp(pair, option, strlen(option)) == 0) {
109                         picked = checked_strdup(pair + strlen(option));
110                 } else {
111                         if (first == false)
112                                 strcat(newoptions, ",");
113                         else
114                                 first = false;
115                         strcat(newoptions, pair);
116                 }
117         }
118
119         free(tofree);
120         *optionsp = newoptions;
121
122         return (picked);
123 }
124
125 static void
126 create_subtree(const struct node *node, bool incomplete)
127 {
128         const struct node *child;
129         char *path;
130         bool wildcard_found = false;
131
132         /*
133          * Skip wildcard nodes.
134          */
135         if (strcmp(node->n_key, "*") == 0)
136                 return;
137
138         path = node_path(node);
139         log_debugx("creating subtree at %s", path);
140         create_directory(path);
141
142         if (incomplete) {
143                 TAILQ_FOREACH(child, &node->n_children, n_next) {
144                         if (strcmp(child->n_key, "*") == 0) {
145                                 wildcard_found = true;
146                                 break;
147                         }
148                 }
149
150                 if (wildcard_found) {
151                         log_debugx("node %s contains wildcard entry; "
152                             "not creating its subdirectories due to -d flag",
153                             path);
154                         free(path);
155                         return;
156                 }
157         }
158
159         free(path);
160
161         TAILQ_FOREACH(child, &node->n_children, n_next)
162                 create_subtree(child, incomplete);
163 }
164
165 static void
166 exit_callback(void)
167 {
168
169         done(EIO, true);
170 }
171
172 static void
173 handle_request(const struct autofs_daemon_request *adr, char *cmdline_options,
174     bool incomplete_hierarchy)
175 {
176         const char *map;
177         struct node *root, *parent, *node;
178         FILE *f;
179         char *key, *options, *fstype, *nobrowse, *retrycnt, *tmp;
180         int error;
181         bool wildcards;
182
183         log_debugx("got request %d: from %s, path %s, prefix \"%s\", "
184             "key \"%s\", options \"%s\"", adr->adr_id, adr->adr_from,
185             adr->adr_path, adr->adr_prefix, adr->adr_key, adr->adr_options);
186
187         /*
188          * Try to notify the kernel about any problems.
189          */
190         request_id = adr->adr_id;
191         atexit(exit_callback);
192
193         if (strncmp(adr->adr_from, "map ", 4) != 0) {
194                 log_errx(1, "invalid mountfrom \"%s\"; failing request",
195                     adr->adr_from);
196         }
197
198         map = adr->adr_from + 4; /* 4 for strlen("map "); */
199         root = node_new_root();
200         if (adr->adr_prefix[0] == '\0' || strcmp(adr->adr_prefix, "/") == 0) {
201                 /*
202                  * Direct map.  autofs(4) doesn't have a way to determine
203                  * correct map key, but since it's a direct map, we can just
204                  * use adr_path instead.
205                  */
206                 parent = root;
207                 key = checked_strdup(adr->adr_path);
208         } else {
209                 /*
210                  * Indirect map.
211                  */
212                 parent = node_new_map(root, checked_strdup(adr->adr_prefix),
213                     NULL,  checked_strdup(map),
214                     checked_strdup("[kernel request]"), lineno);
215
216                 if (adr->adr_key[0] == '\0')
217                         key = NULL;
218                 else
219                         key = checked_strdup(adr->adr_key);
220         }
221
222         /*
223          * "Wildcards" here actually means "make autofs(4) request
224          * automountd(8) action if the node being looked up does not
225          * exist, even though the parent is marked as cached".  This
226          * needs to be done for maps with wildcard entries, but also
227          * for special and executable maps.
228          */
229         parse_map(parent, map, key, &wildcards);
230         if (!wildcards)
231                 wildcards = node_has_wildcards(parent);
232         if (wildcards)
233                 log_debugx("map may contain wildcard entries");
234         else
235                 log_debugx("map does not contain wildcard entries");
236
237         if (key != NULL)
238                 node_expand_wildcard(root, key);
239
240         node = node_find(root, adr->adr_path);
241         if (node == NULL) {
242                 log_errx(1, "map %s does not contain key for \"%s\"; "
243                     "failing mount", map, adr->adr_path);
244         }
245
246         options = node_options(node);
247
248         /*
249          * Append options from auto_master.
250          */
251         options = concat(options, ',', adr->adr_options);
252
253         /*
254          * Prepend options passed via automountd(8) command line.
255          */
256         options = concat(cmdline_options, ',', options);
257
258         if (node->n_location == NULL) {
259                 log_debugx("found node defined at %s:%d; not a mountpoint",
260                     node->n_config_file, node->n_config_line);
261
262                 nobrowse = pick_option("nobrowse", &options);
263                 if (nobrowse != NULL && key == NULL) {
264                         log_debugx("skipping map %s due to \"nobrowse\" "
265                             "option; exiting", map);
266                         done(0, true);
267
268                         /*
269                          * Exit without calling exit_callback().
270                          */
271                         quick_exit(0);
272                 }
273
274                 /*
275                  * Not a mountpoint; create directories in the autofs mount
276                  * and complete the request.
277                  */
278                 create_subtree(node, incomplete_hierarchy);
279
280                 if (incomplete_hierarchy && key != NULL) {
281                         /*
282                          * We still need to create the single subdirectory
283                          * user is trying to access.
284                          */
285                         tmp = concat(adr->adr_path, '/', key);
286                         node = node_find(root, tmp);
287                         if (node != NULL)
288                                 create_subtree(node, false);
289                 }
290
291                 log_debugx("nothing to mount; exiting");
292                 done(0, wildcards);
293
294                 /*
295                  * Exit without calling exit_callback().
296                  */
297                 quick_exit(0);
298         }
299
300         log_debugx("found node defined at %s:%d; it is a mountpoint",
301             node->n_config_file, node->n_config_line);
302
303         if (key != NULL)
304                 node_expand_ampersand(node, key);
305         error = node_expand_defined(node);
306         if (error != 0) {
307                 log_errx(1, "variable expansion failed for %s; "
308                     "failing mount", adr->adr_path);
309         }
310
311         /*
312          * Append "automounted".
313          */
314         options = concat(options, ',', "automounted");
315
316         /*
317          * Remove "nobrowse", mount(8) doesn't understand it.
318          */
319         pick_option("nobrowse", &options);
320
321         /*
322          * Figure out fstype.
323          */
324         fstype = pick_option("fstype=", &options);
325         if (fstype == NULL) {
326                 log_debugx("fstype not specified in options; "
327                     "defaulting to \"nfs\"");
328                 fstype = checked_strdup("nfs");
329         }
330
331         if (strcmp(fstype, "nfs") == 0) {
332                 /*
333                  * The mount_nfs(8) command defaults to retry undefinitely.
334                  * We do not want that behaviour, because it leaves mount_nfs(8)
335                  * instances and automountd(8) children hanging forever.
336                  * Disable retries unless the option was passed explicitly.
337                  */
338                 retrycnt = pick_option("retrycnt=", &options);
339                 if (retrycnt == NULL) {
340                         log_debugx("retrycnt not specified in options; "
341                             "defaulting to 1");
342                         options = concat(options, ',', "retrycnt=1");
343                 } else {
344                         options = concat(options, ',',
345                             concat("retrycnt", '=', retrycnt));
346                 }
347         }
348
349         f = auto_popen("mount", "-t", fstype, "-o", options,
350             node->n_location, adr->adr_path, NULL);
351         assert(f != NULL);
352         error = auto_pclose(f);
353         if (error != 0)
354                 log_errx(1, "mount failed");
355
356         log_debugx("mount done; exiting");
357         done(0, wildcards);
358
359         /*
360          * Exit without calling exit_callback().
361          */
362         quick_exit(0);
363 }
364
365 static void
366 sigchld_handler(int dummy __unused)
367 {
368
369         /*
370          * The only purpose of this handler is to make SIGCHLD
371          * interrupt the AUTOFSREQUEST ioctl(2), so we can call
372          * wait_for_children().
373          */
374 }
375
376 static void
377 register_sigchld(void)
378 {
379         struct sigaction sa;
380         int error;
381
382         bzero(&sa, sizeof(sa));
383         sa.sa_handler = sigchld_handler;
384         sigfillset(&sa.sa_mask);
385         error = sigaction(SIGCHLD, &sa, NULL);
386         if (error != 0)
387                 log_err(1, "sigaction");
388
389 }
390
391
392 static int
393 wait_for_children(bool block)
394 {
395         pid_t pid;
396         int status;
397         int num = 0;
398
399         for (;;) {
400                 /*
401                  * If "block" is true, wait for at least one process.
402                  */
403                 if (block && num == 0)
404                         pid = wait4(-1, &status, 0, NULL);
405                 else
406                         pid = wait4(-1, &status, WNOHANG, NULL);
407                 if (pid <= 0)
408                         break;
409                 if (WIFSIGNALED(status)) {
410                         log_warnx("child process %d terminated with signal %d",
411                             pid, WTERMSIG(status));
412                 } else if (WEXITSTATUS(status) != 0) {
413                         log_debugx("child process %d terminated with exit status %d",
414                             pid, WEXITSTATUS(status));
415                 } else {
416                         log_debugx("child process %d terminated gracefully", pid);
417                 }
418                 num++;
419         }
420
421         return (num);
422 }
423
424 static void
425 usage_automountd(void)
426 {
427
428         fprintf(stderr, "usage: automountd [-D name=value][-m maxproc]"
429             "[-o opts][-Tidv]\n");
430         exit(1);
431 }
432
433 int
434 main_automountd(int argc, char **argv)
435 {
436         struct pidfh *pidfh;
437         pid_t pid, otherpid;
438         const char *pidfile_path = AUTOMOUNTD_PIDFILE;
439         char *options = NULL;
440         struct autofs_daemon_request request;
441         int ch, debug = 0, error, maxproc = 30, retval, saved_errno;
442         bool dont_daemonize = false, incomplete_hierarchy = false;
443
444         defined_init();
445
446         while ((ch = getopt(argc, argv, "D:Tdim:o:v")) != -1) {
447                 switch (ch) {
448                 case 'D':
449                         defined_parse_and_add(optarg);
450                         break;
451                 case 'T':
452                         /*
453                          * For compatibility with other implementations,
454                          * such as OS X.
455                          */
456                         debug++;
457                         break;
458                 case 'd':
459                         dont_daemonize = true;
460                         debug++;
461                         break;
462                 case 'i':
463                         incomplete_hierarchy = true;
464                         break;
465                 case 'm':
466                         maxproc = atoi(optarg);
467                         break;
468                 case 'o':
469                         options = concat(options, ',', optarg);
470                         break;
471                 case 'v':
472                         debug++;
473                         break;
474                 case '?':
475                 default:
476                         usage_automountd();
477                 }
478         }
479         argc -= optind;
480         if (argc != 0)
481                 usage_automountd();
482
483         log_init(debug);
484
485         pidfh = pidfile_open(pidfile_path, 0600, &otherpid);
486         if (pidfh == NULL) {
487                 if (errno == EEXIST) {
488                         log_errx(1, "daemon already running, pid: %jd.",
489                             (intmax_t)otherpid);
490                 }
491                 log_err(1, "cannot open or create pidfile \"%s\"",
492                     pidfile_path);
493         }
494
495         autofs_fd = open(AUTOFS_PATH, O_RDWR | O_CLOEXEC);
496         if (autofs_fd < 0 && errno == ENOENT) {
497                 saved_errno = errno;
498                 retval = kldload("autofs");
499                 if (retval != -1)
500                         autofs_fd = open(AUTOFS_PATH, O_RDWR | O_CLOEXEC);
501                 else
502                         errno = saved_errno;
503         }
504         if (autofs_fd < 0)
505                 log_err(1, "failed to open %s", AUTOFS_PATH);
506
507         if (dont_daemonize == false) {
508                 if (daemon(0, 0) == -1) {
509                         log_warn("cannot daemonize");
510                         pidfile_remove(pidfh);
511                         exit(1);
512                 }
513         } else {
514                 lesser_daemon();
515         }
516
517         pidfile_write(pidfh);
518
519         register_sigchld();
520
521         for (;;) {
522                 log_debugx("waiting for request from the kernel");
523
524                 memset(&request, 0, sizeof(request));
525                 error = ioctl(autofs_fd, AUTOFSREQUEST, &request);
526                 if (error != 0) {
527                         if (errno == EINTR) {
528                                 nchildren -= wait_for_children(false);
529                                 assert(nchildren >= 0);
530                                 continue;
531                         }
532
533                         log_err(1, "AUTOFSREQUEST");
534                 }
535
536                 if (dont_daemonize) {
537                         log_debugx("not forking due to -d flag; "
538                             "will exit after servicing a single request");
539                 } else {
540                         nchildren -= wait_for_children(false);
541                         assert(nchildren >= 0);
542
543                         while (maxproc > 0 && nchildren >= maxproc) {
544                                 log_debugx("maxproc limit of %d child processes hit; "
545                                     "waiting for child process to exit", maxproc);
546                                 nchildren -= wait_for_children(true);
547                                 assert(nchildren >= 0);
548                         }
549                         log_debugx("got request; forking child process #%d",
550                             nchildren);
551                         nchildren++;
552
553                         pid = fork();
554                         if (pid < 0)
555                                 log_err(1, "fork");
556                         if (pid > 0)
557                                 continue;
558                 }
559
560                 pidfile_close(pidfh);
561                 handle_request(&request, options, incomplete_hierarchy);
562         }
563
564         pidfile_close(pidfh);
565
566         return (0);
567 }
568