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