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