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 dfmounts 44 */ 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <stdarg.h> 48 #include <string.h> 49 #include <rpc/rpc.h> 50 #include <rpc/rpcb_clnt.h> 51 #include <sys/socket.h> 52 #include <netdb.h> 53 #include <sys/time.h> 54 #include <sys/errno.h> 55 #include <nfs/nfs.h> 56 #include <rpcsvc/mount.h> 57 #include <locale.h> 58 59 static int hflg; 60 61 static void pr_mounts(char *); 62 static void freemntlist(struct mountbody *); 63 static int sortpath(const void *, const void *); 64 static void usage(void); 65 static void pr_err(char *, ...); 66 67 main(argc, argv) 68 int argc; 69 char **argv; 70 { 71 72 char hostbuf[256]; 73 extern int optind; 74 int i, c; 75 76 (void) setlocale(LC_ALL, ""); 77 78 #if !defined(TEXT_DOMAIN) 79 #define TEXT_DOMAIN "SYS_TEST" 80 #endif 81 (void) textdomain(TEXT_DOMAIN); 82 83 while ((c = getopt(argc, argv, "h")) != EOF) { 84 switch (c) { 85 case 'h': 86 hflg++; 87 break; 88 default: 89 usage(); 90 exit(1); 91 } 92 } 93 94 if (optind < argc) { 95 for (i = optind; i < argc; i++) 96 pr_mounts(argv[i]); 97 } else { 98 if (gethostname(hostbuf, sizeof (hostbuf)) < 0) { 99 perror("nfs dfmounts: gethostname"); 100 exit(1); 101 } 102 pr_mounts(hostbuf); 103 } 104 105 return (0); 106 } 107 108 #define NTABLEENTRIES 2048 109 static struct mountbody *table[NTABLEENTRIES]; 110 static struct timeval rpc_totout_new = {15, 0}; 111 112 /* 113 * Print the filesystems on "host" that are currently mounted by a client. 114 */ 115 116 static void 117 pr_mounts(host) 118 char *host; 119 { 120 CLIENT *cl; 121 struct mountbody *ml = NULL; 122 struct mountbody **tb, **endtb; 123 enum clnt_stat err; 124 char *lastpath; 125 char *lastclient; 126 int tail = 0; 127 struct timeval tout, rpc_totout_old; 128 129 (void) __rpc_control(CLCR_GET_RPCB_TIMEOUT, &rpc_totout_old); 130 (void) __rpc_control(CLCR_SET_RPCB_TIMEOUT, &rpc_totout_new); 131 132 /* 133 * First try circuit, then drop back to datagram if 134 * circuit is unavailable (an old version of mountd perhaps) 135 * Using circuit is preferred because it can handle 136 * arbitrarily long export lists. 137 */ 138 cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "circuit_n"); 139 if (cl == NULL) { 140 if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) 141 cl = clnt_create(host, MOUNTPROG, MOUNTVERS, 142 "datagram_n"); 143 if (cl == NULL) { 144 pr_err(gettext("can't contact server: %s\n"), 145 clnt_spcreateerror(host)); 146 (void) __rpc_control(CLCR_SET_RPCB_TIMEOUT, 147 &rpc_totout_old); 148 return; 149 } 150 } 151 152 (void) __rpc_control(CLCR_SET_RPCB_TIMEOUT, &rpc_totout_old); 153 tout.tv_sec = 10; 154 tout.tv_usec = 0; 155 156 if (err = clnt_call(cl, MOUNTPROC_DUMP, xdr_void, 157 0, xdr_mountlist, (caddr_t) &ml, tout)) { 158 pr_err("%s\n", clnt_sperrno(err)); 159 clnt_destroy(cl); 160 return; 161 } 162 163 if (ml == NULL) 164 return; /* no mounts */ 165 166 if (!hflg) { 167 printf("%-8s %10s %-24s %s", 168 gettext("RESOURCE"), gettext("SERVER"), 169 gettext("PATHNAME"), gettext("CLIENTS")); 170 hflg++; 171 } 172 173 /* 174 * Create an array describing the mounts, so that we can sort them. 175 */ 176 tb = table; 177 for (; ml != NULL && tb < &table[NTABLEENTRIES]; ml = ml->ml_next) 178 *tb++ = ml; 179 if (ml != NULL && tb == &table[NTABLEENTRIES]) 180 pr_err(gettext("table overflow: only %d entries shown\n"), 181 NTABLEENTRIES); 182 endtb = tb; 183 qsort(table, endtb - table, sizeof (struct mountbody *), sortpath); 184 185 /* 186 * Print out the sorted array. Group entries for the same 187 * filesystem together, and ignore duplicate entries. 188 */ 189 lastpath = ""; 190 lastclient = ""; 191 for (tb = table; tb < endtb; tb++) { 192 if (*((*tb)->ml_directory) == '\0' || 193 *((*tb)->ml_hostname) == '\0') 194 continue; 195 if (strcmp(lastpath, (*tb)->ml_directory) == 0) { 196 if (strcmp(lastclient, (*tb)->ml_hostname) == 0) { 197 continue; /* ignore duplicate */ 198 } 199 } else { 200 printf("\n%-8s %10s %-24s ", 201 " -", host, (*tb)->ml_directory); 202 lastpath = (*tb)->ml_directory; 203 tail = 0; 204 } 205 if (tail++) 206 printf(","); 207 printf("%s", (*tb)->ml_hostname); 208 lastclient = (*tb)->ml_hostname; 209 } 210 printf("\n"); 211 212 freemntlist(ml); 213 clnt_destroy(cl); 214 } 215 216 static void 217 freemntlist(ml) 218 struct mountbody *ml; 219 { 220 register struct mountbody *old; 221 222 while (ml) { 223 if (ml->ml_hostname) 224 free(ml->ml_hostname); 225 if (ml->ml_directory) 226 free(ml->ml_directory); 227 old = ml; 228 ml = ml->ml_next; 229 free(old); 230 } 231 } 232 233 /* 234 * Compare two structs for mounted filesystems. The primary sort key is 235 * the name of the exported filesystem. There is also a secondary sort on 236 * the name of the client, so that duplicate entries (same path and 237 * hostname) will sort together. 238 * 239 * Returns < 0 if the first entry sorts before the second entry, 0 if they 240 * sort the same, and > 0 if the first entry sorts after the second entry. 241 */ 242 243 static int 244 sortpath(a, b) 245 const void *a, *b; 246 { 247 const struct mountbody **m1, **m2; 248 int result; 249 250 m1 = (const struct mountbody **)a; 251 m2 = (const struct mountbody **)b; 252 253 result = strcmp((*m1)->ml_directory, (*m2)->ml_directory); 254 if (result == 0) { 255 result = strcmp((*m1)->ml_hostname, (*m2)->ml_hostname); 256 } 257 258 return (result); 259 } 260 261 static void 262 usage() 263 { 264 (void) fprintf(stderr, gettext("Usage: dfmounts [-h] [host ...]\n")); 265 } 266 267 /* VARARGS1 */ 268 static void 269 pr_err(char *fmt, ...) 270 { 271 va_list ap; 272 273 va_start(ap, fmt); 274 (void) fprintf(stderr, "nfs dfmounts: "); 275 (void) vfprintf(stderr, fmt, ap); 276 va_end(ap); 277 } 278