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