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