Home | History | Annotate | Line # | Download | only in autofs
automount.c revision 1.1
      1 /*	$NetBSD: automount.c,v 1.1 2018/01/09 03:31:15 christos 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.1 2018/01/09 03:31:15 christos 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 	char *cwd;
     87 
     88 	/*
     89 	 * There is no guarantee we are at /, so chdir to /.
     90 	 */
     91 	cwd = getcwd(NULL, 0);
     92 	if (chdir("/") != 0)
     93 		log_warn("failed to chdir to /");
     94 	create_directory(fspath);
     95 	if (chdir(cwd) != 0)
     96 		log_warn("failed to restore cwd");
     97 	free(cwd);
     98 
     99 	log_debugx("mounting %s on %s, prefix \"%s\", options \"%s\"",
    100 	    from, fspath, prefix, options);
    101 
    102 	memset(&args, 0, sizeof(args));
    103 	args.from = from;
    104 	args.master_options = options;
    105 	args.master_prefix = prefix;
    106 
    107 	error = mount("autofs", fspath, 0, &args, sizeof(args));
    108 	if (error != 0)
    109 		log_err(1, "cannot mount %s on %s", from, fspath);
    110 }
    111 
    112 static void
    113 mount_if_not_already(const struct node *n, const char *map, const char *options,
    114     const char *prefix, const struct statvfs *mntbuf, int nitems)
    115 {
    116 	const struct statvfs *sb;
    117 	char *mountpoint;
    118 	char *from;
    119 	int ret;
    120 
    121 	ret = asprintf(&from, "map %s", map);
    122 	if (ret < 0)
    123 		log_err(1, "asprintf");
    124 
    125 	mountpoint = node_path(n);
    126 	sb = find_statfs(mntbuf, nitems, mountpoint);
    127 	if (sb != NULL) {
    128 		if (strcmp(sb->f_fstypename, "autofs") != 0) {
    129 			log_debugx("unknown filesystem mounted "
    130 			    "on %s; mounting", mountpoint);
    131 			/*
    132 			 * XXX: Compare options and 'from',
    133 			 *	and update the mount if necessary.
    134 			 */
    135 		} else {
    136 			log_debugx("autofs already mounted "
    137 			    "on %s", mountpoint);
    138 			free(from);
    139 			free(mountpoint);
    140 			return;
    141 		}
    142 	} else {
    143 		log_debugx("nothing mounted on %s; mounting",
    144 		    mountpoint);
    145 	}
    146 
    147 	mount_autofs(from, mountpoint, options, prefix);
    148 	free(from);
    149 	free(mountpoint);
    150 }
    151 
    152 static void
    153 mount_unmount(struct node *root)
    154 {
    155 	struct statvfs *mntbuf;
    156 	struct node *n, *n2;
    157 	int i, nitems;
    158 
    159 	nitems = getmntinfo(&mntbuf, MNT_WAIT);
    160 	if (nitems <= 0)
    161 		log_err(1, "getmntinfo");
    162 
    163 	log_debugx("unmounting stale autofs mounts");
    164 
    165 	for (i = 0; i < nitems; i++) {
    166 		if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) {
    167 			log_debugx("skipping %s, filesystem type is not autofs",
    168 			    mntbuf[i].f_mntonname);
    169 			continue;
    170 		}
    171 
    172 		n = node_find(root, mntbuf[i].f_mntonname);
    173 		if (n != NULL) {
    174 			log_debugx("leaving autofs mounted on %s",
    175 			    mntbuf[i].f_mntonname);
    176 			continue;
    177 		}
    178 
    179 		log_debugx("autofs mounted on %s not found "
    180 		    "in new configuration; unmounting", mntbuf[i].f_mntonname);
    181 		unmount_by_statfs(&(mntbuf[i]), false);
    182 	}
    183 
    184 	log_debugx("mounting new autofs mounts");
    185 
    186 	TAILQ_FOREACH(n, &root->n_children, n_next) {
    187 		if (!node_is_direct_map(n)) {
    188 			mount_if_not_already(n, n->n_map, n->n_options,
    189 			    n->n_key, mntbuf, nitems);
    190 			continue;
    191 		}
    192 
    193 		TAILQ_FOREACH(n2, &n->n_children, n_next) {
    194 			mount_if_not_already(n2, n->n_map, n->n_options,
    195 			    "/", mntbuf, nitems);
    196 		}
    197 	}
    198 }
    199 
    200 static void
    201 flush_autofs(const char *fspath)
    202 {
    203 	int error;
    204 
    205 	log_debugx("flushing %s", fspath);
    206 
    207 	error = mount("autofs", fspath, MNT_UPDATE, NULL, 0);
    208 	if (error != 0)
    209 		log_err(1, "cannot flush %s", fspath);
    210 }
    211 
    212 static void
    213 flush_caches(void)
    214 {
    215 	struct statvfs *mntbuf;
    216 	int i, nitems;
    217 
    218 	nitems = getmntinfo(&mntbuf, MNT_WAIT);
    219 	if (nitems <= 0)
    220 		log_err(1, "getmntinfo");
    221 
    222 	log_debugx("flushing autofs caches");
    223 
    224 	for (i = 0; i < nitems; i++) {
    225 		if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) {
    226 			log_debugx("skipping %s, filesystem type is not autofs",
    227 			    mntbuf[i].f_mntonname);
    228 			continue;
    229 		}
    230 
    231 		flush_autofs(mntbuf[i].f_mntonname);
    232 	}
    233 }
    234 
    235 static void
    236 unmount_automounted(bool force)
    237 {
    238 	struct statvfs *mntbuf;
    239 	int i, nitems;
    240 
    241 	nitems = getmntinfo(&mntbuf, MNT_WAIT);
    242 	if (nitems <= 0)
    243 		log_err(1, "getmntinfo");
    244 
    245 	log_debugx("unmounting automounted filesystems");
    246 
    247 	for (i = 0; i < nitems; i++) {
    248 		if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) {
    249 			log_debugx("skipping %s, filesystem type is autofs",
    250 			    mntbuf[i].f_mntonname);
    251 			continue;
    252 		}
    253 
    254 		if ((mntbuf[i].f_flag & MNT_AUTOMOUNTED) == 0) {
    255 			log_debugx("skipping %s, not automounted",
    256 			    mntbuf[i].f_mntonname);
    257 			continue;
    258 		}
    259 
    260 		unmount_by_statfs(&(mntbuf[i]), force);
    261 	}
    262 }
    263 
    264 __dead static void
    265 usage_automount(void)
    266 {
    267 
    268 	fprintf(stderr, "Usage: %s [-D name=value][-o opts][-Lcfuv]\n",
    269 	    getprogname());
    270 	exit(1);
    271 }
    272 
    273 int
    274 main_automount(int argc, char **argv)
    275 {
    276 	struct node *root;
    277 	int ch, debug = 0, show_maps = 0;
    278 	char *options = NULL;
    279 	bool do_unmount = false, force_unmount = false, flush = false;
    280 
    281 	/*
    282 	 * Note that in automount(8), the only purpose of variable
    283 	 * handling is to aid in debugging maps (automount -L).
    284 	 */
    285 	defined_init();
    286 
    287 	while ((ch = getopt(argc, argv, "D:Lfco:uv")) != -1) {
    288 		switch (ch) {
    289 		case 'D':
    290 			defined_parse_and_add(optarg);
    291 			break;
    292 		case 'L':
    293 			show_maps++;
    294 			break;
    295 		case 'c':
    296 			flush = true;
    297 			break;
    298 		case 'f':
    299 			force_unmount = true;
    300 			break;
    301 		case 'o':
    302 			options = concat(options, ',', optarg);
    303 			break;
    304 		case 'u':
    305 			do_unmount = true;
    306 			break;
    307 		case 'v':
    308 			debug++;
    309 			break;
    310 		case '?':
    311 		default:
    312 			usage_automount();
    313 		}
    314 	}
    315 	argc -= optind;
    316 	if (argc != 0)
    317 		usage_automount();
    318 
    319 	if (force_unmount && !do_unmount)
    320 		usage_automount();
    321 
    322 	log_init(debug);
    323 
    324 	if (flush) {
    325 		flush_caches();
    326 		return 0;
    327 	}
    328 
    329 	if (do_unmount) {
    330 		unmount_automounted(force_unmount);
    331 		return 0;
    332 	}
    333 
    334 	root = node_new_root();
    335 	parse_master(root, AUTO_MASTER_PATH);
    336 
    337 	if (show_maps) {
    338 		if (show_maps > 1) {
    339 			node_expand_indirect_maps(root);
    340 			node_expand_ampersand(root, NULL);
    341 		}
    342 		node_expand_defined(root);
    343 		node_print(root, options);
    344 		return 0;
    345 	}
    346 
    347 	mount_unmount(root);
    348 
    349 	return 0;
    350 }
    351