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