xref: /netbsd-src/usr.sbin/autofs/automount.c (revision 5628820aad1597d6c95795148f19b1e219c2967f)
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@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
unmount_by_statfs(const struct statvfs * sb,bool force)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 *
find_statfs(const struct statvfs * mntbuf,int nitems,const char * mountpoint)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
mount_autofs(const char * from,const char * fspath,const char * options,const char * prefix)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
mount_if_not_already(const struct node * n,const char * map,const char * options,const char * prefix,const struct statvfs * mntbuf,int nitems)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
mount_unmount(struct node * root)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
flush_autofs(const char * fspath)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
flush_caches(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
unmount_automounted(bool force)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
usage_automount(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
main_automount(int argc,char ** argv)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