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