1 /* 2 * Copyright (c) 1999 Martin Blapp 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: src/usr.sbin/rpc.umntall/rpc.umntall.c,v 1.3.2.1 2001/12/13 01:27:20 iedowse Exp $ 27 * $DragonFly: src/usr.sbin/rpc.umntall/rpc.umntall.c,v 1.6 2005/04/02 20:49:56 joerg Exp $ 28 */ 29 30 #include <sys/param.h> 31 #include <sys/ucred.h> 32 #include <sys/mount.h> 33 34 #include <rpc/rpc.h> 35 #include <rpc/pmap_clnt.h> 36 #include <nfs/rpcv2.h> 37 38 #include <err.h> 39 #include <netdb.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 #include <resolv.h> 45 46 #include "mounttab.h" 47 48 int verbose; 49 int fastopt; 50 51 static int do_umount (char *, char *); 52 static int do_umntall (char *); 53 static int is_mounted (char *, char *); 54 static void usage (void); 55 static struct hostent *gethostbyname_quick(const char *name); 56 57 int xdr_dir (XDR *, char *); 58 59 int 60 main(int argc, char **argv) { 61 int ch, keep, success, pathlen; 62 time_t expire, now; 63 char *host, *path; 64 struct mtablist *mtab; 65 66 expire = 0; 67 host = path = NULL; 68 success = keep = verbose = 0; 69 while ((ch = getopt(argc, argv, "h:kp:vfe:")) != -1) 70 switch (ch) { 71 case 'h': 72 host = optarg; 73 break; 74 case 'e': 75 expire = atoi(optarg); 76 break; 77 case 'k': 78 keep = 1; 79 break; 80 case 'p': 81 path = optarg; 82 break; 83 case 'v': 84 verbose = 1; 85 break; 86 case 'f': 87 fastopt = 1; 88 break; 89 case '?': 90 usage(); 91 default: 92 break; 93 } 94 argc -= optind; 95 argv += optind; 96 97 /* Default expiretime is one day */ 98 if (expire == 0) 99 expire = 86400; 100 time(&now); 101 102 /* Read PATH_MOUNTTAB. */ 103 if (!read_mtab()) { 104 if (verbose) 105 warnx("no mounttab entries (%s does not exist)", 106 PATH_MOUNTTAB); 107 mtabhead = NULL; 108 } 109 110 if (host == NULL && path == NULL) { 111 /* Check each entry and do any necessary unmount RPCs. */ 112 for (mtab = mtabhead; mtab != NULL; mtab = mtab->mtab_next) { 113 if (*mtab->mtab_host == '\0') 114 continue; 115 if (mtab->mtab_time + expire < now) { 116 /* Clear expired entry. */ 117 if (verbose) 118 warnx("remove expired entry %s:%s", 119 mtab->mtab_host, mtab->mtab_dirp); 120 bzero(mtab->mtab_host, 121 sizeof(mtab->mtab_host)); 122 continue; 123 } 124 if (keep && is_mounted(mtab->mtab_host, 125 mtab->mtab_dirp)) { 126 if (verbose) 127 warnx("skip entry %s:%s", 128 mtab->mtab_host, mtab->mtab_dirp); 129 continue; 130 } 131 if (do_umount(mtab->mtab_host, mtab->mtab_dirp)) { 132 if (verbose) 133 warnx("umount RPC for %s:%s succeeded", 134 mtab->mtab_host, mtab->mtab_dirp); 135 /* Remove all entries for this host + path. */ 136 clean_mtab(mtab->mtab_host, mtab->mtab_dirp, 137 verbose); 138 } 139 } 140 success = 1; 141 } else { 142 if (host == NULL && path != NULL) 143 /* Missing hostname. */ 144 usage(); 145 if (path == NULL) { 146 /* Do a RPC UMNTALL for this specific host */ 147 success = do_umntall(host); 148 if (verbose && success) 149 warnx("umntall RPC for %s succeeded", host); 150 } else { 151 /* Do a RPC UMNTALL for this specific mount */ 152 for (pathlen = strlen(path); 153 pathlen > 1 && path[pathlen - 1] == '/'; pathlen--) 154 path[pathlen - 1] = '\0'; 155 success = do_umount(host, path); 156 if (verbose && success) 157 warnx("umount RPC for %s:%s succeeded", host, 158 path); 159 } 160 /* If successful, remove any corresponding mounttab entries. */ 161 if (success) 162 clean_mtab(host, path, verbose); 163 } 164 /* Write and unlink PATH_MOUNTTAB if necessary */ 165 if (success) 166 success = write_mtab(verbose); 167 free_mtab(); 168 exit (success ? 0 : 1); 169 } 170 171 /* 172 * Send a RPC_MNT UMNTALL request to hostname. 173 * XXX This works for all mountd implementations, 174 * but produces a RPC IOERR on non FreeBSD systems. 175 */ 176 int 177 do_umntall(char *hostname) { 178 enum clnt_stat clnt_stat; 179 struct hostent *hp; 180 struct sockaddr_in saddr; 181 struct timeval pertry, try; 182 int so; 183 CLIENT *clp; 184 185 if ((hp = gethostbyname_quick(hostname)) == NULL) { 186 warnx("gethostbyname(%s) failed", hostname); 187 return (0); 188 } 189 memset(&saddr, 0, sizeof(saddr)); 190 saddr.sin_family = AF_INET; 191 saddr.sin_port = 0; 192 memmove(&saddr.sin_addr, hp->h_addr, MIN(hp->h_length, 193 (int)sizeof(saddr.sin_addr))); 194 pertry.tv_sec = 3; 195 pertry.tv_usec = 0; 196 if (fastopt) 197 pmap_getport_timeout(NULL, &pertry); 198 so = RPC_ANYSOCK; 199 clp = clntudp_create(&saddr, RPCPROG_MNT, RPCMNT_VER1, pertry, &so); 200 if (clp == NULL) { 201 warnx("%s: %s", hostname, clnt_spcreateerror("RPCPROG_MNT")); 202 return (0); 203 } 204 clp->cl_auth = authunix_create_default(); 205 try.tv_sec = 3; 206 try.tv_usec = 0; 207 clnt_stat = clnt_call(clp, RPCMNT_UMNTALL, xdr_void, (caddr_t)0, 208 xdr_void, (caddr_t)0, try); 209 if (clnt_stat != RPC_SUCCESS) 210 warnx("%s: %s", hostname, clnt_sperror(clp, "RPCMNT_UMNTALL")); 211 auth_destroy(clp->cl_auth); 212 clnt_destroy(clp); 213 return (clnt_stat == RPC_SUCCESS); 214 } 215 216 /* 217 * Send a RPC_MNT UMOUNT request for dirp to hostname. 218 */ 219 int 220 do_umount(char *hostname, char *dirp) { 221 enum clnt_stat clnt_stat; 222 struct hostent *hp; 223 struct sockaddr_in saddr; 224 struct timeval pertry, try; 225 CLIENT *clp; 226 int so; 227 228 if ((hp = gethostbyname_quick(hostname)) == NULL) { 229 warnx("gethostbyname(%s) failed", hostname); 230 return (0); 231 } 232 memset(&saddr, 0, sizeof(saddr)); 233 saddr.sin_family = AF_INET; 234 saddr.sin_port = 0; 235 memmove(&saddr.sin_addr, hp->h_addr, MIN(hp->h_length, 236 (int)sizeof(saddr.sin_addr))); 237 pertry.tv_sec = 3; 238 pertry.tv_usec = 0; 239 if (fastopt) 240 pmap_getport_timeout(NULL, &pertry); 241 so = RPC_ANYSOCK; 242 clp = clntudp_create(&saddr, RPCPROG_MNT, RPCMNT_VER1, pertry, &so); 243 if (clp == NULL) { 244 warnx("%s: %s", hostname, clnt_spcreateerror("RPCPROG_MNT")); 245 return (0); 246 } 247 clp->cl_auth = authunix_create_default(); 248 try.tv_sec = 3; 249 try.tv_usec = 0; 250 clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, xdr_dir, dirp, 251 xdr_void, (caddr_t)0, try); 252 if (clnt_stat != RPC_SUCCESS) 253 warnx("%s: %s", hostname, clnt_sperror(clp, "RPCMNT_UMOUNT")); 254 auth_destroy(clp->cl_auth); 255 clnt_destroy(clp); 256 return (clnt_stat == RPC_SUCCESS); 257 } 258 259 /* 260 * Check if the entry is still/already mounted. 261 */ 262 int 263 is_mounted(char *hostname, char *dirp) { 264 struct statfs *mntbuf; 265 char name[MNAMELEN + 1]; 266 size_t bufsize; 267 int mntsize, i; 268 269 if (strlen(hostname) + strlen(dirp) >= MNAMELEN) 270 return (0); 271 snprintf(name, sizeof(name), "%s:%s", hostname, dirp); 272 mntsize = getfsstat(NULL, 0, MNT_NOWAIT); 273 if (mntsize <= 0) 274 return (0); 275 bufsize = (mntsize + 1) * sizeof(struct statfs); 276 if ((mntbuf = malloc(bufsize)) == NULL) 277 err(1, "malloc"); 278 mntsize = getfsstat(mntbuf, (long)bufsize, MNT_NOWAIT); 279 for (i = mntsize - 1; i >= 0; i--) { 280 if (strcmp(mntbuf[i].f_mntfromname, name) == 0) { 281 free(mntbuf); 282 return (1); 283 } 284 } 285 free(mntbuf); 286 return (0); 287 } 288 289 /* 290 * rpc.umntall is often called at boot. If the network or target host has 291 * issues we want to limit resolver stalls so rpc.umntall doesn't stall 292 * the boot sequence forever. 293 */ 294 struct hostent * 295 gethostbyname_quick(const char *name) 296 { 297 struct hostent *he; 298 int save_retrans; 299 int save_retry; 300 301 save_retrans = _res.retrans; 302 save_retry = _res.retry; 303 if (fastopt) { 304 _res.retrans = 1; 305 _res.retry = 2; 306 } 307 he = gethostbyname(name); 308 if (fastopt) { 309 _res.retrans = save_retrans; 310 _res.retry = save_retry; 311 } 312 return(he); 313 } 314 315 /* 316 * xdr routines for mount rpc's 317 */ 318 int 319 xdr_dir(XDR *xdrsp, char *dirp) { 320 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 321 } 322 323 static void 324 usage() { 325 fprintf(stderr, "%s\n", 326 "usage: rpc.umntall [-kv] [-e expire] [-h host] [-p path]"); 327 exit(1); 328 } 329