Home | History | Annotate | Line # | Download | only in autofs
      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