1270096Strasz/*-
2270096Strasz * Copyright (c) 2014 The FreeBSD Foundation
3270096Strasz * All rights reserved.
4270096Strasz *
5270096Strasz * This software was developed by Edward Tomasz Napierala under sponsorship
6270096Strasz * from the FreeBSD Foundation.
7270096Strasz *
8270096Strasz * Redistribution and use in source and binary forms, with or without
9270096Strasz * modification, are permitted provided that the following conditions
10270096Strasz * are met:
11270096Strasz * 1. Redistributions of source code must retain the above copyright
12270096Strasz *    notice, this list of conditions and the following disclaimer.
13270096Strasz * 2. Redistributions in binary form must reproduce the above copyright
14270096Strasz *    notice, this list of conditions and the following disclaimer in the
15270096Strasz *    documentation and/or other materials provided with the distribution.
16270096Strasz *
17270096Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18270096Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19270096Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20270096Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21270096Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22270096Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23270096Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24270096Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25270096Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26270096Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27270096Strasz * SUCH DAMAGE.
28270096Strasz *
29270096Strasz */
30270096Strasz
31270897Strasz#include <sys/cdefs.h>
32270897Strasz__FBSDID("$FreeBSD$");
33270897Strasz
34270096Strasz#include <sys/types.h>
35270096Strasz#include <sys/time.h>
36270096Strasz#include <sys/ioctl.h>
37270096Strasz#include <sys/param.h>
38270096Strasz#include <sys/linker.h>
39270096Strasz#include <sys/mount.h>
40270096Strasz#include <sys/socket.h>
41270096Strasz#include <sys/stat.h>
42270096Strasz#include <sys/wait.h>
43270096Strasz#include <sys/utsname.h>
44270096Strasz#include <assert.h>
45270096Strasz#include <ctype.h>
46270096Strasz#include <errno.h>
47270096Strasz#include <fcntl.h>
48270096Strasz#include <libgen.h>
49270096Strasz#include <netdb.h>
50270096Strasz#include <signal.h>
51270096Strasz#include <stdbool.h>
52270096Strasz#include <stdint.h>
53270096Strasz#include <stdio.h>
54270096Strasz#include <stdlib.h>
55270096Strasz#include <string.h>
56270096Strasz#include <unistd.h>
57270096Strasz
58270096Strasz#include <libutil.h>
59270096Strasz
60270096Strasz#include "common.h"
61270096Strasz#include "mntopts.h"
62270096Strasz
63270096Straszstatic int
64270096Straszunmount_by_statfs(const struct statfs *sb, bool force)
65270096Strasz{
66270096Strasz	char *fsid_str;
67270096Strasz	int error, ret, flags;
68270096Strasz
69270096Strasz	ret = asprintf(&fsid_str, "FSID:%d:%d",
70270096Strasz	    sb->f_fsid.val[0], sb->f_fsid.val[1]);
71270096Strasz	if (ret < 0)
72270096Strasz		log_err(1, "asprintf");
73270096Strasz
74270096Strasz	log_debugx("unmounting %s using %s", sb->f_mntonname, fsid_str);
75270096Strasz
76270096Strasz	flags = MNT_BYFSID;
77270096Strasz	if (force)
78270096Strasz		flags |= MNT_FORCE;
79270096Strasz	error = unmount(fsid_str, flags);
80270096Strasz	free(fsid_str);
81270096Strasz	if (error != 0)
82270096Strasz		log_warn("cannot unmount %s", sb->f_mntonname);
83270096Strasz
84270096Strasz	return (error);
85270096Strasz}
86270096Strasz
87270096Straszstatic const struct statfs *
88270096Straszfind_statfs(const struct statfs *mntbuf, int nitems, const char *mountpoint)
89270096Strasz{
90270096Strasz	int i;
91270096Strasz
92270096Strasz	for (i = 0; i < nitems; i++) {
93270096Strasz		if (strcmp(mntbuf[i].f_mntonname, mountpoint) == 0)
94270096Strasz			return (mntbuf + i);
95270096Strasz	}
96270096Strasz
97270096Strasz	return (NULL);
98270096Strasz}
99270096Strasz
100270096Straszstatic void
101270096Straszmount_autofs(const char *from, const char *fspath, const char *options,
102270096Strasz    const char *prefix)
103270096Strasz{
104270096Strasz	struct iovec *iov = NULL;
105270096Strasz	char errmsg[255];
106270096Strasz	int error, iovlen = 0;
107270096Strasz
108270096Strasz	create_directory(fspath);
109270096Strasz
110270096Strasz	log_debugx("mounting %s on %s, prefix \"%s\", options \"%s\"",
111270096Strasz	    from, fspath, prefix, options);
112270096Strasz	memset(errmsg, 0, sizeof(errmsg));
113270096Strasz
114270096Strasz	build_iovec(&iov, &iovlen, "fstype",
115270096Strasz	    __DECONST(void *, "autofs"), (size_t)-1);
116270096Strasz	build_iovec(&iov, &iovlen, "fspath",
117270096Strasz	    __DECONST(void *, fspath), (size_t)-1);
118270096Strasz	build_iovec(&iov, &iovlen, "from",
119270096Strasz	    __DECONST(void *, from), (size_t)-1);
120270096Strasz	build_iovec(&iov, &iovlen, "errmsg",
121270096Strasz	    errmsg, sizeof(errmsg));
122270096Strasz
123270096Strasz	/*
124270096Strasz	 * Append the options and mountpoint defined in auto_master(5);
125270096Strasz	 * this way automountd(8) does not need to parse it.
126270096Strasz	 */
127270096Strasz	build_iovec(&iov, &iovlen, "master_options",
128270096Strasz	    __DECONST(void *, options), (size_t)-1);
129270096Strasz	build_iovec(&iov, &iovlen, "master_prefix",
130270096Strasz	    __DECONST(void *, prefix), (size_t)-1);
131270096Strasz
132270096Strasz	error = nmount(iov, iovlen, 0);
133270096Strasz	if (error != 0) {
134270096Strasz		if (*errmsg != '\0') {
135270096Strasz			log_err(1, "cannot mount %s on %s: %s",
136270096Strasz			    from, fspath, errmsg);
137270096Strasz		} else {
138270096Strasz			log_err(1, "cannot mount %s on %s", from, fspath);
139270096Strasz		}
140270096Strasz	}
141270096Strasz}
142270096Strasz
143270096Straszstatic void
144283235Straszmount_if_not_already(const struct node *n, const char *map, const char *options,
145283235Strasz    const char *prefix, const struct statfs *mntbuf, int nitems)
146270096Strasz{
147270096Strasz	const struct statfs *sb;
148270096Strasz	char *mountpoint;
149270096Strasz	char *from;
150270096Strasz	int ret;
151270096Strasz
152270096Strasz	ret = asprintf(&from, "map %s", map);
153270096Strasz	if (ret < 0)
154270096Strasz		log_err(1, "asprintf");
155270096Strasz
156270096Strasz	mountpoint = node_path(n);
157270096Strasz	sb = find_statfs(mntbuf, nitems, mountpoint);
158270096Strasz	if (sb != NULL) {
159270096Strasz		if (strcmp(sb->f_fstypename, "autofs") != 0) {
160270096Strasz			log_debugx("unknown filesystem mounted "
161270096Strasz			    "on %s; mounting", mountpoint);
162270096Strasz			/*
163270096Strasz			 * XXX: Compare options and 'from',
164270096Strasz			 *	and update the mount if necessary.
165270096Strasz			 */
166270096Strasz		} else {
167270096Strasz			log_debugx("autofs already mounted "
168270096Strasz			    "on %s", mountpoint);
169270096Strasz			free(from);
170270096Strasz			free(mountpoint);
171270096Strasz			return;
172270096Strasz		}
173270096Strasz	} else {
174270096Strasz		log_debugx("nothing mounted on %s; mounting",
175270096Strasz		    mountpoint);
176270096Strasz	}
177270096Strasz
178283235Strasz	mount_autofs(from, mountpoint, options, prefix);
179270096Strasz	free(from);
180270096Strasz	free(mountpoint);
181270096Strasz}
182270096Strasz
183270096Straszstatic void
184270096Straszmount_unmount(struct node *root)
185270096Strasz{
186270096Strasz	struct statfs *mntbuf;
187283235Strasz	struct node *n, *n2;
188270096Strasz	int i, nitems;
189270096Strasz
190270096Strasz	nitems = getmntinfo(&mntbuf, MNT_WAIT);
191270096Strasz	if (nitems <= 0)
192270096Strasz		log_err(1, "getmntinfo");
193270096Strasz
194270096Strasz	log_debugx("unmounting stale autofs mounts");
195270096Strasz
196270096Strasz	for (i = 0; i < nitems; i++) {
197270096Strasz		if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) {
198270096Strasz			log_debugx("skipping %s, filesystem type is not autofs",
199270096Strasz			    mntbuf[i].f_mntonname);
200270096Strasz			continue;
201270096Strasz		}
202270096Strasz
203270096Strasz		n = node_find(root, mntbuf[i].f_mntonname);
204270096Strasz		if (n != NULL) {
205270096Strasz			log_debugx("leaving autofs mounted on %s",
206270096Strasz			    mntbuf[i].f_mntonname);
207270096Strasz			continue;
208270096Strasz		}
209270096Strasz
210270096Strasz		log_debugx("autofs mounted on %s not found "
211270096Strasz		    "in new configuration; unmounting", mntbuf[i].f_mntonname);
212270096Strasz		unmount_by_statfs(&(mntbuf[i]), false);
213270096Strasz	}
214270096Strasz
215270096Strasz	log_debugx("mounting new autofs mounts");
216270096Strasz
217270096Strasz	TAILQ_FOREACH(n, &root->n_children, n_next) {
218270096Strasz		if (!node_is_direct_map(n)) {
219283235Strasz			mount_if_not_already(n, n->n_map, n->n_options,
220283235Strasz			    n->n_key, mntbuf, nitems);
221270096Strasz			continue;
222270096Strasz		}
223270096Strasz
224270096Strasz		TAILQ_FOREACH(n2, &n->n_children, n_next) {
225283235Strasz			mount_if_not_already(n2, n->n_map, n->n_options,
226283235Strasz			    "/", mntbuf, nitems);
227270096Strasz		}
228270096Strasz	}
229270096Strasz}
230270096Strasz
231270096Straszstatic void
232279742Straszflush_autofs(const char *fspath)
233279742Strasz{
234279742Strasz	struct iovec *iov = NULL;
235279742Strasz	char errmsg[255];
236279742Strasz	int error, iovlen = 0;
237279742Strasz
238279742Strasz	log_debugx("flushing %s", fspath);
239279742Strasz	memset(errmsg, 0, sizeof(errmsg));
240279742Strasz
241279742Strasz	build_iovec(&iov, &iovlen, "fstype",
242279742Strasz	    __DECONST(void *, "autofs"), (size_t)-1);
243279742Strasz	build_iovec(&iov, &iovlen, "fspath",
244279742Strasz	    __DECONST(void *, fspath), (size_t)-1);
245279742Strasz	build_iovec(&iov, &iovlen, "errmsg",
246279742Strasz	    errmsg, sizeof(errmsg));
247279742Strasz
248279742Strasz	error = nmount(iov, iovlen, MNT_UPDATE);
249279742Strasz	if (error != 0) {
250279742Strasz		if (*errmsg != '\0') {
251279742Strasz			log_err(1, "cannot flush %s: %s",
252279742Strasz			    fspath, errmsg);
253279742Strasz		} else {
254279742Strasz			log_err(1, "cannot flush %s", fspath);
255279742Strasz		}
256279742Strasz	}
257279742Strasz}
258279742Strasz
259279742Straszstatic void
260279742Straszflush_caches(void)
261279742Strasz{
262279742Strasz	struct statfs *mntbuf;
263279742Strasz	int i, nitems;
264279742Strasz
265279742Strasz	nitems = getmntinfo(&mntbuf, MNT_WAIT);
266279742Strasz	if (nitems <= 0)
267279742Strasz		log_err(1, "getmntinfo");
268279742Strasz
269279742Strasz	log_debugx("flushing autofs caches");
270279742Strasz
271279742Strasz	for (i = 0; i < nitems; i++) {
272279742Strasz		if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) {
273279742Strasz			log_debugx("skipping %s, filesystem type is not autofs",
274279742Strasz			    mntbuf[i].f_mntonname);
275279742Strasz			continue;
276279742Strasz		}
277279742Strasz
278279742Strasz		flush_autofs(mntbuf[i].f_mntonname);
279279742Strasz	}
280279742Strasz}
281279742Strasz
282279742Straszstatic void
283270096Straszunmount_automounted(bool force)
284270096Strasz{
285270096Strasz	struct statfs *mntbuf;
286270096Strasz	int i, nitems;
287270096Strasz
288270096Strasz	nitems = getmntinfo(&mntbuf, MNT_WAIT);
289270096Strasz	if (nitems <= 0)
290270096Strasz		log_err(1, "getmntinfo");
291270096Strasz
292270096Strasz	log_debugx("unmounting automounted filesystems");
293270096Strasz
294270096Strasz	for (i = 0; i < nitems; i++) {
295270096Strasz		if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) {
296270096Strasz			log_debugx("skipping %s, filesystem type is autofs",
297270096Strasz			    mntbuf[i].f_mntonname);
298270096Strasz			continue;
299270096Strasz		}
300270096Strasz
301270096Strasz		if ((mntbuf[i].f_flags & MNT_AUTOMOUNTED) == 0) {
302270096Strasz			log_debugx("skipping %s, not automounted",
303270096Strasz			    mntbuf[i].f_mntonname);
304270096Strasz			continue;
305270096Strasz		}
306270096Strasz
307270096Strasz		unmount_by_statfs(&(mntbuf[i]), force);
308270096Strasz	}
309270096Strasz}
310270096Strasz
311270096Straszstatic void
312270096Straszusage_automount(void)
313270096Strasz{
314270096Strasz
315279742Strasz	fprintf(stderr, "usage: automount [-D name=value][-o opts][-Lcfuv]\n");
316270096Strasz	exit(1);
317270096Strasz}
318270096Strasz
319270096Straszint
320270096Straszmain_automount(int argc, char **argv)
321270096Strasz{
322270096Strasz	struct node *root;
323270096Strasz	int ch, debug = 0, show_maps = 0;
324270096Strasz	char *options = NULL;
325279742Strasz	bool do_unmount = false, force_unmount = false, flush = false;
326270096Strasz
327270096Strasz	/*
328270096Strasz	 * Note that in automount(8), the only purpose of variable
329270096Strasz	 * handling is to aid in debugging maps (automount -L).
330270096Strasz	 */
331270096Strasz	defined_init();
332270096Strasz
333279742Strasz	while ((ch = getopt(argc, argv, "D:Lfco:uv")) != -1) {
334270096Strasz		switch (ch) {
335270096Strasz		case 'D':
336270096Strasz			defined_parse_and_add(optarg);
337270096Strasz			break;
338270096Strasz		case 'L':
339270096Strasz			show_maps++;
340270096Strasz			break;
341279742Strasz		case 'c':
342279742Strasz			flush = true;
343279742Strasz			break;
344270096Strasz		case 'f':
345270096Strasz			force_unmount = true;
346270096Strasz			break;
347270096Strasz		case 'o':
348283238Strasz			options = concat(options, ',', optarg);
349270096Strasz			break;
350270096Strasz		case 'u':
351270096Strasz			do_unmount = true;
352270096Strasz			break;
353270096Strasz		case 'v':
354270096Strasz			debug++;
355270096Strasz			break;
356270096Strasz		case '?':
357270096Strasz		default:
358270096Strasz			usage_automount();
359270096Strasz		}
360270096Strasz	}
361270096Strasz	argc -= optind;
362270096Strasz	if (argc != 0)
363270096Strasz		usage_automount();
364270096Strasz
365270096Strasz	if (force_unmount && !do_unmount)
366270096Strasz		usage_automount();
367270096Strasz
368270096Strasz	log_init(debug);
369270096Strasz
370279742Strasz	if (flush) {
371279742Strasz		flush_caches();
372279742Strasz		return (0);
373279742Strasz	}
374279742Strasz
375270096Strasz	if (do_unmount) {
376270096Strasz		unmount_automounted(force_unmount);
377270096Strasz		return (0);
378270096Strasz	}
379270096Strasz
380270096Strasz	root = node_new_root();
381270096Strasz	parse_master(root, AUTO_MASTER_PATH);
382270096Strasz
383270096Strasz	if (show_maps) {
384270096Strasz		if (show_maps > 1) {
385270096Strasz			node_expand_indirect_maps(root);
386270096Strasz			node_expand_ampersand(root, NULL);
387270096Strasz		}
388270096Strasz		node_expand_defined(root);
389283239Strasz		node_print(root, options);
390270096Strasz		return (0);
391270096Strasz	}
392270096Strasz
393270096Strasz	mount_unmount(root);
394270096Strasz
395270096Strasz	return (0);
396270096Strasz}
397