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(1, strlen(*optionsp) + 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 569