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 } 398