1 /*- 2 * Copyright (c) 2016 The DragonFly Project 3 * Copyright (c) 2014 The FreeBSD Foundation 4 * All rights reserved. 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/types.h> 33 #include <sys/mount.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <vfs/autofs/autofs_mount.h> 39 40 #include "common.h" 41 42 static int 43 unmount_by_statfs(const struct statfs *sb, bool force) 44 { 45 int error, flags; 46 47 log_debugx("unmounting %s", sb->f_mntonname); 48 49 flags = 0; 50 if (force) 51 flags |= MNT_FORCE; 52 error = unmount(sb->f_mntonname, flags); 53 if (error != 0) 54 log_warn("cannot unmount %s", sb->f_mntonname); 55 56 return (error); 57 } 58 59 static const struct statfs * 60 find_statfs(const struct statfs *mntbuf, int nitems, const char *mountpoint) 61 { 62 int i; 63 64 for (i = 0; i < nitems; i++) { 65 if (strcmp(mntbuf[i].f_mntonname, mountpoint) == 0) 66 return (mntbuf + i); 67 } 68 69 return (NULL); 70 } 71 72 static void 73 mount_autofs(const char *from, const char *fspath, const char *options, 74 const char *prefix) 75 { 76 struct autofs_mount_info info; 77 int error; 78 79 create_directory(fspath); 80 81 log_debugx("mounting %s on %s, prefix \"%s\", options \"%s\"", 82 from, fspath, prefix, options); 83 84 memset(&info, 0, sizeof(info)); 85 info.from = from; 86 info.master_options = options; 87 info.master_prefix = prefix; 88 89 error = mount("autofs", fspath, 0, &info); 90 if (error != 0) 91 log_err(1, "cannot mount %s on %s", from, fspath); 92 } 93 94 static void 95 mount_if_not_already(const struct node *n, const char *map, const char *options, 96 const char *prefix, const struct statfs *mntbuf, int nitems) 97 { 98 const struct statfs *sb; 99 char *mountpoint; 100 char *from; 101 int ret; 102 103 ret = asprintf(&from, "map %s", map); 104 if (ret < 0) 105 log_err(1, "asprintf"); 106 107 mountpoint = node_path(n); 108 sb = find_statfs(mntbuf, nitems, mountpoint); 109 if (sb != NULL) { 110 if (strcmp(sb->f_fstypename, "autofs") != 0) { 111 log_debugx("unknown filesystem mounted " 112 "on %s; mounting", mountpoint); 113 /* 114 * XXX: Compare options and 'from', 115 * and update the mount if necessary. 116 */ 117 } else { 118 log_debugx("autofs already mounted " 119 "on %s", mountpoint); 120 free(from); 121 free(mountpoint); 122 return; 123 } 124 } else { 125 log_debugx("nothing mounted on %s; mounting", 126 mountpoint); 127 } 128 129 mount_autofs(from, mountpoint, options, prefix); 130 free(from); 131 free(mountpoint); 132 } 133 134 static void 135 mount_unmount(struct node *root) 136 { 137 struct statfs *mntbuf; 138 struct node *n, *n2; 139 int i, nitems; 140 141 nitems = getmntinfo(&mntbuf, MNT_WAIT); 142 if (nitems <= 0) 143 log_err(1, "getmntinfo"); 144 145 log_debugx("unmounting stale autofs mounts"); 146 147 for (i = 0; i < nitems; i++) { 148 if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) { 149 log_debugx("skipping %s, filesystem type is not autofs", 150 mntbuf[i].f_mntonname); 151 continue; 152 } 153 154 n = node_find(root, mntbuf[i].f_mntonname); 155 if (n != NULL) { 156 log_debugx("leaving autofs mounted on %s", 157 mntbuf[i].f_mntonname); 158 continue; 159 } 160 161 log_debugx("autofs mounted on %s not found " 162 "in new configuration; unmounting", mntbuf[i].f_mntonname); 163 unmount_by_statfs(&(mntbuf[i]), false); 164 } 165 166 log_debugx("mounting new autofs mounts"); 167 168 TAILQ_FOREACH(n, &root->n_children, n_next) { 169 if (!node_is_direct_map(n)) { 170 mount_if_not_already(n, n->n_map, n->n_options, 171 n->n_key, mntbuf, nitems); 172 continue; 173 } 174 175 TAILQ_FOREACH(n2, &n->n_children, n_next) { 176 mount_if_not_already(n2, n->n_map, n->n_options, 177 "/", mntbuf, nitems); 178 } 179 } 180 } 181 182 static void 183 flush_autofs(const char *fspath) 184 { 185 int error; 186 187 log_debugx("flushing %s", fspath); 188 189 error = mount("autofs", fspath, MNT_UPDATE, NULL); 190 if (error != 0) 191 log_err(1, "cannot flush %s", fspath); 192 } 193 194 static void 195 flush_caches(void) 196 { 197 struct statfs *mntbuf; 198 int i, nitems; 199 200 nitems = getmntinfo(&mntbuf, MNT_WAIT); 201 if (nitems <= 0) 202 log_err(1, "getmntinfo"); 203 204 log_debugx("flushing autofs caches"); 205 206 for (i = 0; i < nitems; i++) { 207 if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) { 208 log_debugx("skipping %s, filesystem type is not autofs", 209 mntbuf[i].f_mntonname); 210 continue; 211 } 212 213 flush_autofs(mntbuf[i].f_mntonname); 214 } 215 } 216 217 static void 218 unmount_automounted(bool force) 219 { 220 struct statfs *mntbuf; 221 int i, nitems; 222 223 nitems = getmntinfo(&mntbuf, MNT_WAIT); 224 if (nitems <= 0) 225 log_err(1, "getmntinfo"); 226 227 log_debugx("unmounting automounted filesystems"); 228 229 for (i = 0; i < nitems; i++) { 230 if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) { 231 log_debugx("skipping %s, filesystem type is autofs", 232 mntbuf[i].f_mntonname); 233 continue; 234 } 235 236 if ((mntbuf[i].f_flags & MNT_AUTOMOUNTED) == 0) { 237 log_debugx("skipping %s, not automounted", 238 mntbuf[i].f_mntonname); 239 continue; 240 } 241 242 unmount_by_statfs(&(mntbuf[i]), force); 243 } 244 } 245 246 static void 247 usage_automount(void) 248 { 249 250 fprintf(stderr, "usage: automount [-D name=value][-o opts][-Lcfuv]\n"); 251 exit(1); 252 } 253 254 int 255 main_automount(int argc, char **argv) 256 { 257 struct node *root; 258 int ch, debug = 0, show_maps = 0; 259 char *options = NULL; 260 bool do_unmount = false, force_unmount = false, flush = false; 261 262 /* 263 * Note that in automount(8), the only purpose of variable 264 * handling is to aid in debugging maps (automount -L). 265 */ 266 defined_init(); 267 268 while ((ch = getopt(argc, argv, "D:Lfco:uv")) != -1) { 269 switch (ch) { 270 case 'D': 271 defined_parse_and_add(optarg); 272 break; 273 case 'L': 274 show_maps++; 275 break; 276 case 'c': 277 flush = true; 278 break; 279 case 'f': 280 force_unmount = true; 281 break; 282 case 'o': 283 options = concat(options, ',', optarg); 284 break; 285 case 'u': 286 do_unmount = true; 287 break; 288 case 'v': 289 debug++; 290 break; 291 case '?': 292 default: 293 usage_automount(); 294 } 295 } 296 argc -= optind; 297 if (argc != 0) 298 usage_automount(); 299 300 if (force_unmount && !do_unmount) 301 usage_automount(); 302 303 log_init(debug); 304 305 if (flush) { 306 flush_caches(); 307 return (0); 308 } 309 310 if (do_unmount) { 311 unmount_automounted(force_unmount); 312 return (0); 313 } 314 315 root = node_new_root(); 316 parse_master(root, AUTO_MASTER_PATH); 317 318 if (show_maps) { 319 if (show_maps > 1) { 320 node_expand_indirect_maps(root); 321 node_expand_ampersand(root, NULL); 322 } 323 node_expand_defined(root); 324 node_print(root, options); 325 return (0); 326 } 327 328 mount_unmount(root); 329 330 return (0); 331 } 332