Index: head/usr.sbin/autofs/automount.c =================================================================== --- head/usr.sbin/autofs/automount.c (revision 328337) +++ head/usr.sbin/autofs/automount.c (revision 328338) @@ -1,395 +1,397 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "mntopts.h" static int unmount_by_statfs(const struct statfs *sb, bool force) { char *fsid_str; int error, ret, flags; ret = asprintf(&fsid_str, "FSID:%d:%d", sb->f_fsid.val[0], sb->f_fsid.val[1]); if (ret < 0) log_err(1, "asprintf"); log_debugx("unmounting %s using %s", sb->f_mntonname, fsid_str); flags = MNT_BYFSID; if (force) flags |= MNT_FORCE; error = unmount(fsid_str, flags); free(fsid_str); if (error != 0) log_warn("cannot unmount %s", sb->f_mntonname); return (error); } static const struct statfs * find_statfs(const struct statfs *mntbuf, int nitems, const char *mountpoint) { int i; for (i = 0; i < nitems; i++) { if (strcmp(mntbuf[i].f_mntonname, mountpoint) == 0) return (mntbuf + i); } return (NULL); } static void mount_autofs(const char *from, const char *fspath, const char *options, const char *prefix) { struct iovec *iov = NULL; char errmsg[255]; int error, iovlen = 0; create_directory(fspath); log_debugx("mounting %s on %s, prefix \"%s\", options \"%s\"", from, fspath, prefix, options); memset(errmsg, 0, sizeof(errmsg)); build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "autofs"), (size_t)-1); build_iovec(&iov, &iovlen, "fspath", __DECONST(void *, fspath), (size_t)-1); build_iovec(&iov, &iovlen, "from", __DECONST(void *, from), (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); /* * Append the options and mountpoint defined in auto_master(5); * this way automountd(8) does not need to parse it. */ build_iovec(&iov, &iovlen, "master_options", __DECONST(void *, options), (size_t)-1); build_iovec(&iov, &iovlen, "master_prefix", __DECONST(void *, prefix), (size_t)-1); error = nmount(iov, iovlen, 0); if (error != 0) { if (*errmsg != '\0') { log_err(1, "cannot mount %s on %s: %s", from, fspath, errmsg); } else { log_err(1, "cannot mount %s on %s", from, fspath); } } } static void mount_if_not_already(const struct node *n, const char *map, const char *options, const char *prefix, const struct statfs *mntbuf, int nitems) { const struct statfs *sb; char *mountpoint; char *from; int ret; ret = asprintf(&from, "map %s", map); if (ret < 0) log_err(1, "asprintf"); mountpoint = node_path(n); sb = find_statfs(mntbuf, nitems, mountpoint); if (sb != NULL) { if (strcmp(sb->f_fstypename, "autofs") != 0) { log_debugx("unknown filesystem mounted " "on %s; mounting", mountpoint); /* * XXX: Compare options and 'from', * and update the mount if necessary. */ } else { log_debugx("autofs already mounted " "on %s", mountpoint); free(from); free(mountpoint); return; } } else { log_debugx("nothing mounted on %s; mounting", mountpoint); } mount_autofs(from, mountpoint, options, prefix); free(from); free(mountpoint); } static void mount_unmount(struct node *root) { struct statfs *mntbuf; struct node *n, *n2; int i, nitems; nitems = getmntinfo(&mntbuf, MNT_WAIT); if (nitems <= 0) log_err(1, "getmntinfo"); log_debugx("unmounting stale autofs mounts"); for (i = 0; i < nitems; i++) { if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) { log_debugx("skipping %s, filesystem type is not autofs", mntbuf[i].f_mntonname); continue; } n = node_find(root, mntbuf[i].f_mntonname); if (n != NULL) { log_debugx("leaving autofs mounted on %s", mntbuf[i].f_mntonname); continue; } log_debugx("autofs mounted on %s not found " "in new configuration; unmounting", mntbuf[i].f_mntonname); unmount_by_statfs(&(mntbuf[i]), false); } log_debugx("mounting new autofs mounts"); TAILQ_FOREACH(n, &root->n_children, n_next) { if (!node_is_direct_map(n)) { mount_if_not_already(n, n->n_map, n->n_options, n->n_key, mntbuf, nitems); continue; } TAILQ_FOREACH(n2, &n->n_children, n_next) { mount_if_not_already(n2, n->n_map, n->n_options, "/", mntbuf, nitems); } } } static void flush_autofs(const char *fspath) { struct iovec *iov = NULL; char errmsg[255]; int error, iovlen = 0; log_debugx("flushing %s", fspath); memset(errmsg, 0, sizeof(errmsg)); build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "autofs"), (size_t)-1); build_iovec(&iov, &iovlen, "fspath", __DECONST(void *, fspath), (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); error = nmount(iov, iovlen, MNT_UPDATE); if (error != 0) { if (*errmsg != '\0') { log_err(1, "cannot flush %s: %s", fspath, errmsg); } else { log_err(1, "cannot flush %s", fspath); } } } static void flush_caches(void) { struct statfs *mntbuf; int i, nitems; nitems = getmntinfo(&mntbuf, MNT_WAIT); if (nitems <= 0) log_err(1, "getmntinfo"); log_debugx("flushing autofs caches"); for (i = 0; i < nitems; i++) { if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) { log_debugx("skipping %s, filesystem type is not autofs", mntbuf[i].f_mntonname); continue; } flush_autofs(mntbuf[i].f_mntonname); } } static void unmount_automounted(bool force) { struct statfs *mntbuf; int i, nitems; nitems = getmntinfo(&mntbuf, MNT_WAIT); if (nitems <= 0) log_err(1, "getmntinfo"); log_debugx("unmounting automounted filesystems"); for (i = 0; i < nitems; i++) { if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) { log_debugx("skipping %s, filesystem type is autofs", mntbuf[i].f_mntonname); continue; } if ((mntbuf[i].f_flags & MNT_AUTOMOUNTED) == 0) { log_debugx("skipping %s, not automounted", mntbuf[i].f_mntonname); continue; } unmount_by_statfs(&(mntbuf[i]), force); } } static void usage_automount(void) { fprintf(stderr, "usage: automount [-D name=value][-o opts][-Lcfuv]\n"); exit(1); } int main_automount(int argc, char **argv) { struct node *root; int ch, debug = 0, show_maps = 0; char *options = NULL; bool do_unmount = false, force_unmount = false, flush = false; /* * Note that in automount(8), the only purpose of variable * handling is to aid in debugging maps (automount -L). */ defined_init(); while ((ch = getopt(argc, argv, "D:Lfco:uv")) != -1) { switch (ch) { case 'D': defined_parse_and_add(optarg); break; case 'L': show_maps++; break; case 'c': flush = true; break; case 'f': force_unmount = true; break; case 'o': options = concat(options, ',', optarg); break; case 'u': do_unmount = true; break; case 'v': debug++; break; case '?': default: usage_automount(); } } argc -= optind; if (argc != 0) usage_automount(); if (force_unmount && !do_unmount) usage_automount(); log_init(debug); if (flush) { flush_caches(); return (0); } if (do_unmount) { unmount_automounted(force_unmount); return (0); } root = node_new_root(); parse_master(root, AUTO_MASTER_PATH); if (show_maps) { if (show_maps > 1) { node_expand_indirect_maps(root); node_expand_ampersand(root, NULL); } node_expand_defined(root); node_print(root, options); return (0); } mount_unmount(root); return (0); } Index: head/usr.sbin/autofs/automountd.c =================================================================== --- head/usr.sbin/autofs/automountd.c (revision 328337) +++ head/usr.sbin/autofs/automountd.c (revision 328338) @@ -1,568 +1,570 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "autofs_ioctl.h" #include "common.h" #define AUTOMOUNTD_PIDFILE "/var/run/automountd.pid" static int nchildren = 0; static int autofs_fd; static int request_id; static void done(int request_error, bool wildcards) { struct autofs_daemon_done add; int error; memset(&add, 0, sizeof(add)); add.add_id = request_id; add.add_wildcards = wildcards; add.add_error = request_error; log_debugx("completing request %d with error %d", request_id, request_error); error = ioctl(autofs_fd, AUTOFSDONE, &add); if (error != 0) log_warn("AUTOFSDONE"); } /* * Remove "fstype=whatever" from optionsp and return the "whatever" part. */ static char * pick_option(const char *option, char **optionsp) { char *tofree, *pair, *newoptions; char *picked = NULL; bool first = true; tofree = *optionsp; newoptions = calloc(1, strlen(*optionsp) + 1); if (newoptions == NULL) log_err(1, "calloc"); while ((pair = strsep(optionsp, ",")) != NULL) { /* * XXX: strncasecmp(3) perhaps? */ if (strncmp(pair, option, strlen(option)) == 0) { picked = checked_strdup(pair + strlen(option)); } else { if (first == false) strcat(newoptions, ","); else first = false; strcat(newoptions, pair); } } free(tofree); *optionsp = newoptions; return (picked); } static void create_subtree(const struct node *node, bool incomplete) { const struct node *child; char *path; bool wildcard_found = false; /* * Skip wildcard nodes. */ if (strcmp(node->n_key, "*") == 0) return; path = node_path(node); log_debugx("creating subtree at %s", path); create_directory(path); if (incomplete) { TAILQ_FOREACH(child, &node->n_children, n_next) { if (strcmp(child->n_key, "*") == 0) { wildcard_found = true; break; } } if (wildcard_found) { log_debugx("node %s contains wildcard entry; " "not creating its subdirectories due to -d flag", path); free(path); return; } } free(path); TAILQ_FOREACH(child, &node->n_children, n_next) create_subtree(child, incomplete); } static void exit_callback(void) { done(EIO, true); } static void handle_request(const struct autofs_daemon_request *adr, char *cmdline_options, bool incomplete_hierarchy) { const char *map; struct node *root, *parent, *node; FILE *f; char *key, *options, *fstype, *nobrowse, *retrycnt, *tmp; int error; bool wildcards; log_debugx("got request %d: from %s, path %s, prefix \"%s\", " "key \"%s\", options \"%s\"", adr->adr_id, adr->adr_from, adr->adr_path, adr->adr_prefix, adr->adr_key, adr->adr_options); /* * Try to notify the kernel about any problems. */ request_id = adr->adr_id; atexit(exit_callback); if (strncmp(adr->adr_from, "map ", 4) != 0) { log_errx(1, "invalid mountfrom \"%s\"; failing request", adr->adr_from); } map = adr->adr_from + 4; /* 4 for strlen("map "); */ root = node_new_root(); if (adr->adr_prefix[0] == '\0' || strcmp(adr->adr_prefix, "/") == 0) { /* * Direct map. autofs(4) doesn't have a way to determine * correct map key, but since it's a direct map, we can just * use adr_path instead. */ parent = root; key = checked_strdup(adr->adr_path); } else { /* * Indirect map. */ parent = node_new_map(root, checked_strdup(adr->adr_prefix), NULL, checked_strdup(map), checked_strdup("[kernel request]"), lineno); if (adr->adr_key[0] == '\0') key = NULL; else key = checked_strdup(adr->adr_key); } /* * "Wildcards" here actually means "make autofs(4) request * automountd(8) action if the node being looked up does not * exist, even though the parent is marked as cached". This * needs to be done for maps with wildcard entries, but also * for special and executable maps. */ parse_map(parent, map, key, &wildcards); if (!wildcards) wildcards = node_has_wildcards(parent); if (wildcards) log_debugx("map may contain wildcard entries"); else log_debugx("map does not contain wildcard entries"); if (key != NULL) node_expand_wildcard(root, key); node = node_find(root, adr->adr_path); if (node == NULL) { log_errx(1, "map %s does not contain key for \"%s\"; " "failing mount", map, adr->adr_path); } options = node_options(node); /* * Append options from auto_master. */ options = concat(options, ',', adr->adr_options); /* * Prepend options passed via automountd(8) command line. */ options = concat(cmdline_options, ',', options); if (node->n_location == NULL) { log_debugx("found node defined at %s:%d; not a mountpoint", node->n_config_file, node->n_config_line); nobrowse = pick_option("nobrowse", &options); if (nobrowse != NULL && key == NULL) { log_debugx("skipping map %s due to \"nobrowse\" " "option; exiting", map); done(0, true); /* * Exit without calling exit_callback(). */ quick_exit(0); } /* * Not a mountpoint; create directories in the autofs mount * and complete the request. */ create_subtree(node, incomplete_hierarchy); if (incomplete_hierarchy && key != NULL) { /* * We still need to create the single subdirectory * user is trying to access. */ tmp = concat(adr->adr_path, '/', key); node = node_find(root, tmp); if (node != NULL) create_subtree(node, false); } log_debugx("nothing to mount; exiting"); done(0, wildcards); /* * Exit without calling exit_callback(). */ quick_exit(0); } log_debugx("found node defined at %s:%d; it is a mountpoint", node->n_config_file, node->n_config_line); if (key != NULL) node_expand_ampersand(node, key); error = node_expand_defined(node); if (error != 0) { log_errx(1, "variable expansion failed for %s; " "failing mount", adr->adr_path); } /* * Append "automounted". */ options = concat(options, ',', "automounted"); /* * Remove "nobrowse", mount(8) doesn't understand it. */ pick_option("nobrowse", &options); /* * Figure out fstype. */ fstype = pick_option("fstype=", &options); if (fstype == NULL) { log_debugx("fstype not specified in options; " "defaulting to \"nfs\""); fstype = checked_strdup("nfs"); } if (strcmp(fstype, "nfs") == 0) { /* * The mount_nfs(8) command defaults to retry undefinitely. * We do not want that behaviour, because it leaves mount_nfs(8) * instances and automountd(8) children hanging forever. * Disable retries unless the option was passed explicitly. */ retrycnt = pick_option("retrycnt=", &options); if (retrycnt == NULL) { log_debugx("retrycnt not specified in options; " "defaulting to 1"); options = concat(options, ',', "retrycnt=1"); } else { options = concat(options, ',', concat("retrycnt", '=', retrycnt)); } } f = auto_popen("mount", "-t", fstype, "-o", options, node->n_location, adr->adr_path, NULL); assert(f != NULL); error = auto_pclose(f); if (error != 0) log_errx(1, "mount failed"); log_debugx("mount done; exiting"); done(0, wildcards); /* * Exit without calling exit_callback(). */ quick_exit(0); } static void sigchld_handler(int dummy __unused) { /* * The only purpose of this handler is to make SIGCHLD * interrupt the AUTOFSREQUEST ioctl(2), so we can call * wait_for_children(). */ } static void register_sigchld(void) { struct sigaction sa; int error; bzero(&sa, sizeof(sa)); sa.sa_handler = sigchld_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGCHLD, &sa, NULL); if (error != 0) log_err(1, "sigaction"); } static int wait_for_children(bool block) { pid_t pid; int status; int num = 0; for (;;) { /* * If "block" is true, wait for at least one process. */ if (block && num == 0) pid = wait4(-1, &status, 0, NULL); else pid = wait4(-1, &status, WNOHANG, NULL); if (pid <= 0) break; if (WIFSIGNALED(status)) { log_warnx("child process %d terminated with signal %d", pid, WTERMSIG(status)); } else if (WEXITSTATUS(status) != 0) { log_debugx("child process %d terminated with exit status %d", pid, WEXITSTATUS(status)); } else { log_debugx("child process %d terminated gracefully", pid); } num++; } return (num); } static void usage_automountd(void) { fprintf(stderr, "usage: automountd [-D name=value][-m maxproc]" "[-o opts][-Tidv]\n"); exit(1); } int main_automountd(int argc, char **argv) { struct pidfh *pidfh; pid_t pid, otherpid; const char *pidfile_path = AUTOMOUNTD_PIDFILE; char *options = NULL; struct autofs_daemon_request request; int ch, debug = 0, error, maxproc = 30, retval, saved_errno; bool dont_daemonize = false, incomplete_hierarchy = false; defined_init(); while ((ch = getopt(argc, argv, "D:Tdim:o:v")) != -1) { switch (ch) { case 'D': defined_parse_and_add(optarg); break; case 'T': /* * For compatibility with other implementations, * such as OS X. */ debug++; break; case 'd': dont_daemonize = true; debug++; break; case 'i': incomplete_hierarchy = true; break; case 'm': maxproc = atoi(optarg); break; case 'o': options = concat(options, ',', optarg); break; case 'v': debug++; break; case '?': default: usage_automountd(); } } argc -= optind; if (argc != 0) usage_automountd(); log_init(debug); pidfh = pidfile_open(pidfile_path, 0600, &otherpid); if (pidfh == NULL) { if (errno == EEXIST) { log_errx(1, "daemon already running, pid: %jd.", (intmax_t)otherpid); } log_err(1, "cannot open or create pidfile \"%s\"", pidfile_path); } autofs_fd = open(AUTOFS_PATH, O_RDWR | O_CLOEXEC); if (autofs_fd < 0 && errno == ENOENT) { saved_errno = errno; retval = kldload("autofs"); if (retval != -1) autofs_fd = open(AUTOFS_PATH, O_RDWR | O_CLOEXEC); else errno = saved_errno; } if (autofs_fd < 0) log_err(1, "failed to open %s", AUTOFS_PATH); if (dont_daemonize == false) { if (daemon(0, 0) == -1) { log_warn("cannot daemonize"); pidfile_remove(pidfh); exit(1); } } else { lesser_daemon(); } pidfile_write(pidfh); register_sigchld(); for (;;) { log_debugx("waiting for request from the kernel"); memset(&request, 0, sizeof(request)); error = ioctl(autofs_fd, AUTOFSREQUEST, &request); if (error != 0) { if (errno == EINTR) { nchildren -= wait_for_children(false); assert(nchildren >= 0); continue; } log_err(1, "AUTOFSREQUEST"); } if (dont_daemonize) { log_debugx("not forking due to -d flag; " "will exit after servicing a single request"); } else { nchildren -= wait_for_children(false); assert(nchildren >= 0); while (maxproc > 0 && nchildren >= maxproc) { log_debugx("maxproc limit of %d child processes hit; " "waiting for child process to exit", maxproc); nchildren -= wait_for_children(true); assert(nchildren >= 0); } log_debugx("got request; forking child process #%d", nchildren); nchildren++; pid = fork(); if (pid < 0) log_err(1, "fork"); if (pid > 0) continue; } pidfile_close(pidfh); handle_request(&request, options, incomplete_hierarchy); } pidfile_close(pidfh); return (0); } Index: head/usr.sbin/autofs/autounmountd.c =================================================================== --- head/usr.sbin/autofs/autounmountd.c (revision 328337) +++ head/usr.sbin/autofs/autounmountd.c (revision 328338) @@ -1,354 +1,356 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #define AUTOUNMOUNTD_PIDFILE "/var/run/autounmountd.pid" struct automounted_fs { TAILQ_ENTRY(automounted_fs) af_next; time_t af_mount_time; bool af_mark; fsid_t af_fsid; char af_mountpoint[MNAMELEN]; }; static TAILQ_HEAD(, automounted_fs) automounted; static struct automounted_fs * automounted_find(fsid_t fsid) { struct automounted_fs *af; TAILQ_FOREACH(af, &automounted, af_next) { if (af->af_fsid.val[0] == fsid.val[0] && af->af_fsid.val[1] == fsid.val[1]) return (af); } return (NULL); } static struct automounted_fs * automounted_add(fsid_t fsid, const char *mountpoint) { struct automounted_fs *af; af = calloc(1, sizeof(*af)); if (af == NULL) log_err(1, "calloc"); af->af_mount_time = time(NULL); af->af_fsid = fsid; strlcpy(af->af_mountpoint, mountpoint, sizeof(af->af_mountpoint)); TAILQ_INSERT_TAIL(&automounted, af, af_next); return (af); } static void automounted_remove(struct automounted_fs *af) { TAILQ_REMOVE(&automounted, af, af_next); free(af); } static void refresh_automounted(void) { struct automounted_fs *af, *tmpaf; struct statfs *mntbuf; int i, nitems; nitems = getmntinfo(&mntbuf, MNT_WAIT); if (nitems <= 0) log_err(1, "getmntinfo"); log_debugx("refreshing list of automounted filesystems"); TAILQ_FOREACH(af, &automounted, af_next) af->af_mark = false; for (i = 0; i < nitems; i++) { if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) { log_debugx("skipping %s, filesystem type is autofs", mntbuf[i].f_mntonname); continue; } if ((mntbuf[i].f_flags & MNT_AUTOMOUNTED) == 0) { log_debugx("skipping %s, not automounted", mntbuf[i].f_mntonname); continue; } af = automounted_find(mntbuf[i].f_fsid); if (af == NULL) { log_debugx("new automounted filesystem found on %s " "(FSID:%d:%d)", mntbuf[i].f_mntonname, mntbuf[i].f_fsid.val[0], mntbuf[i].f_fsid.val[1]); af = automounted_add(mntbuf[i].f_fsid, mntbuf[i].f_mntonname); } else { log_debugx("already known automounted filesystem " "found on %s (FSID:%d:%d)", mntbuf[i].f_mntonname, mntbuf[i].f_fsid.val[0], mntbuf[i].f_fsid.val[1]); } af->af_mark = true; } TAILQ_FOREACH_SAFE(af, &automounted, af_next, tmpaf) { if (af->af_mark) continue; log_debugx("lost filesystem mounted on %s (FSID:%d:%d)", af->af_mountpoint, af->af_fsid.val[0], af->af_fsid.val[1]); automounted_remove(af); } } static int unmount_by_fsid(const fsid_t fsid, const char *mountpoint) { char *fsid_str; int error, ret; ret = asprintf(&fsid_str, "FSID:%d:%d", fsid.val[0], fsid.val[1]); if (ret < 0) log_err(1, "asprintf"); error = unmount(fsid_str, MNT_NONBUSY | MNT_BYFSID); if (error != 0) { if (errno == EBUSY) { log_debugx("cannot unmount %s (%s): %s", mountpoint, fsid_str, strerror(errno)); } else { log_warn("cannot unmount %s (%s)", mountpoint, fsid_str); } } free(fsid_str); return (error); } static double expire_automounted(double expiration_time) { struct automounted_fs *af, *tmpaf; time_t now; double mounted_for, mounted_max = -1.0; int error; now = time(NULL); log_debugx("expiring automounted filesystems"); TAILQ_FOREACH_SAFE(af, &automounted, af_next, tmpaf) { mounted_for = difftime(now, af->af_mount_time); if (mounted_for < expiration_time) { log_debugx("skipping %s (FSID:%d:%d), mounted " "for %.0f seconds", af->af_mountpoint, af->af_fsid.val[0], af->af_fsid.val[1], mounted_for); if (mounted_for > mounted_max) mounted_max = mounted_for; continue; } log_debugx("filesystem mounted on %s (FSID:%d:%d), " "was mounted for %.0f seconds; unmounting", af->af_mountpoint, af->af_fsid.val[0], af->af_fsid.val[1], mounted_for); error = unmount_by_fsid(af->af_fsid, af->af_mountpoint); if (error != 0) { if (mounted_for > mounted_max) mounted_max = mounted_for; } } return (mounted_max); } static void usage_autounmountd(void) { fprintf(stderr, "usage: autounmountd [-r time][-t time][-dv]\n"); exit(1); } static void do_wait(int kq, double sleep_time) { struct timespec timeout; struct kevent unused; int nevents; if (sleep_time != -1.0) { assert(sleep_time > 0.0); timeout.tv_sec = sleep_time; timeout.tv_nsec = 0; log_debugx("waiting for filesystem event for %.0f seconds", sleep_time); nevents = kevent(kq, NULL, 0, &unused, 1, &timeout); } else { log_debugx("waiting for filesystem event"); nevents = kevent(kq, NULL, 0, &unused, 1, NULL); } if (nevents < 0) { if (errno == EINTR) return; log_err(1, "kevent"); } if (nevents == 0) { log_debugx("timeout reached"); assert(sleep_time > 0.0); } else { log_debugx("got filesystem event"); } } int main_autounmountd(int argc, char **argv) { struct kevent event; struct pidfh *pidfh; pid_t otherpid; const char *pidfile_path = AUTOUNMOUNTD_PIDFILE; int ch, debug = 0, error, kq; double expiration_time = 600, retry_time = 600, mounted_max, sleep_time; bool dont_daemonize = false; while ((ch = getopt(argc, argv, "dr:t:v")) != -1) { switch (ch) { case 'd': dont_daemonize = true; debug++; break; case 'r': retry_time = atoi(optarg); break; case 't': expiration_time = atoi(optarg); break; case 'v': debug++; break; case '?': default: usage_autounmountd(); } } argc -= optind; if (argc != 0) usage_autounmountd(); if (retry_time <= 0) log_errx(1, "retry time must be greater than zero"); if (expiration_time <= 0) log_errx(1, "expiration time must be greater than zero"); log_init(debug); pidfh = pidfile_open(pidfile_path, 0600, &otherpid); if (pidfh == NULL) { if (errno == EEXIST) { log_errx(1, "daemon already running, pid: %jd.", (intmax_t)otherpid); } log_err(1, "cannot open or create pidfile \"%s\"", pidfile_path); } if (dont_daemonize == false) { if (daemon(0, 0) == -1) { log_warn("cannot daemonize"); pidfile_remove(pidfh); exit(1); } } pidfile_write(pidfh); TAILQ_INIT(&automounted); kq = kqueue(); if (kq < 0) log_err(1, "kqueue"); EV_SET(&event, 0, EVFILT_FS, EV_ADD | EV_CLEAR, 0, 0, NULL); error = kevent(kq, &event, 1, NULL, 0, NULL); if (error < 0) log_err(1, "kevent"); for (;;) { refresh_automounted(); mounted_max = expire_automounted(expiration_time); if (mounted_max == -1.0) { sleep_time = mounted_max; log_debugx("no filesystems to expire"); } else if (mounted_max < expiration_time) { sleep_time = difftime(expiration_time, mounted_max); log_debugx("some filesystems expire in %.0f seconds", sleep_time); } else { sleep_time = retry_time; log_debugx("some expired filesystems remain mounted, " "will retry in %.0f seconds", sleep_time); } do_wait(kq, sleep_time); } return (0); } Index: head/usr.sbin/autofs/common.c =================================================================== --- head/usr.sbin/autofs/common.c (revision 328337) +++ head/usr.sbin/autofs/common.c (revision 328338) @@ -1,1223 +1,1225 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "autofs_ioctl.h" #include "common.h" extern FILE *yyin; extern char *yytext; extern int yylex(void); static void parse_master_yyin(struct node *root, const char *master); static void parse_map_yyin(struct node *parent, const char *map, const char *executable_key); char * checked_strdup(const char *s) { char *c; assert(s != NULL); c = strdup(s); if (c == NULL) log_err(1, "strdup"); return (c); } /* * Concatenate two strings, inserting separator between them, unless not needed. */ char * concat(const char *s1, char separator, const char *s2) { char *result; char s1last, s2first; int ret; if (s1 == NULL) s1 = ""; if (s2 == NULL) s2 = ""; if (s1[0] == '\0') s1last = '\0'; else s1last = s1[strlen(s1) - 1]; s2first = s2[0]; if (s1last == separator && s2first == separator) { /* * If s1 ends with the separator and s2 begins with * it - skip the latter; otherwise concatenating "/" * and "/foo" would end up returning "//foo". */ ret = asprintf(&result, "%s%s", s1, s2 + 1); } else if (s1last == separator || s2first == separator || s1[0] == '\0' || s2[0] == '\0') { ret = asprintf(&result, "%s%s", s1, s2); } else { ret = asprintf(&result, "%s%c%s", s1, separator, s2); } if (ret < 0) log_err(1, "asprintf"); //log_debugx("%s: got %s and %s, returning %s", __func__, s1, s2, result); return (result); } void create_directory(const char *path) { char *component, *copy, *tofree, *partial, *tmp; int error; assert(path[0] == '/'); /* * +1 to skip the leading slash. */ copy = tofree = checked_strdup(path + 1); partial = checked_strdup(""); for (;;) { component = strsep(©, "/"); if (component == NULL) break; tmp = concat(partial, '/', component); free(partial); partial = tmp; //log_debugx("creating \"%s\"", partial); error = mkdir(partial, 0755); if (error != 0 && errno != EEXIST) { log_warn("cannot create %s", partial); return; } } free(tofree); } struct node * node_new_root(void) { struct node *n; n = calloc(1, sizeof(*n)); if (n == NULL) log_err(1, "calloc"); // XXX n->n_key = checked_strdup("/"); n->n_options = checked_strdup(""); TAILQ_INIT(&n->n_children); return (n); } struct node * node_new(struct node *parent, char *key, char *options, char *location, const char *config_file, int config_line) { struct node *n; n = calloc(1, sizeof(*n)); if (n == NULL) log_err(1, "calloc"); TAILQ_INIT(&n->n_children); assert(key != NULL); assert(key[0] != '\0'); n->n_key = key; if (options != NULL) n->n_options = options; else n->n_options = strdup(""); n->n_location = location; assert(config_file != NULL); n->n_config_file = config_file; assert(config_line >= 0); n->n_config_line = config_line; assert(parent != NULL); n->n_parent = parent; TAILQ_INSERT_TAIL(&parent->n_children, n, n_next); return (n); } struct node * node_new_map(struct node *parent, char *key, char *options, char *map, const char *config_file, int config_line) { struct node *n; n = calloc(1, sizeof(*n)); if (n == NULL) log_err(1, "calloc"); TAILQ_INIT(&n->n_children); assert(key != NULL); assert(key[0] != '\0'); n->n_key = key; if (options != NULL) n->n_options = options; else n->n_options = strdup(""); n->n_map = map; assert(config_file != NULL); n->n_config_file = config_file; assert(config_line >= 0); n->n_config_line = config_line; assert(parent != NULL); n->n_parent = parent; TAILQ_INSERT_TAIL(&parent->n_children, n, n_next); return (n); } static struct node * node_duplicate(const struct node *o, struct node *parent) { const struct node *child; struct node *n; if (parent == NULL) parent = o->n_parent; n = node_new(parent, o->n_key, o->n_options, o->n_location, o->n_config_file, o->n_config_line); TAILQ_FOREACH(child, &o->n_children, n_next) node_duplicate(child, n); return (n); } static void node_delete(struct node *n) { struct node *child, *tmp; assert (n != NULL); TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp) node_delete(child); if (n->n_parent != NULL) TAILQ_REMOVE(&n->n_parent->n_children, n, n_next); free(n); } /* * Move (reparent) node 'n' to make it sibling of 'previous', placed * just after it. */ static void node_move_after(struct node *n, struct node *previous) { TAILQ_REMOVE(&n->n_parent->n_children, n, n_next); n->n_parent = previous->n_parent; TAILQ_INSERT_AFTER(&previous->n_parent->n_children, previous, n, n_next); } static void node_expand_includes(struct node *root, bool is_master) { struct node *n, *n2, *tmp, *tmp2, *tmproot; int error; TAILQ_FOREACH_SAFE(n, &root->n_children, n_next, tmp) { if (n->n_key[0] != '+') continue; error = access(AUTO_INCLUDE_PATH, F_OK); if (error != 0) { log_errx(1, "directory services not configured; " "%s does not exist", AUTO_INCLUDE_PATH); } /* * "+1" to skip leading "+". */ yyin = auto_popen(AUTO_INCLUDE_PATH, n->n_key + 1, NULL); assert(yyin != NULL); tmproot = node_new_root(); if (is_master) parse_master_yyin(tmproot, n->n_key); else parse_map_yyin(tmproot, n->n_key, NULL); error = auto_pclose(yyin); yyin = NULL; if (error != 0) { log_errx(1, "failed to handle include \"%s\"", n->n_key); } /* * Entries to be included are now in tmproot. We need to merge * them with the rest, preserving their place and ordering. */ TAILQ_FOREACH_REVERSE_SAFE(n2, &tmproot->n_children, nodehead, n_next, tmp2) { node_move_after(n2, n); } node_delete(n); node_delete(tmproot); } } static char * expand_ampersand(char *string, const char *key) { char c, *expanded; int i, ret, before_len = 0; bool backslashed = false; assert(key[0] != '\0'); expanded = checked_strdup(string); for (i = 0; string[i] != '\0'; i++) { c = string[i]; if (c == '\\' && backslashed == false) { backslashed = true; continue; } if (backslashed) { backslashed = false; continue; } backslashed = false; if (c != '&') continue; /* * The 'before_len' variable contains the number * of characters before the '&'. */ before_len = i; //assert(i + 1 < (int)strlen(string)); ret = asprintf(&expanded, "%.*s%s%s", before_len, string, key, string + before_len + 1); if (ret < 0) log_err(1, "asprintf"); //log_debugx("\"%s\" expanded with key \"%s\" to \"%s\"", // string, key, expanded); /* * Figure out where to start searching for next variable. */ string = expanded; i = before_len + strlen(key); backslashed = false; //assert(i < (int)strlen(string)); } return (expanded); } /* * Expand "&" in n_location. If the key is NULL, try to use * key from map entries themselves. Keep in mind that maps * consist of tho levels of node structures, the key is one * level up. * * Variant with NULL key is for "automount -LL". */ void node_expand_ampersand(struct node *n, const char *key) { struct node *child; if (n->n_location != NULL) { if (key == NULL) { if (n->n_parent != NULL && strcmp(n->n_parent->n_key, "*") != 0) { n->n_location = expand_ampersand(n->n_location, n->n_parent->n_key); } } else { n->n_location = expand_ampersand(n->n_location, key); } } TAILQ_FOREACH(child, &n->n_children, n_next) node_expand_ampersand(child, key); } /* * Expand "*" in n_key. */ void node_expand_wildcard(struct node *n, const char *key) { struct node *child, *expanded; assert(key != NULL); if (strcmp(n->n_key, "*") == 0) { expanded = node_duplicate(n, NULL); expanded->n_key = checked_strdup(key); node_move_after(expanded, n); } TAILQ_FOREACH(child, &n->n_children, n_next) node_expand_wildcard(child, key); } int node_expand_defined(struct node *n) { struct node *child; int error, cumulated_error = 0; if (n->n_location != NULL) { n->n_location = defined_expand(n->n_location); if (n->n_location == NULL) { log_warnx("failed to expand location for %s", node_path(n)); return (EINVAL); } } TAILQ_FOREACH(child, &n->n_children, n_next) { error = node_expand_defined(child); if (error != 0 && cumulated_error == 0) cumulated_error = error; } return (cumulated_error); } static bool node_is_direct_key(const struct node *n) { if (n->n_parent != NULL && n->n_parent->n_parent == NULL && strcmp(n->n_key, "/-") == 0) { return (true); } return (false); } bool node_is_direct_map(const struct node *n) { for (;;) { assert(n->n_parent != NULL); if (n->n_parent->n_parent == NULL) break; n = n->n_parent; } return (node_is_direct_key(n)); } bool node_has_wildcards(const struct node *n) { const struct node *child; TAILQ_FOREACH(child, &n->n_children, n_next) { if (strcmp(child->n_key, "*") == 0) return (true); } return (false); } static void node_expand_maps(struct node *n, bool indirect) { struct node *child, *tmp; TAILQ_FOREACH_SAFE(child, &n->n_children, n_next, tmp) { if (node_is_direct_map(child)) { if (indirect) continue; } else { if (indirect == false) continue; } /* * This is the first-level map node; the one that contains * the key and subnodes with mountpoints and actual map names. */ if (child->n_map == NULL) continue; if (indirect) { log_debugx("map \"%s\" is an indirect map, parsing", child->n_map); } else { log_debugx("map \"%s\" is a direct map, parsing", child->n_map); } parse_map(child, child->n_map, NULL, NULL); } } static void node_expand_direct_maps(struct node *n) { node_expand_maps(n, false); } void node_expand_indirect_maps(struct node *n) { node_expand_maps(n, true); } static char * node_path_x(const struct node *n, char *x) { char *path; if (n->n_parent == NULL) return (x); /* * Return "/-" for direct maps only if we were asked for path * to the "/-" node itself, not to any of its subnodes. */ if (node_is_direct_key(n) && x[0] != '\0') return (x); assert(n->n_key[0] != '\0'); path = concat(n->n_key, '/', x); free(x); return (node_path_x(n->n_parent, path)); } /* * Return full path for node, consisting of concatenated * paths of node itself and all its parents, up to the root. */ char * node_path(const struct node *n) { char *path; size_t len; path = node_path_x(n, checked_strdup("")); /* * Strip trailing slash, unless the whole path is "/". */ len = strlen(path); if (len > 1 && path[len - 1] == '/') path[len - 1] = '\0'; return (path); } static char * node_options_x(const struct node *n, char *x) { char *options; if (n == NULL) return (x); options = concat(x, ',', n->n_options); free(x); return (node_options_x(n->n_parent, options)); } /* * Return options for node, consisting of concatenated * options from the node itself and all its parents, * up to the root. */ char * node_options(const struct node *n) { return (node_options_x(n, checked_strdup(""))); } static void node_print_indent(const struct node *n, const char *cmdline_options, int indent) { const struct node *child, *first_child; char *path, *options, *tmp; path = node_path(n); tmp = node_options(n); options = concat(cmdline_options, ',', tmp); free(tmp); /* * Do not show both parent and child node if they have the same * mountpoint; only show the child node. This means the typical, * "key location", map entries are shown in a single line; * the "key mountpoint1 location2 mountpoint2 location2" entries * take multiple lines. */ first_child = TAILQ_FIRST(&n->n_children); if (first_child == NULL || TAILQ_NEXT(first_child, n_next) != NULL || strcmp(path, node_path(first_child)) != 0) { assert(n->n_location == NULL || n->n_map == NULL); printf("%*.s%-*s %s%-*s %-*s # %s map %s at %s:%d\n", indent, "", 25 - indent, path, options[0] != '\0' ? "-" : " ", 20, options[0] != '\0' ? options : "", 20, n->n_location != NULL ? n->n_location : n->n_map != NULL ? n->n_map : "", node_is_direct_map(n) ? "direct" : "indirect", indent == 0 ? "referenced" : "defined", n->n_config_file, n->n_config_line); } free(path); free(options); TAILQ_FOREACH(child, &n->n_children, n_next) node_print_indent(child, cmdline_options, indent + 2); } /* * Recursively print node with all its children. The cmdline_options * argument is used for additional options to be prepended to all the * others - usually those are the options passed by command line. */ void node_print(const struct node *n, const char *cmdline_options) { const struct node *child; TAILQ_FOREACH(child, &n->n_children, n_next) node_print_indent(child, cmdline_options, 0); } static struct node * node_find_x(struct node *node, const char *path) { struct node *child, *found; char *tmp; size_t tmplen; //log_debugx("looking up %s in %s", path, node_path(node)); if (!node_is_direct_key(node)) { tmp = node_path(node); tmplen = strlen(tmp); if (strncmp(tmp, path, tmplen) != 0) { free(tmp); return (NULL); } if (path[tmplen] != '/' && path[tmplen] != '\0') { /* * If we have two map entries like 'foo' and 'foobar', make * sure the search for 'foobar' won't match 'foo' instead. */ free(tmp); return (NULL); } free(tmp); } TAILQ_FOREACH(child, &node->n_children, n_next) { found = node_find_x(child, path); if (found != NULL) return (found); } if (node->n_parent == NULL || node_is_direct_key(node)) return (NULL); return (node); } struct node * node_find(struct node *root, const char *path) { struct node *node; assert(root->n_parent == NULL); node = node_find_x(root, path); if (node != NULL) assert(node != root); return (node); } /* * Canonical form of a map entry looks like this: * * key [-options] [ [/mountpoint] [-options2] location ... ] * * Entries for executable maps are slightly different, as they * lack the 'key' field and are always single-line; the key field * for those maps is taken from 'executable_key' argument. * * We parse it in such a way that a map always has two levels - first * for key, and the second, for the mountpoint. */ static void parse_map_yyin(struct node *parent, const char *map, const char *executable_key) { char *key = NULL, *options = NULL, *mountpoint = NULL, *options2 = NULL, *location = NULL; int ret; struct node *node; lineno = 1; if (executable_key != NULL) key = checked_strdup(executable_key); for (;;) { ret = yylex(); if (ret == 0 || ret == NEWLINE) { /* * In case of executable map, the key is always * non-NULL, even if the map is empty. So, make sure * we don't fail empty maps here. */ if ((key != NULL && executable_key == NULL) || options != NULL) { log_errx(1, "truncated entry at %s, line %d", map, lineno); } if (ret == 0 || executable_key != NULL) { /* * End of file. */ break; } else { key = options = NULL; continue; } } if (key == NULL) { key = checked_strdup(yytext); if (key[0] == '+') { node_new(parent, key, NULL, NULL, map, lineno); key = options = NULL; continue; } continue; } else if (yytext[0] == '-') { if (options != NULL) { log_errx(1, "duplicated options at %s, line %d", map, lineno); } /* * +1 to skip leading "-". */ options = checked_strdup(yytext + 1); continue; } /* * We cannot properly handle a situation where the map key * is "/". Ignore such entries. * * XXX: According to Piete Brooks, Linux automounter uses * "/" as a wildcard character in LDAP maps. Perhaps * we should work around this braindamage by substituting * "*" for "/"? */ if (strcmp(key, "/") == 0) { log_warnx("nonsensical map key \"/\" at %s, line %d; " "ignoring map entry ", map, lineno); /* * Skip the rest of the entry. */ do { ret = yylex(); } while (ret != 0 && ret != NEWLINE); key = options = NULL; continue; } //log_debugx("adding map node, %s", key); node = node_new(parent, key, options, NULL, map, lineno); key = options = NULL; for (;;) { if (yytext[0] == '/') { if (mountpoint != NULL) { log_errx(1, "duplicated mountpoint " "in %s, line %d", map, lineno); } if (options2 != NULL || location != NULL) { log_errx(1, "mountpoint out of order " "in %s, line %d", map, lineno); } mountpoint = checked_strdup(yytext); goto again; } if (yytext[0] == '-') { if (options2 != NULL) { log_errx(1, "duplicated options " "in %s, line %d", map, lineno); } if (location != NULL) { log_errx(1, "options out of order " "in %s, line %d", map, lineno); } options2 = checked_strdup(yytext + 1); goto again; } if (location != NULL) { log_errx(1, "too many arguments " "in %s, line %d", map, lineno); } /* * If location field starts with colon, e.g. ":/dev/cd0", * then strip it. */ if (yytext[0] == ':') { location = checked_strdup(yytext + 1); if (location[0] == '\0') { log_errx(1, "empty location in %s, " "line %d", map, lineno); } } else { location = checked_strdup(yytext); } if (mountpoint == NULL) mountpoint = checked_strdup("/"); if (options2 == NULL) options2 = checked_strdup(""); #if 0 log_debugx("adding map node, %s %s %s", mountpoint, options2, location); #endif node_new(node, mountpoint, options2, location, map, lineno); mountpoint = options2 = location = NULL; again: ret = yylex(); if (ret == 0 || ret == NEWLINE) { if (mountpoint != NULL || options2 != NULL || location != NULL) { log_errx(1, "truncated entry " "in %s, line %d", map, lineno); } break; } } } } /* * Parse output of a special map called without argument. It is a list * of keys, separated by newlines. They can contain whitespace, so use * getline(3) instead of lexer used for maps. */ static void parse_map_keys_yyin(struct node *parent, const char *map) { char *line = NULL, *key; size_t linecap = 0; ssize_t linelen; lineno = 1; for (;;) { linelen = getline(&line, &linecap, yyin); if (linelen < 0) { /* * End of file. */ break; } if (linelen <= 1) { /* * Empty line, consisting of just the newline. */ continue; } /* * "-1" to strip the trailing newline. */ key = strndup(line, linelen - 1); log_debugx("adding key \"%s\"", key); node_new(parent, key, NULL, NULL, map, lineno); lineno++; } free(line); } static bool file_is_executable(const char *path) { struct stat sb; int error; error = stat(path, &sb); if (error != 0) log_err(1, "cannot stat %s", path); if ((sb.st_mode & S_IXUSR) || (sb.st_mode & S_IXGRP) || (sb.st_mode & S_IXOTH)) return (true); return (false); } /* * Parse a special map, e.g. "-hosts". */ static void parse_special_map(struct node *parent, const char *map, const char *key) { char *path; int error, ret; assert(map[0] == '-'); /* * +1 to skip leading "-" in map name. */ ret = asprintf(&path, "%s/special_%s", AUTO_SPECIAL_PREFIX, map + 1); if (ret < 0) log_err(1, "asprintf"); yyin = auto_popen(path, key, NULL); assert(yyin != NULL); if (key == NULL) { parse_map_keys_yyin(parent, map); } else { parse_map_yyin(parent, map, key); } error = auto_pclose(yyin); yyin = NULL; if (error != 0) log_errx(1, "failed to handle special map \"%s\"", map); node_expand_includes(parent, false); node_expand_direct_maps(parent); free(path); } /* * Retrieve and parse map from directory services, e.g. LDAP. * Note that it is different from executable maps, in that * the include script outputs the whole map to standard output * (as opposed to executable maps that only output a single * entry, without the key), and it takes the map name as an * argument, instead of key. */ static void parse_included_map(struct node *parent, const char *map) { int error; assert(map[0] != '-'); assert(map[0] != '/'); error = access(AUTO_INCLUDE_PATH, F_OK); if (error != 0) { log_errx(1, "directory services not configured;" " %s does not exist", AUTO_INCLUDE_PATH); } yyin = auto_popen(AUTO_INCLUDE_PATH, map, NULL); assert(yyin != NULL); parse_map_yyin(parent, map, NULL); error = auto_pclose(yyin); yyin = NULL; if (error != 0) log_errx(1, "failed to handle remote map \"%s\"", map); node_expand_includes(parent, false); node_expand_direct_maps(parent); } void parse_map(struct node *parent, const char *map, const char *key, bool *wildcards) { char *path = NULL; int error, ret; bool executable; assert(map != NULL); assert(map[0] != '\0'); log_debugx("parsing map \"%s\"", map); if (wildcards != NULL) *wildcards = false; if (map[0] == '-') { if (wildcards != NULL) *wildcards = true; return (parse_special_map(parent, map, key)); } if (map[0] == '/') { path = checked_strdup(map); } else { ret = asprintf(&path, "%s/%s", AUTO_MAP_PREFIX, map); if (ret < 0) log_err(1, "asprintf"); log_debugx("map \"%s\" maps to \"%s\"", map, path); /* * See if the file exists. If not, try to obtain the map * from directory services. */ error = access(path, F_OK); if (error != 0) { log_debugx("map file \"%s\" does not exist; falling " "back to directory services", path); return (parse_included_map(parent, map)); } } executable = file_is_executable(path); if (executable) { log_debugx("map \"%s\" is executable", map); if (wildcards != NULL) *wildcards = true; if (key != NULL) { yyin = auto_popen(path, key, NULL); } else { yyin = auto_popen(path, NULL); } assert(yyin != NULL); } else { yyin = fopen(path, "r"); if (yyin == NULL) log_err(1, "unable to open \"%s\"", path); } free(path); path = NULL; parse_map_yyin(parent, map, executable ? key : NULL); if (executable) { error = auto_pclose(yyin); yyin = NULL; if (error != 0) { log_errx(1, "failed to handle executable map \"%s\"", map); } } else { fclose(yyin); } yyin = NULL; log_debugx("done parsing map \"%s\"", map); node_expand_includes(parent, false); node_expand_direct_maps(parent); } static void parse_master_yyin(struct node *root, const char *master) { char *mountpoint = NULL, *map = NULL, *options = NULL; int ret; /* * XXX: 1 gives incorrect values; wtf? */ lineno = 0; for (;;) { ret = yylex(); if (ret == 0 || ret == NEWLINE) { if (mountpoint != NULL) { //log_debugx("adding map for %s", mountpoint); node_new_map(root, mountpoint, options, map, master, lineno); } if (ret == 0) { break; } else { mountpoint = map = options = NULL; continue; } } if (mountpoint == NULL) { mountpoint = checked_strdup(yytext); } else if (map == NULL) { map = checked_strdup(yytext); } else if (options == NULL) { /* * +1 to skip leading "-". */ options = checked_strdup(yytext + 1); } else { log_errx(1, "too many arguments at %s, line %d", master, lineno); } } } void parse_master(struct node *root, const char *master) { log_debugx("parsing auto_master file at \"%s\"", master); yyin = fopen(master, "r"); if (yyin == NULL) err(1, "unable to open %s", master); parse_master_yyin(root, master); fclose(yyin); yyin = NULL; log_debugx("done parsing \"%s\"", master); node_expand_includes(root, true); node_expand_direct_maps(root); } /* * Two things daemon(3) does, that we actually also want to do * when running in foreground, is closing the stdin and chdiring * to "/". This is what we do here. */ void lesser_daemon(void) { int error, fd; error = chdir("/"); if (error != 0) log_warn("chdir"); fd = open(_PATH_DEVNULL, O_RDWR, 0); if (fd < 0) { log_warn("cannot open %s", _PATH_DEVNULL); return; } error = dup2(fd, STDIN_FILENO); if (error != 0) log_warn("dup2"); error = close(fd); if (error != 0) { /* Bloody hell. */ log_warn("close"); } } int main(int argc, char **argv) { char *cmdname; if (argv[0] == NULL) log_errx(1, "NULL command name"); cmdname = basename(argv[0]); if (strcmp(cmdname, "automount") == 0) return (main_automount(argc, argv)); else if (strcmp(cmdname, "automountd") == 0) return (main_automountd(argc, argv)); else if (strcmp(cmdname, "autounmountd") == 0) return (main_autounmountd(argc, argv)); else log_errx(1, "binary name should be either \"automount\", " "\"automountd\", or \"autounmountd\""); } Index: head/usr.sbin/autofs/common.h =================================================================== --- head/usr.sbin/autofs/common.h (revision 328337) +++ head/usr.sbin/autofs/common.h (revision 328338) @@ -1,114 +1,116 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef AUTOMOUNTD_H #define AUTOMOUNTD_H #include #include #define AUTO_MASTER_PATH "/etc/auto_master" #define AUTO_MAP_PREFIX "/etc" #define AUTO_SPECIAL_PREFIX "/etc/autofs" #define AUTO_INCLUDE_PATH AUTO_SPECIAL_PREFIX "/include" struct node { TAILQ_ENTRY(node) n_next; TAILQ_HEAD(nodehead, node) n_children; struct node *n_parent; char *n_key; char *n_options; char *n_location; char *n_map; const char *n_config_file; int n_config_line; }; struct defined_value { TAILQ_ENTRY(defined_value) d_next; char *d_name; char *d_value; }; void log_init(int level); void log_set_peer_name(const char *name); void log_set_peer_addr(const char *addr); void log_err(int, const char *, ...) __dead2 __printf0like(2, 3); void log_errx(int, const char *, ...) __dead2 __printf0like(2, 3); void log_warn(const char *, ...) __printf0like(1, 2); void log_warnx(const char *, ...) __printflike(1, 2); void log_debugx(const char *, ...) __printf0like(1, 2); char *checked_strdup(const char *); char *concat(const char *s1, char separator, const char *s2); void create_directory(const char *path); struct node *node_new_root(void); struct node *node_new(struct node *parent, char *key, char *options, char *location, const char *config_file, int config_line); struct node *node_new_map(struct node *parent, char *key, char *options, char *map, const char *config_file, int config_line); struct node *node_find(struct node *root, const char *mountpoint); bool node_is_direct_map(const struct node *n); bool node_has_wildcards(const struct node *n); char *node_path(const struct node *n); char *node_options(const struct node *n); void node_expand_ampersand(struct node *root, const char *key); void node_expand_wildcard(struct node *root, const char *key); int node_expand_defined(struct node *root); void node_expand_indirect_maps(struct node *n); void node_print(const struct node *n, const char *cmdline_options); void parse_master(struct node *root, const char *path); void parse_map(struct node *parent, const char *map, const char *args, bool *wildcards); char *defined_expand(const char *string); void defined_init(void); void defined_parse_and_add(char *def); void lesser_daemon(void); int main_automount(int argc, char **argv); int main_automountd(int argc, char **argv); int main_autounmountd(int argc, char **argv); FILE *auto_popen(const char *argv0, ...); int auto_pclose(FILE *iop); /* * lex(1) stuff. */ extern int lineno; #define STR 1 #define NEWLINE 2 #endif /* !AUTOMOUNTD_H */ Index: head/usr.sbin/autofs/defined.c =================================================================== --- head/usr.sbin/autofs/defined.c (revision 328337) +++ head/usr.sbin/autofs/defined.c (revision 328338) @@ -1,271 +1,273 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * All the "defined" stuff is for handling variables, * such as ${OSNAME}, in maps. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" static TAILQ_HEAD(, defined_value) defined_values; static const char * defined_find(const char *name) { struct defined_value *d; TAILQ_FOREACH(d, &defined_values, d_next) { if (strcmp(d->d_name, name) == 0) return (d->d_value); } return (NULL); } char * defined_expand(const char *string) { const char *value; char c, *expanded, *name; int i, ret, before_len = 0, name_off = 0, name_len = 0, after_off = 0; bool backslashed = false, bracketed = false; expanded = checked_strdup(string); for (i = 0; string[i] != '\0'; i++) { c = string[i]; if (c == '\\' && backslashed == false) { backslashed = true; continue; } if (backslashed) { backslashed = false; continue; } backslashed = false; if (c != '$') continue; /* * The 'before_len' variable contains the number * of characters before the '$'. */ before_len = i; assert(i + 1 < (int)strlen(string)); if (string[i + 1] == '{') bracketed = true; if (string[i + 1] == '\0') { log_warnx("truncated variable"); return (NULL); } /* * Skip '$'. */ i++; if (bracketed) { if (string[i + 1] == '\0') { log_warnx("truncated variable"); return (NULL); } /* * Skip '{'. */ i++; } /* * The 'name_off' variable contains the number * of characters before the variable name, * including the "$" or "${". */ name_off = i; for (; string[i] != '\0'; i++) { c = string[i]; /* * XXX: Decide on the set of characters that can be * used in a variable name. */ if (isalnum(c) || c == '_') continue; /* * End of variable name. */ if (bracketed) { if (c != '}') continue; /* * The 'after_off' variable contains the number * of characters before the rest of the string, * i.e. after the variable name. */ after_off = i + 1; assert(i > 1); assert(i - 1 > name_off); name_len = i - name_off; break; } after_off = i; assert(i > 1); assert(i > name_off); name_len = i - name_off; break; } name = strndup(string + name_off, name_len); if (name == NULL) log_err(1, "strndup"); value = defined_find(name); if (value == NULL) { log_warnx("undefined variable ${%s}", name); return (NULL); } /* * Concatenate it back. */ ret = asprintf(&expanded, "%.*s%s%s", before_len, string, value, string + after_off); if (ret < 0) log_err(1, "asprintf"); //log_debugx("\"%s\" expanded to \"%s\"", string, expanded); free(name); /* * Figure out where to start searching for next variable. */ string = expanded; i = before_len + strlen(value); backslashed = bracketed = false; before_len = name_off = name_len = after_off = 0; assert(i <= (int)strlen(string)); } if (before_len != 0 || name_off != 0 || name_len != 0 || after_off != 0) { log_warnx("truncated variable"); return (NULL); } return (expanded); } static void defined_add(const char *name, const char *value) { struct defined_value *d; const char *found; found = defined_find(name); if (found != NULL) log_errx(1, "variable %s already defined", name); log_debugx("defining variable %s=%s", name, value); d = calloc(1, sizeof(*d)); if (d == NULL) log_err(1, "calloc"); d->d_name = checked_strdup(name); d->d_value = checked_strdup(value); TAILQ_INSERT_TAIL(&defined_values, d, d_next); } void defined_parse_and_add(char *def) { char *name, *value; value = def; name = strsep(&value, "="); if (value == NULL || value[0] == '\0') log_errx(1, "missing variable value"); if (name == NULL || name[0] == '\0') log_errx(1, "missing variable name"); defined_add(name, value); } void defined_init(void) { struct utsname name; int error; TAILQ_INIT(&defined_values); error = uname(&name); if (error != 0) log_err(1, "uname"); defined_add("ARCH", name.machine); defined_add("CPU", name.machine); defined_add("HOST", name.nodename); defined_add("OSNAME", name.sysname); defined_add("OSREL", name.release); defined_add("OSVERS", name.version); } Index: head/usr.sbin/autofs/log.c =================================================================== --- head/usr.sbin/autofs/log.c (revision 328337) +++ head/usr.sbin/autofs/log.c (revision 328338) @@ -1,201 +1,203 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "common.h" static int log_level = 0; static char *peer_name = NULL; static char *peer_addr = NULL; #define MSGBUF_LEN 1024 void log_init(int level) { log_level = level; openlog(getprogname(), LOG_NDELAY | LOG_PID, LOG_DAEMON); } void log_set_peer_name(const char *name) { /* * XXX: Turn it into assertion? */ if (peer_name != NULL) log_errx(1, "%s called twice", __func__); if (peer_addr == NULL) log_errx(1, "%s called before log_set_peer_addr", __func__); peer_name = checked_strdup(name); } void log_set_peer_addr(const char *addr) { /* * XXX: Turn it into assertion? */ if (peer_addr != NULL) log_errx(1, "%s called twice", __func__); peer_addr = checked_strdup(addr); } static void log_common(int priority, int log_errno, const char *fmt, va_list ap) { static char msgbuf[MSGBUF_LEN]; static char msgbuf_strvised[MSGBUF_LEN * 4 + 1]; char *errstr; int ret; ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); if (ret < 0) { fprintf(stderr, "%s: snprintf failed", getprogname()); syslog(LOG_CRIT, "snprintf failed"); exit(1); } ret = strnvis(msgbuf_strvised, sizeof(msgbuf_strvised), msgbuf, VIS_NL); if (ret < 0) { fprintf(stderr, "%s: strnvis failed", getprogname()); syslog(LOG_CRIT, "strnvis failed"); exit(1); } if (log_errno == -1) { if (peer_name != NULL) { fprintf(stderr, "%s: %s (%s): %s\n", getprogname(), peer_addr, peer_name, msgbuf_strvised); syslog(priority, "%s (%s): %s", peer_addr, peer_name, msgbuf_strvised); } else if (peer_addr != NULL) { fprintf(stderr, "%s: %s: %s\n", getprogname(), peer_addr, msgbuf_strvised); syslog(priority, "%s: %s", peer_addr, msgbuf_strvised); } else { fprintf(stderr, "%s: %s\n", getprogname(), msgbuf_strvised); syslog(priority, "%s", msgbuf_strvised); } } else { errstr = strerror(log_errno); if (peer_name != NULL) { fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(), peer_addr, peer_name, msgbuf_strvised, errstr); syslog(priority, "%s (%s): %s: %s", peer_addr, peer_name, msgbuf_strvised, errstr); } else if (peer_addr != NULL) { fprintf(stderr, "%s: %s: %s: %s\n", getprogname(), peer_addr, msgbuf_strvised, errstr); syslog(priority, "%s: %s: %s", peer_addr, msgbuf_strvised, errstr); } else { fprintf(stderr, "%s: %s: %s\n", getprogname(), msgbuf_strvised, errstr); syslog(priority, "%s: %s", msgbuf_strvised, errstr); } } } void log_err(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); log_common(LOG_CRIT, errno, fmt, ap); va_end(ap); exit(eval); } void log_errx(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); log_common(LOG_CRIT, -1, fmt, ap); va_end(ap); exit(eval); } void log_warn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); log_common(LOG_WARNING, errno, fmt, ap); va_end(ap); } void log_warnx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); log_common(LOG_WARNING, -1, fmt, ap); va_end(ap); } void log_debugx(const char *fmt, ...) { va_list ap; if (log_level == 0) return; va_start(ap, fmt); log_common(LOG_DEBUG, -1, fmt, ap); va_end(ap); } Index: head/usr.sbin/autofs/popen.c =================================================================== --- head/usr.sbin/autofs/popen.c (revision 328337) +++ head/usr.sbin/autofs/popen.c (revision 328338) @@ -1,193 +1,195 @@ /* + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This code is derived from software written by Ken Arnold and * published in UNIX Review, Vol. 6, No. 8. * * Portions of this software were developed by Edward Tomasz Napierala * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "common.h" extern char **environ; struct pid { SLIST_ENTRY(pid) next; FILE *outfp; pid_t pid; char *command; }; static SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist); #define ARGV_LEN 42 /* * Replacement for popen(3), without stdin (which we do not use), but with * stderr, proper logging, and improved command line arguments passing. * Error handling is built in - if it returns, then it succeeded. */ FILE * auto_popen(const char *argv0, ...) { va_list ap; struct pid *cur, *p; pid_t pid; int error, i, nullfd, outfds[2]; char *arg, *argv[ARGV_LEN], *command; nullfd = open(_PATH_DEVNULL, O_RDWR, 0); if (nullfd < 0) log_err(1, "cannot open %s", _PATH_DEVNULL); error = pipe(outfds); if (error != 0) log_err(1, "pipe"); cur = malloc(sizeof(struct pid)); if (cur == NULL) log_err(1, "malloc"); argv[0] = checked_strdup(argv0); command = argv[0]; va_start(ap, argv0); for (i = 1;; i++) { if (i >= ARGV_LEN) log_errx(1, "too many arguments to auto_popen"); arg = va_arg(ap, char *); argv[i] = arg; if (arg == NULL) break; command = concat(command, ' ', arg); } va_end(ap); cur->command = checked_strdup(command); switch (pid = fork()) { case -1: /* Error. */ log_err(1, "fork"); /* NOTREACHED */ case 0: /* Child. */ dup2(nullfd, STDIN_FILENO); dup2(outfds[1], STDOUT_FILENO); close(nullfd); close(outfds[0]); close(outfds[1]); SLIST_FOREACH(p, &pidlist, next) close(fileno(p->outfp)); execvp(argv[0], argv); log_err(1, "failed to execute %s", argv[0]); /* NOTREACHED */ } log_debugx("executing \"%s\" as pid %d", command, pid); /* Parent; assume fdopen cannot fail. */ cur->outfp = fdopen(outfds[0], "r"); close(nullfd); close(outfds[1]); /* Link into list of file descriptors. */ cur->pid = pid; SLIST_INSERT_HEAD(&pidlist, cur, next); return (cur->outfp); } int auto_pclose(FILE *iop) { struct pid *cur, *last = NULL; int status; pid_t pid; /* * Find the appropriate file pointer and remove it from the list. */ SLIST_FOREACH(cur, &pidlist, next) { if (cur->outfp == iop) break; last = cur; } if (cur == NULL) { return (-1); } if (last == NULL) SLIST_REMOVE_HEAD(&pidlist, next); else SLIST_REMOVE_AFTER(last, next); fclose(cur->outfp); do { pid = wait4(cur->pid, &status, 0, NULL); } while (pid == -1 && errno == EINTR); if (WIFSIGNALED(status)) { log_warnx("\"%s\", pid %d, terminated with signal %d", cur->command, pid, WTERMSIG(status)); return (status); } if (WEXITSTATUS(status) != 0) { log_warnx("\"%s\", pid %d, terminated with exit status %d", cur->command, pid, WEXITSTATUS(status)); return (status); } log_debugx("\"%s\", pid %d, terminated gracefully", cur->command, pid); free(cur->command); free(cur); return (pid == -1 ? -1 : status); } Index: head/usr.sbin/autofs/token.l =================================================================== --- head/usr.sbin/autofs/token.l (revision 328337) +++ head/usr.sbin/autofs/token.l (revision 328338) @@ -1,58 +1,60 @@ %{ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include "common.h" int lineno; #define YY_DECL int yylex(void) extern int yylex(void); %} %option noinput %option nounput %option noyywrap %% \"[^"]+\" { yytext++; yytext[strlen(yytext) - 1] = '\0'; return STR; }; [a-zA-Z0-9\.\+-_/\:\[\]$&%{}]+ { return STR; } #.*\n { lineno++; return NEWLINE; }; \\\n { lineno++; }; \n { lineno++; return NEWLINE; } [ \t]+ /* ignore whitespace */; . { return STR; } %%