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
50static int
51unmount_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
67static const struct statvfs *
68find_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
80static void
81mount_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
102static void
103mount_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
142static void
143mount_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
191static void
192flush_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
203static void
204flush_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
226static void
227unmount_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
256usage_automount(void)
257{
258
259	fprintf(stderr, "Usage: %s [-D name=value][-o opts][-Lcfuv]\n",
260	    getprogname());
261	exit(1);
262}
263
264int
265main_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