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 144270096Straszmount_if_not_already(const struct node *n, const char *map, 145270096Strasz 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 178270096Strasz mount_autofs(from, mountpoint, n->n_options, n->n_key); 179270096Strasz free(from); 180270096Strasz free(mountpoint); 181270096Strasz} 182270096Strasz 183270096Straszstatic void 184270096Straszmount_unmount(struct node *root) 185270096Strasz{ 186270096Strasz struct statfs *mntbuf; 187270096Strasz struct node *n, *n2, *n3; 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)) { 219270096Strasz mount_if_not_already(n, n->n_map, mntbuf, nitems); 220270096Strasz continue; 221270096Strasz } 222270096Strasz 223270096Strasz TAILQ_FOREACH(n2, &n->n_children, n_next) { 224270096Strasz TAILQ_FOREACH(n3, &n2->n_children, n_next) { 225270096Strasz mount_if_not_already(n3, n->n_map, 226270096Strasz mntbuf, nitems); 227270096Strasz } 228270096Strasz } 229270096Strasz } 230270096Strasz} 231270096Strasz 232270096Straszstatic void 233270096Straszunmount_automounted(bool force) 234270096Strasz{ 235270096Strasz struct statfs *mntbuf; 236270096Strasz int i, nitems; 237270096Strasz 238270096Strasz nitems = getmntinfo(&mntbuf, MNT_WAIT); 239270096Strasz if (nitems <= 0) 240270096Strasz log_err(1, "getmntinfo"); 241270096Strasz 242270096Strasz log_debugx("unmounting automounted filesystems"); 243270096Strasz 244270096Strasz for (i = 0; i < nitems; i++) { 245270096Strasz if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) { 246270096Strasz log_debugx("skipping %s, filesystem type is autofs", 247270096Strasz mntbuf[i].f_mntonname); 248270096Strasz continue; 249270096Strasz } 250270096Strasz 251270096Strasz if ((mntbuf[i].f_flags & MNT_AUTOMOUNTED) == 0) { 252270096Strasz log_debugx("skipping %s, not automounted", 253270096Strasz mntbuf[i].f_mntonname); 254270096Strasz continue; 255270096Strasz } 256270096Strasz 257270096Strasz unmount_by_statfs(&(mntbuf[i]), force); 258270096Strasz } 259270096Strasz} 260270096Strasz 261270096Straszstatic void 262270096Straszusage_automount(void) 263270096Strasz{ 264270096Strasz 265270096Strasz fprintf(stderr, "usage: automount [-D name=value][-o opts][-Lfuv]\n"); 266270096Strasz exit(1); 267270096Strasz} 268270096Strasz 269270096Straszint 270270096Straszmain_automount(int argc, char **argv) 271270096Strasz{ 272270096Strasz struct node *root; 273270096Strasz int ch, debug = 0, show_maps = 0; 274270096Strasz char *options = NULL; 275270096Strasz bool do_unmount = false, force_unmount = false; 276270096Strasz 277270096Strasz /* 278270096Strasz * Note that in automount(8), the only purpose of variable 279270096Strasz * handling is to aid in debugging maps (automount -L). 280270096Strasz */ 281270096Strasz defined_init(); 282270096Strasz 283270096Strasz while ((ch = getopt(argc, argv, "D:Lfo:uv")) != -1) { 284270096Strasz switch (ch) { 285270096Strasz case 'D': 286270096Strasz defined_parse_and_add(optarg); 287270096Strasz break; 288270096Strasz case 'L': 289270096Strasz show_maps++; 290270096Strasz break; 291270096Strasz case 'f': 292270096Strasz force_unmount = true; 293270096Strasz break; 294270096Strasz case 'o': 295270096Strasz if (options == NULL) { 296270096Strasz options = checked_strdup(optarg); 297270096Strasz } else { 298270096Strasz options = 299270096Strasz separated_concat(options, optarg, ','); 300270096Strasz } 301270096Strasz break; 302270096Strasz case 'u': 303270096Strasz do_unmount = true; 304270096Strasz break; 305270096Strasz case 'v': 306270096Strasz debug++; 307270096Strasz break; 308270096Strasz case '?': 309270096Strasz default: 310270096Strasz usage_automount(); 311270096Strasz } 312270096Strasz } 313270096Strasz argc -= optind; 314270096Strasz if (argc != 0) 315270096Strasz usage_automount(); 316270096Strasz 317270096Strasz if (force_unmount && !do_unmount) 318270096Strasz usage_automount(); 319270096Strasz 320270096Strasz log_init(debug); 321270096Strasz 322270096Strasz if (do_unmount) { 323270096Strasz unmount_automounted(force_unmount); 324270096Strasz return (0); 325270096Strasz } 326270096Strasz 327270096Strasz root = node_new_root(); 328270096Strasz parse_master(root, AUTO_MASTER_PATH); 329270096Strasz 330270096Strasz if (show_maps) { 331270096Strasz if (options != NULL) { 332270096Strasz root->n_options = separated_concat(options, 333270096Strasz root->n_options, ','); 334270096Strasz } 335270096Strasz if (show_maps > 1) { 336270096Strasz node_expand_indirect_maps(root); 337270096Strasz node_expand_ampersand(root, NULL); 338270096Strasz } 339270096Strasz node_expand_defined(root); 340270096Strasz node_print(root); 341270096Strasz return (0); 342270096Strasz } 343270096Strasz 344270096Strasz mount_unmount(root); 345270096Strasz 346270096Strasz return (0); 347270096Strasz} 348