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 2004 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 main(argc, argv) 86 int argc; 87 char **argv; 88 { 89 extern int optind; 90 int c; 91 int umnt_flag = 0; 92 93 (void) setlocale(LC_ALL, ""); 94 95 #if !defined(TEXT_DOMAIN) 96 #define TEXT_DOMAIN "SYS_TEST" 97 #endif 98 (void) textdomain(TEXT_DOMAIN); 99 100 myname = strrchr(argv[0], '/'); 101 myname = myname ? myname+1 : argv[0]; 102 (void) sprintf(typename, "nfs %s", myname); 103 argv[0] = typename; 104 105 /* 106 * Set options 107 */ 108 while ((c = getopt(argc, argv, "f")) != EOF) { 109 switch (c) { 110 case 'f': 111 umnt_flag |= MS_FORCE; /* forced unmount is desired */ 112 break; 113 default: 114 usage(); 115 exit(RET_ERR); 116 } 117 } 118 if (argc - optind != 1) { 119 usage(); 120 exit(RET_ERR); 121 } 122 123 if (!priv_ineffect(PRIV_SYS_MOUNT) || 124 !priv_ineffect(PRIV_NET_PRIVADDR)) { 125 pr_err(gettext("insufficient privileges\n")); 126 exit(RET_ERR); 127 } 128 129 /* 130 * exit, really 131 */ 132 return (nfs_unmount(argv[optind], umnt_flag)); 133 } 134 135 static void 136 pr_err(const char *fmt, ...) 137 { 138 va_list ap; 139 140 va_start(ap, fmt); 141 (void) fprintf(stderr, "%s: ", typename); 142 (void) vfprintf(stderr, fmt, ap); 143 (void) fflush(stderr); 144 va_end(ap); 145 } 146 147 static void 148 usage() 149 { 150 (void) fprintf(stderr, 151 gettext("Usage: nfs umount [-o opts] {server:path | dir}\n")); 152 exit(RET_ERR); 153 } 154 155 static int 156 nfs_unmount(char *pathname, int umnt_flag) 157 { 158 struct extmnttab *mntp; 159 bool_t quick = FALSE; 160 int is_v4 = FALSE; 161 162 mntp = mnttab_find(pathname); 163 if (mntp) { 164 pathname = mntp->mnt_mountp; 165 } 166 167 if (mntp) 168 is_v4 = is_v4_mount(mntp); 169 170 /* Forced unmount will almost always be successful */ 171 if (umount2(pathname, umnt_flag) < 0) { 172 if (errno == EBUSY) { 173 pr_err(gettext("%s: is busy\n"), pathname); 174 } else { 175 pr_err(gettext("%s: not mounted\n"), pathname); 176 } 177 return (RET_ERR); 178 } 179 180 /* All done if it's v4 */ 181 if (is_v4 == TRUE) 182 return (RET_OK); 183 184 /* Inform server quickly in case of forced unmount */ 185 if (umnt_flag & MS_FORCE) 186 quick = TRUE; 187 188 if (mntp) { 189 inform_server(mntp->mnt_special, mntp->mnt_mntopts, quick); 190 } 191 192 return (RET_OK); 193 } 194 195 /* 196 * Find the mnttab entry that corresponds to "name". 197 * We're not sure what the name represents: either 198 * a mountpoint name, or a special name (server:/path). 199 * Return the last entry in the file that matches. 200 */ 201 static struct extmnttab * 202 mnttab_find(dirname) 203 char *dirname; 204 { 205 FILE *fp; 206 struct extmnttab mnt; 207 struct extmnttab *res = NULL; 208 209 fp = fopen(MNTTAB, "r"); 210 if (fp == NULL) { 211 pr_err("%s: %s\n", MNTTAB, strerror(errno)); 212 return (NULL); 213 } 214 while (getextmntent(fp, &mnt, sizeof (struct extmnttab)) == 0) { 215 if (strcmp(mnt.mnt_mountp, dirname) == 0 || 216 strcmp(mnt.mnt_special, dirname) == 0) { 217 if (res) 218 fsfreemnttab(res); 219 res = fsdupmnttab(&mnt); 220 } 221 } 222 223 fclose(fp); 224 return (res); 225 } 226 227 /* 228 * If quick is TRUE, it will try to inform server quickly 229 * as possible. 230 */ 231 static void 232 inform_server(char *string, char *opts, bool_t quick) 233 { 234 struct timeval timeout; 235 CLIENT *cl; 236 enum clnt_stat rpc_stat; 237 struct replica *list; 238 int i, n; 239 char *p = NULL; 240 static struct timeval create_timeout = {5, 0}; 241 static struct timeval *timep; 242 243 list = parse_replica(string, &n); 244 245 if (list == NULL) { 246 if (n < 0) 247 pr_err(gettext("%s is not hostname:path format\n"), 248 string); 249 else 250 pr_err(gettext("no memory\n")); 251 return; 252 } 253 254 /* 255 * If mounted with -o public, then no need to contact server 256 * because mount protocol was not used. 257 */ 258 if (opts != NULL) 259 p = strstr(opts, MNTOPT_PUBLIC); 260 261 if (p != NULL) { 262 i = strlen(MNTOPT_PUBLIC); 263 /* 264 * Now make sure the match of "public" isn't a substring 265 * of another option. 266 */ 267 if (((p == opts) || (*(p-1) == ',')) && 268 ((p[i] == ',') || (p[i] == '\0'))) 269 return; 270 } 271 272 for (i = 0; i < n; i++) { 273 int vers; 274 275 /* 276 * Skip file systems mounted using WebNFS, because mount 277 * protocol was not used. 278 */ 279 if (strcmp(list[i].host, "nfs") == 0 && strncmp(list[i].path, 280 "//", 2) == 0) 281 continue; 282 283 vers = MOUNTVERS; 284 retry: 285 /* 286 * Use 5 sec. timeout if file system is forced unmounted, 287 * otherwise use default timeout to create a client handle. 288 * This would minimize the time to force unmount a file 289 * system reside on a server that is down. 290 */ 291 timep = (quick ? &create_timeout : NULL); 292 cl = clnt_create_timed(list[i].host, MOUNTPROG, vers, 293 "datagram_n", timep); 294 /* 295 * Do not print any error messages in case of forced 296 * unmount. 297 */ 298 if (cl == NULL) { 299 if (!quick) 300 pr_err("%s:%s %s\n", list[i].host, list[i].path, 301 clnt_spcreateerror( 302 "server not responding")); 303 continue; 304 } 305 /* 306 * Now it is most likely that the NFS client will be able 307 * to contact the server since rpcbind is running on 308 * the server. There is still a small window in which 309 * server can be unreachable. 310 */ 311 312 if (__clnt_bindresvport(cl) < 0) { 313 if (!quick) 314 pr_err(gettext( 315 "couldn't bind to reserved port\n")); 316 clnt_destroy(cl); 317 return; 318 } 319 if ((cl->cl_auth = authsys_create_default()) == NULL) { 320 if (!quick) 321 pr_err(gettext( 322 "couldn't create authsys structure\n")); 323 clnt_destroy(cl); 324 return; 325 } 326 timeout.tv_usec = 0; 327 timeout.tv_sec = 5; 328 clnt_control(cl, CLSET_RETRY_TIMEOUT, (char *)&timeout); 329 timeout.tv_sec = 25; 330 rpc_stat = clnt_call(cl, MOUNTPROC_UMNT, xdr_dirpath, 331 (char *)&list[i].path, xdr_void, (char *)NULL, 332 timeout); 333 AUTH_DESTROY(cl->cl_auth); 334 clnt_destroy(cl); 335 if (rpc_stat == RPC_PROGVERSMISMATCH && vers == MOUNTVERS) { 336 /* 337 * The rare case of a v3-only server 338 */ 339 vers = MOUNTVERS3; 340 goto retry; 341 } 342 if (rpc_stat != RPC_SUCCESS) 343 pr_err("%s\n", clnt_sperror(cl, "unmount")); 344 } 345 346 free_replica(list, n); 347 } 348 349 /* 350 * This function's behavior is taken from nfsstat. 351 * Trying to determine what NFS version was used for the mount. 352 */ 353 int 354 is_v4_mount(struct extmnttab *mntp) 355 { 356 kstat_ctl_t *kc = NULL; /* libkstat cookie */ 357 kstat_t *ksp; 358 ulong_t fsid; 359 struct mntinfo_kstat mik; 360 361 if (mntp == NULL) 362 return (FALSE); 363 364 if ((kc = kstat_open()) == NULL) 365 return (FALSE); 366 367 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 368 if (ksp->ks_type != KSTAT_TYPE_RAW) 369 continue; 370 if (strcmp(ksp->ks_module, "nfs") != 0) 371 continue; 372 if (strcmp(ksp->ks_name, "mntinfo") != 0) 373 continue; 374 if (mntp->mnt_minor != ksp->ks_instance) 375 continue; 376 377 if (kstat_read(kc, ksp, &mik) == -1) 378 continue; 379 380 if (mik.mik_vers == 4) 381 return (TRUE); 382 else 383 return (FALSE); 384 } 385 return (FALSE); 386 } 387