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