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