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