Home | History | Annotate | Line # | Download | only in autofs
      1 /*	$NetBSD: automount.c,v 1.4 2022/05/04 11:27:54 tkusumi Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2017 The NetBSD Foundation, Inc.
      5  * Copyright (c) 2016 The DragonFly Project
      6  * Copyright (c) 2014 The FreeBSD Foundation
      7  * All rights reserved.
      8  *
      9  * This code is derived from software contributed to The NetBSD Foundation
     10  * by Tomohiro Kusumi <kusumi.tomohiro (at) gmail.com>.
     11  *
     12  * This software was developed by Edward Tomasz Napierala under sponsorship
     13  * from the FreeBSD Foundation.
     14  *
     15  * Redistribution and use in source and binary forms, with or without
     16  * modification, are permitted provided that the following conditions
     17  * are met:
     18  * 1. Redistributions of source code must retain the above copyright
     19  *    notice, this list of conditions and the following disclaimer.
     20  * 2. Redistributions in binary form must reproduce the above copyright
     21  *    notice, this list of conditions and the following disclaimer in the
     22  *    documentation and/or other materials provided with the distribution.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  */
     36 #include <sys/cdefs.h>
     37 __RCSID("$NetBSD: automount.c,v 1.4 2022/05/04 11:27:54 tkusumi Exp $");
     38 
     39 #include <sys/types.h>
     40 #include <sys/mount.h>
     41 #include <sys/statvfs.h>
     42 #include <stdio.h>
     43 #include <stdlib.h>
     44 #include <string.h>
     45 #include <unistd.h>
     46 #include <fs/autofs/autofs_mount.h>
     47 
     48 #include "common.h"
     49 
     50 static int
     51 unmount_by_statfs(const struct statvfs *sb, bool force)
     52 {
     53 	int error, flags;
     54 
     55 	log_debugx("unmounting %s", sb->f_mntonname);
     56 
     57 	flags = 0;
     58 	if (force)
     59 		flags |= MNT_FORCE;
     60 	error = unmount(sb->f_mntonname, flags);
     61 	if (error != 0)
     62 		log_warn("cannot unmount %s", sb->f_mntonname);
     63 
     64 	return error;
     65 }
     66 
     67 static const struct statvfs *
     68 find_statfs(const struct statvfs *mntbuf, int nitems, const char *mountpoint)
     69 {
     70 	int i;
     71 
     72 	for (i = 0; i < nitems; i++) {
     73 		if (strcmp(mntbuf[i].f_mntonname, mountpoint) == 0)
     74 			return mntbuf + i;
     75 	}
     76 
     77 	return NULL;
     78 }
     79 
     80 static void
     81 mount_autofs(const char *from, const char *fspath, const char *options,
     82     const char *prefix)
     83 {
     84 	struct autofs_args args;
     85 	int error;
     86 
     87 	create_directory(fspath);
     88 
     89 	log_debugx("mounting %s on %s, prefix \"%s\", options \"%s\"",
     90 	    from, fspath, prefix, options);
     91 
     92 	memset(&args, 0, sizeof(args));
     93 	args.from = __UNCONST(from);
     94 	args.master_options = __UNCONST(options);
     95 	args.master_prefix = __UNCONST(prefix);
     96 
     97 	error = mount("autofs", fspath, 0, &args, sizeof(args));
     98 	if (error != 0)
     99 		log_err(1, "cannot mount %s on %s", from, fspath);
    100 }
    101 
    102 static void
    103 mount_if_not_already(const struct node *n, const char *map, const char *options,
    104     const char *prefix, const struct statvfs *mntbuf, int nitems)
    105 {
    106 	const struct statvfs *sb;
    107 	char *mountpoint;
    108 	char *from;
    109 	int ret;
    110 
    111 	ret = asprintf(&from, "map %s", map);
    112 	if (ret < 0)
    113 		log_err(1, "asprintf");
    114 
    115 	mountpoint = node_path(n);
    116 	sb = find_statfs(mntbuf, nitems, mountpoint);
    117 	if (sb != NULL) {
    118 		if (strcmp(sb->f_fstypename, "autofs") != 0) {
    119 			log_debugx("unknown filesystem mounted "
    120 			    "on %s; mounting", mountpoint);
    121 			/*
    122 			 * XXX: Compare options and 'from',
    123 			 *	and update the mount if necessary.
    124 			 */
    125 		} else {
    126 			log_debugx("autofs already mounted "
    127 			    "on %s", mountpoint);
    128 			free(from);
    129 			free(mountpoint);
    130 			return;
    131 		}
    132 	} else {
    133 		log_debugx("nothing mounted on %s; mounting",
    134 		    mountpoint);
    135 	}
    136 
    137 	mount_autofs(from, mountpoint, options, prefix);
    138 	free(from);
    139 	free(mountpoint);
    140 }
    141 
    142 static void
    143 mount_unmount(struct node *root)
    144 {
    145 	struct statvfs *mntbuf;
    146 	struct node *n, *n2;
    147 	int i, nitems;
    148 	static char rootdir[] = "/";
    149 
    150 	nitems = getmntinfo(&mntbuf, MNT_WAIT);
    151 	if (nitems <= 0)
    152 		log_err(1, "getmntinfo");
    153 
    154 	log_debugx("unmounting stale autofs mounts");
    155 
    156 	for (i = 0; i < nitems; i++) {
    157 		if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) {
    158 			log_debugx("skipping %s, filesystem type is not autofs",
    159 			    mntbuf[i].f_mntonname);
    160 			continue;
    161 		}
    162 
    163 		n = node_find(root, mntbuf[i].f_mntonname);
    164 		if (n != NULL) {
    165 			log_debugx("leaving autofs mounted on %s",
    166 			    mntbuf[i].f_mntonname);
    167 			continue;
    168 		}
    169 
    170 		log_debugx("autofs mounted on %s not found "
    171 		    "in new configuration; unmounting", mntbuf[i].f_mntonname);
    172 		unmount_by_statfs(&(mntbuf[i]), false);
    173 	}
    174 
    175 	log_debugx("mounting new autofs mounts");
    176 
    177 	TAILQ_FOREACH(n, &root->n_children, n_next) {
    178 		if (!node_is_direct_map(n)) {
    179 			mount_if_not_already(n, n->n_map, n->n_options,
    180 			    n->n_key, mntbuf, nitems);
    181 			continue;
    182 		}
    183 
    184 		TAILQ_FOREACH(n2, &n->n_children, n_next) {
    185 			mount_if_not_already(n2, n->n_map, n->n_options,
    186 			    rootdir, mntbuf, nitems);
    187 		}
    188 	}
    189 }
    190 
    191 static void
    192 flush_autofs(const char *fspath)
    193 {
    194 	int error;
    195 
    196 	log_debugx("flushing %s", fspath);
    197 
    198 	error = mount("autofs", fspath, MNT_UPDATE, NULL, 0);
    199 	if (error != 0)
    200 		log_err(1, "cannot flush %s", fspath);
    201 }
    202 
    203 static void
    204 flush_caches(void)
    205 {
    206 	struct statvfs *mntbuf;
    207 	int i, nitems;
    208 
    209 	nitems = getmntinfo(&mntbuf, MNT_WAIT);
    210 	if (nitems <= 0)
    211 		log_err(1, "getmntinfo");
    212 
    213 	log_debugx("flushing autofs caches");
    214 
    215 	for (i = 0; i < nitems; i++) {
    216 		if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) {
    217 			log_debugx("skipping %s, filesystem type is not autofs",
    218 			    mntbuf[i].f_mntonname);
    219 			continue;
    220 		}
    221 
    222 		flush_autofs(mntbuf[i].f_mntonname);
    223 	}
    224 }
    225 
    226 static void
    227 unmount_automounted(bool force)
    228 {
    229 	struct statvfs *mntbuf;
    230 	int i, nitems;
    231 
    232 	nitems = getmntinfo(&mntbuf, MNT_WAIT);
    233 	if (nitems <= 0)
    234 		log_err(1, "getmntinfo");
    235 
    236 	log_debugx("unmounting automounted filesystems");
    237 
    238 	for (i = 0; i < nitems; i++) {
    239 		if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) {
    240 			log_debugx("skipping %s, filesystem type is autofs",
    241 			    mntbuf[i].f_mntonname);
    242 			continue;
    243 		}
    244 
    245 		if ((mntbuf[i].f_flag & MNT_AUTOMOUNTED) == 0) {
    246 			log_debugx("skipping %s, not automounted",
    247 			    mntbuf[i].f_mntonname);
    248 			continue;
    249 		}
    250 
    251 		unmount_by_statfs(&(mntbuf[i]), force);
    252 	}
    253 }
    254 
    255 __dead static void
    256 usage_automount(void)
    257 {
    258 
    259 	fprintf(stderr, "Usage: %s [-D name=value][-o opts][-Lcfuv]\n",
    260 	    getprogname());
    261 	exit(1);
    262 }
    263 
    264 int
    265 main_automount(int argc, char **argv)
    266 {
    267 	struct node *root;
    268 	int ch, debug = 0, show_maps = 0;
    269 	char *options = NULL;
    270 	bool do_unmount = false, force_unmount = false, flush = false;
    271 
    272 	/*
    273 	 * Note that in automount(8), the only purpose of variable
    274 	 * handling is to aid in debugging maps (automount -L).
    275 	 */
    276 	defined_init();
    277 
    278 	while ((ch = getopt(argc, argv, "D:Lfco:uv")) != -1) {
    279 		switch (ch) {
    280 		case 'D':
    281 			defined_parse_and_add(optarg);
    282 			break;
    283 		case 'L':
    284 			show_maps++;
    285 			break;
    286 		case 'c':
    287 			flush = true;
    288 			break;
    289 		case 'f':
    290 			force_unmount = true;
    291 			break;
    292 		case 'o':
    293 			options = concat(options, ',', optarg);
    294 			break;
    295 		case 'u':
    296 			do_unmount = true;
    297 			break;
    298 		case 'v':
    299 			debug++;
    300 			break;
    301 		case '?':
    302 		default:
    303 			usage_automount();
    304 		}
    305 	}
    306 	argc -= optind;
    307 	if (argc != 0)
    308 		usage_automount();
    309 
    310 	if (force_unmount && !do_unmount)
    311 		usage_automount();
    312 
    313 	log_init(debug);
    314 
    315 	if (flush) {
    316 		flush_caches();
    317 		return 0;
    318 	}
    319 
    320 	if (do_unmount) {
    321 		unmount_automounted(force_unmount);
    322 		return 0;
    323 	}
    324 
    325 	root = node_new_root();
    326 	parse_master(root, AUTO_MASTER_PATH);
    327 
    328 	if (show_maps) {
    329 		if (show_maps > 1) {
    330 			node_expand_indirect_maps(root);
    331 			node_expand_ampersand(root, NULL);
    332 		}
    333 		node_expand_defined(root);
    334 		node_print(root, options);
    335 		return 0;
    336 	}
    337 
    338 	mount_unmount(root);
    339 
    340 	return 0;
    341 }
    342