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