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