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.5 2004/12/18 22:48:13 swildner 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 <nfs/rpcv2.h> 36 37 #include <err.h> 38 #include <netdb.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <unistd.h> 43 #include <resolv.h> 44 45 #include "mounttab.h" 46 47 int verbose; 48 int fastopt; 49 50 static int do_umount (char *, char *); 51 static int do_umntall (char *); 52 static int is_mounted (char *, char *); 53 static void usage (void); 54 static struct hostent *gethostbyname_quick(const char *name); 55 56 int xdr_dir (XDR *, char *); 57 58 int 59 main(int argc, char **argv) { 60 int ch, keep, success, pathlen; 61 time_t expire, now; 62 char *host, *path; 63 struct mtablist *mtab; 64 65 expire = 0; 66 host = path = NULL; 67 success = keep = verbose = 0; 68 while ((ch = getopt(argc, argv, "h:kp:vfe:")) != -1) 69 switch (ch) { 70 case 'h': 71 host = optarg; 72 break; 73 case 'e': 74 expire = atoi(optarg); 75 break; 76 case 'k': 77 keep = 1; 78 break; 79 case 'p': 80 path = optarg; 81 break; 82 case 'v': 83 verbose = 1; 84 break; 85 case 'f': 86 fastopt = 1; 87 break; 88 case '?': 89 usage(); 90 default: 91 break; 92 } 93 argc -= optind; 94 argv += optind; 95 96 /* Default expiretime is one day */ 97 if (expire == 0) 98 expire = 86400; 99 time(&now); 100 101 /* Read PATH_MOUNTTAB. */ 102 if (!read_mtab()) { 103 if (verbose) 104 warnx("no mounttab entries (%s does not exist)", 105 PATH_MOUNTTAB); 106 mtabhead = NULL; 107 } 108 109 if (host == NULL && path == NULL) { 110 /* Check each entry and do any necessary unmount RPCs. */ 111 for (mtab = mtabhead; mtab != NULL; mtab = mtab->mtab_next) { 112 if (*mtab->mtab_host == '\0') 113 continue; 114 if (mtab->mtab_time + expire < now) { 115 /* Clear expired entry. */ 116 if (verbose) 117 warnx("remove expired entry %s:%s", 118 mtab->mtab_host, mtab->mtab_dirp); 119 bzero(mtab->mtab_host, 120 sizeof(mtab->mtab_host)); 121 continue; 122 } 123 if (keep && is_mounted(mtab->mtab_host, 124 mtab->mtab_dirp)) { 125 if (verbose) 126 warnx("skip entry %s:%s", 127 mtab->mtab_host, mtab->mtab_dirp); 128 continue; 129 } 130 if (do_umount(mtab->mtab_host, mtab->mtab_dirp)) { 131 if (verbose) 132 warnx("umount RPC for %s:%s succeeded", 133 mtab->mtab_host, mtab->mtab_dirp); 134 /* Remove all entries for this host + path. */ 135 clean_mtab(mtab->mtab_host, mtab->mtab_dirp, 136 verbose); 137 } 138 } 139 success = 1; 140 } else { 141 if (host == NULL && path != NULL) 142 /* Missing hostname. */ 143 usage(); 144 if (path == NULL) { 145 /* Do a RPC UMNTALL for this specific host */ 146 success = do_umntall(host); 147 if (verbose && success) 148 warnx("umntall RPC for %s succeeded", host); 149 } else { 150 /* Do a RPC UMNTALL for this specific mount */ 151 for (pathlen = strlen(path); 152 pathlen > 1 && path[pathlen - 1] == '/'; pathlen--) 153 path[pathlen - 1] = '\0'; 154 success = do_umount(host, path); 155 if (verbose && success) 156 warnx("umount RPC for %s:%s succeeded", host, 157 path); 158 } 159 /* If successful, remove any corresponding mounttab entries. */ 160 if (success) 161 clean_mtab(host, path, verbose); 162 } 163 /* Write and unlink PATH_MOUNTTAB if necessary */ 164 if (success) 165 success = write_mtab(verbose); 166 free_mtab(); 167 exit (success ? 0 : 1); 168 } 169 170 /* 171 * Send a RPC_MNT UMNTALL request to hostname. 172 * XXX This works for all mountd implementations, 173 * but produces a RPC IOERR on non FreeBSD systems. 174 */ 175 int 176 do_umntall(char *hostname) { 177 enum clnt_stat clnt_stat; 178 struct hostent *hp; 179 struct sockaddr_in saddr; 180 struct timeval pertry, try; 181 int so; 182 CLIENT *clp; 183 184 if ((hp = gethostbyname_quick(hostname)) == NULL) { 185 warnx("gethostbyname(%s) failed", hostname); 186 return (0); 187 } 188 memset(&saddr, 0, sizeof(saddr)); 189 saddr.sin_family = AF_INET; 190 saddr.sin_port = 0; 191 memmove(&saddr.sin_addr, hp->h_addr, MIN(hp->h_length, 192 sizeof(saddr.sin_addr))); 193 pertry.tv_sec = 3; 194 pertry.tv_usec = 0; 195 if (fastopt) 196 pmap_getport_timeout(NULL, &pertry); 197 so = RPC_ANYSOCK; 198 clp = clntudp_create(&saddr, RPCPROG_MNT, RPCMNT_VER1, pertry, &so); 199 if (clp == NULL) { 200 warnx("%s: %s", hostname, clnt_spcreateerror("RPCPROG_MNT")); 201 return (0); 202 } 203 clp->cl_auth = authunix_create_default(); 204 try.tv_sec = 3; 205 try.tv_usec = 0; 206 clnt_stat = clnt_call(clp, RPCMNT_UMNTALL, xdr_void, (caddr_t)0, 207 xdr_void, (caddr_t)0, try); 208 if (clnt_stat != RPC_SUCCESS) 209 warnx("%s: %s", hostname, clnt_sperror(clp, "RPCMNT_UMNTALL")); 210 auth_destroy(clp->cl_auth); 211 clnt_destroy(clp); 212 return (clnt_stat == RPC_SUCCESS); 213 } 214 215 /* 216 * Send a RPC_MNT UMOUNT request for dirp to hostname. 217 */ 218 int 219 do_umount(char *hostname, char *dirp) { 220 enum clnt_stat clnt_stat; 221 struct hostent *hp; 222 struct sockaddr_in saddr; 223 struct timeval pertry, try; 224 CLIENT *clp; 225 int so; 226 227 if ((hp = gethostbyname_quick(hostname)) == NULL) { 228 warnx("gethostbyname(%s) failed", hostname); 229 return (0); 230 } 231 memset(&saddr, 0, sizeof(saddr)); 232 saddr.sin_family = AF_INET; 233 saddr.sin_port = 0; 234 memmove(&saddr.sin_addr, hp->h_addr, MIN(hp->h_length, 235 sizeof(saddr.sin_addr))); 236 pertry.tv_sec = 3; 237 pertry.tv_usec = 0; 238 if (fastopt) 239 pmap_getport_timeout(NULL, &pertry); 240 so = RPC_ANYSOCK; 241 clp = clntudp_create(&saddr, RPCPROG_MNT, RPCMNT_VER1, pertry, &so); 242 if (clp == NULL) { 243 warnx("%s: %s", hostname, clnt_spcreateerror("RPCPROG_MNT")); 244 return (0); 245 } 246 clp->cl_auth = authunix_create_default(); 247 try.tv_sec = 3; 248 try.tv_usec = 0; 249 clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, xdr_dir, dirp, 250 xdr_void, (caddr_t)0, try); 251 if (clnt_stat != RPC_SUCCESS) 252 warnx("%s: %s", hostname, clnt_sperror(clp, "RPCMNT_UMOUNT")); 253 auth_destroy(clp->cl_auth); 254 clnt_destroy(clp); 255 return (clnt_stat == RPC_SUCCESS); 256 } 257 258 /* 259 * Check if the entry is still/already mounted. 260 */ 261 int 262 is_mounted(char *hostname, char *dirp) { 263 struct statfs *mntbuf; 264 char name[MNAMELEN + 1]; 265 size_t bufsize; 266 int mntsize, i; 267 268 if (strlen(hostname) + strlen(dirp) >= MNAMELEN) 269 return (0); 270 snprintf(name, sizeof(name), "%s:%s", hostname, dirp); 271 mntsize = getfsstat(NULL, 0, MNT_NOWAIT); 272 if (mntsize <= 0) 273 return (0); 274 bufsize = (mntsize + 1) * sizeof(struct statfs); 275 if ((mntbuf = malloc(bufsize)) == NULL) 276 err(1, "malloc"); 277 mntsize = getfsstat(mntbuf, (long)bufsize, MNT_NOWAIT); 278 for (i = mntsize - 1; i >= 0; i--) { 279 if (strcmp(mntbuf[i].f_mntfromname, name) == 0) { 280 free(mntbuf); 281 return (1); 282 } 283 } 284 free(mntbuf); 285 return (0); 286 } 287 288 /* 289 * rpc.umntall is often called at boot. If the network or target host has 290 * issues we want to limit resolver stalls so rpc.umntall doesn't stall 291 * the boot sequence forever. 292 */ 293 struct hostent * 294 gethostbyname_quick(const char *name) 295 { 296 struct hostent *he; 297 int save_retrans; 298 int save_retry; 299 300 save_retrans = _res.retrans; 301 save_retry = _res.retry; 302 if (fastopt) { 303 _res.retrans = 1; 304 _res.retry = 2; 305 } 306 he = gethostbyname(name); 307 if (fastopt) { 308 _res.retrans = save_retrans; 309 _res.retry = save_retry; 310 } 311 return(he); 312 } 313 314 /* 315 * xdr routines for mount rpc's 316 */ 317 int 318 xdr_dir(XDR *xdrsp, char *dirp) { 319 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 320 } 321 322 static void 323 usage() { 324 fprintf(stderr, "%s\n", 325 "usage: rpc.umntall [-kv] [-e expire] [-h host] [-p path]"); 326 exit(1); 327 } 328