1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 /* 43 * nfs umount 44 */ 45 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <stdarg.h> 50 #include <signal.h> 51 #include <unistd.h> 52 #include <kstat.h> 53 #include <rpc/rpc.h> 54 #include <sys/mnttab.h> 55 #include <sys/mount.h> 56 #include <sys/mntent.h> 57 #include <nfs/nfs.h> 58 #include <nfs/nfs_clnt.h> 59 #include <rpcsvc/mount.h> 60 #include <errno.h> 61 #include <locale.h> 62 #include <fslib.h> 63 #include <priv.h> 64 #include "replica.h" 65 66 #define RET_OK 0 67 #define RET_ERR 32 68 69 #ifdef __STDC__ 70 static void pr_err(const char *fmt, ...); 71 #else 72 static void pr_err(char *fmt, va_dcl); 73 #endif 74 static void usage(); 75 static int nfs_unmount(char *, int); 76 static void inform_server(char *, char *, bool_t); 77 static struct extmnttab *mnttab_find(); 78 extern int __clnt_bindresvport(); 79 80 static int is_v4_mount(struct extmnttab *); 81 82 static char *myname; 83 static char typename[64]; 84 85 int 86 main(int argc, char *argv[]) 87 { 88 extern int optind; 89 int c; 90 int umnt_flag = 0; 91 92 (void) setlocale(LC_ALL, ""); 93 94 #if !defined(TEXT_DOMAIN) 95 #define TEXT_DOMAIN "SYS_TEST" 96 #endif 97 (void) textdomain(TEXT_DOMAIN); 98 99 myname = strrchr(argv[0], '/'); 100 myname = myname ? myname+1 : argv[0]; 101 (void) sprintf(typename, "nfs %s", myname); 102 argv[0] = typename; 103 104 /* 105 * Set options 106 */ 107 while ((c = getopt(argc, argv, "f")) != EOF) { 108 switch (c) { 109 case 'f': 110 umnt_flag |= MS_FORCE; /* forced unmount is desired */ 111 break; 112 default: 113 usage(); 114 exit(RET_ERR); 115 } 116 } 117 if (argc - optind != 1) { 118 usage(); 119 exit(RET_ERR); 120 } 121 122 if (!priv_ineffect(PRIV_SYS_MOUNT) || 123 !priv_ineffect(PRIV_NET_PRIVADDR)) { 124 pr_err(gettext("insufficient privileges\n")); 125 exit(RET_ERR); 126 } 127 128 /* 129 * exit, really 130 */ 131 return (nfs_unmount(argv[optind], umnt_flag)); 132 } 133 134 static void 135 pr_err(const char *fmt, ...) 136 { 137 va_list ap; 138 139 va_start(ap, fmt); 140 (void) fprintf(stderr, "%s: ", typename); 141 (void) vfprintf(stderr, fmt, ap); 142 (void) fflush(stderr); 143 va_end(ap); 144 } 145 146 static void 147 usage() 148 { 149 (void) fprintf(stderr, 150 gettext("Usage: nfs umount [-o opts] {server:path | dir}\n")); 151 exit(RET_ERR); 152 } 153 154 static int 155 nfs_unmount(char *pathname, int umnt_flag) 156 { 157 struct extmnttab *mntp; 158 bool_t quick = FALSE; 159 int is_v4 = FALSE; 160 161 mntp = mnttab_find(pathname); 162 if (mntp) { 163 pathname = mntp->mnt_mountp; 164 } 165 166 if (mntp) 167 is_v4 = is_v4_mount(mntp); 168 169 /* Forced unmount will almost always be successful */ 170 if (umount2(pathname, umnt_flag) < 0) { 171 if (errno == EBUSY) { 172 pr_err(gettext("%s: is busy\n"), pathname); 173 } else { 174 pr_err(gettext("%s: not mounted\n"), pathname); 175 } 176 return (RET_ERR); 177 } 178 179 /* All done if it's v4 */ 180 if (is_v4 == TRUE) 181 return (RET_OK); 182 183 /* Inform server quickly in case of forced unmount */ 184 if (umnt_flag & MS_FORCE) 185 quick = TRUE; 186 187 if (mntp) { 188 inform_server(mntp->mnt_special, mntp->mnt_mntopts, quick); 189 } 190 191 return (RET_OK); 192 } 193 194 /* 195 * Find the mnttab entry that corresponds to "name". 196 * We're not sure what the name represents: either 197 * a mountpoint name, or a special name (server:/path). 198 * Return the last entry in the file that matches. 199 */ 200 static struct extmnttab * 201 mnttab_find(dirname) 202 char *dirname; 203 { 204 FILE *fp; 205 struct extmnttab mnt; 206 struct extmnttab *res = NULL; 207 208 fp = fopen(MNTTAB, "r"); 209 if (fp == NULL) { 210 pr_err("%s: %s\n", MNTTAB, strerror(errno)); 211 return (NULL); 212 } 213 while (getextmntent(fp, &mnt, sizeof (struct extmnttab)) == 0) { 214 if (strcmp(mnt.mnt_mountp, dirname) == 0 || 215 strcmp(mnt.mnt_special, dirname) == 0) { 216 if (res) 217 fsfreemnttab(res); 218 res = fsdupmnttab(&mnt); 219 } 220 } 221 222 fclose(fp); 223 return (res); 224 } 225 226 /* 227 * If quick is TRUE, it will try to inform server quickly 228 * as possible. 229 */ 230 static void 231 inform_server(char *string, char *opts, bool_t quick) 232 { 233 struct timeval timeout; 234 CLIENT *cl; 235 enum clnt_stat rpc_stat; 236 struct replica *list; 237 int i, n; 238 char *p = NULL; 239 static struct timeval create_timeout = {5, 0}; 240 static struct timeval *timep; 241 242 list = parse_replica(string, &n); 243 244 if (list == NULL) { 245 if (n < 0) 246 pr_err(gettext("%s is not hostname:path format\n"), 247 string); 248 else 249 pr_err(gettext("no memory\n")); 250 return; 251 } 252 253 /* 254 * If mounted with -o public, then no need to contact server 255 * because mount protocol was not used. 256 */ 257 if (opts != NULL) 258 p = strstr(opts, MNTOPT_PUBLIC); 259 260 if (p != NULL) { 261 i = strlen(MNTOPT_PUBLIC); 262 /* 263 * Now make sure the match of "public" isn't a substring 264 * of another option. 265 */ 266 if (((p == opts) || (*(p-1) == ',')) && 267 ((p[i] == ',') || (p[i] == '\0'))) 268 return; 269 } 270 271 for (i = 0; i < n; i++) { 272 int vers; 273 274 /* 275 * Skip file systems mounted using WebNFS, because mount 276 * protocol was not used. 277 */ 278 if (strcmp(list[i].host, "nfs") == 0 && strncmp(list[i].path, 279 "//", 2) == 0) 280 continue; 281 282 vers = MOUNTVERS; 283 retry: 284 /* 285 * Use 5 sec. timeout if file system is forced unmounted, 286 * otherwise use default timeout to create a client handle. 287 * This would minimize the time to force unmount a file 288 * system reside on a server that is down. 289 */ 290 timep = (quick ? &create_timeout : NULL); 291 cl = clnt_create_timed(list[i].host, MOUNTPROG, vers, 292 "datagram_n", timep); 293 /* 294 * Do not print any error messages in case of forced 295 * unmount. 296 */ 297 if (cl == NULL) { 298 if (!quick) 299 pr_err("%s:%s %s\n", list[i].host, list[i].path, 300 clnt_spcreateerror( 301 "server not responding")); 302 continue; 303 } 304 /* 305 * Now it is most likely that the NFS client will be able 306 * to contact the server since rpcbind is running on 307 * the server. There is still a small window in which 308 * server can be unreachable. 309 */ 310 311 if (__clnt_bindresvport(cl) < 0) { 312 if (!quick) 313 pr_err(gettext( 314 "couldn't bind to reserved port\n")); 315 clnt_destroy(cl); 316 return; 317 } 318 if ((cl->cl_auth = authsys_create_default()) == NULL) { 319 if (!quick) 320 pr_err(gettext( 321 "couldn't create authsys structure\n")); 322 clnt_destroy(cl); 323 return; 324 } 325 timeout.tv_usec = 0; 326 timeout.tv_sec = 5; 327 clnt_control(cl, CLSET_RETRY_TIMEOUT, (char *)&timeout); 328 timeout.tv_sec = 25; 329 rpc_stat = clnt_call(cl, MOUNTPROC_UMNT, xdr_dirpath, 330 (char *)&list[i].path, xdr_void, (char *)NULL, 331 timeout); 332 AUTH_DESTROY(cl->cl_auth); 333 clnt_destroy(cl); 334 if (rpc_stat == RPC_PROGVERSMISMATCH && vers == MOUNTVERS) { 335 /* 336 * The rare case of a v3-only server 337 */ 338 vers = MOUNTVERS3; 339 goto retry; 340 } 341 if (rpc_stat != RPC_SUCCESS) 342 pr_err("%s\n", clnt_sperror(cl, "unmount")); 343 } 344 345 free_replica(list, n); 346 } 347 348 /* 349 * This function's behavior is taken from nfsstat. 350 * Trying to determine what NFS version was used for the mount. 351 */ 352 int 353 is_v4_mount(struct extmnttab *mntp) 354 { 355 kstat_ctl_t *kc = NULL; /* libkstat cookie */ 356 kstat_t *ksp; 357 ulong_t fsid; 358 struct mntinfo_kstat mik; 359 360 if (mntp == NULL) 361 return (FALSE); 362 363 if ((kc = kstat_open()) == NULL) 364 return (FALSE); 365 366 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 367 if (ksp->ks_type != KSTAT_TYPE_RAW) 368 continue; 369 if (strcmp(ksp->ks_module, "nfs") != 0) 370 continue; 371 if (strcmp(ksp->ks_name, "mntinfo") != 0) 372 continue; 373 if (mntp->mnt_minor != ksp->ks_instance) 374 continue; 375 376 if (kstat_read(kc, ksp, &mik) == -1) 377 continue; 378 379 if (mik.mik_vers == 4) 380 return (TRUE); 381 else 382 return (FALSE); 383 } 384 return (FALSE); 385 } 386