xref: /illumos-gate/usr/src/cmd/fs.d/nfs/nfsstat/nfsstat.c (revision d4517e84e3ffaa7bc4207a17f694c309904e7c48)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /* LINTLIBRARY */
23 /* PROTOLIB1 */
24 
25 /*
26  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
27  * Use is subject to license terms.
28  */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 /*
33  * nfsstat: Network File System statistics
34  *
35  */
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <stdarg.h>
41 #include <string.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <kvm.h>
45 #include <kstat.h>
46 #include <sys/param.h>
47 #include <sys/types.h>
48 #include <sys/t_lock.h>
49 #include <sys/tiuser.h>
50 #include <sys/statvfs.h>
51 #include <sys/mntent.h>
52 #include <sys/mnttab.h>
53 #include <sys/sysmacros.h>
54 #include <sys/mkdev.h>
55 #include <rpc/types.h>
56 #include <rpc/xdr.h>
57 #include <rpc/auth.h>
58 #include <rpc/clnt.h>
59 #include <nfs/nfs.h>
60 #include <nfs/nfs_clnt.h>
61 #include <nfs/nfs_sec.h>
62 #include <inttypes.h>
63 #include <signal.h>
64 #include <time.h>
65 #include <sys/time.h>
66 #include <strings.h>
67 #include <ctype.h>
68 
69 
70 static kstat_ctl_t *kc = NULL;		/* libkstat cookie */
71 static kstat_t *rpc_clts_client_kstat, *rpc_clts_server_kstat;
72 static kstat_t *rpc_cots_client_kstat, *rpc_cots_server_kstat;
73 static kstat_t *rpc_rdma_client_kstat, *rpc_rdma_server_kstat;
74 static kstat_t *nfs_client_kstat, *nfs_server_v2_kstat, *nfs_server_v3_kstat;
75 static kstat_t *nfs4_client_kstat, *nfs_server_v4_kstat;
76 static kstat_t *rfsproccnt_v2_kstat, *rfsproccnt_v3_kstat, *rfsproccnt_v4_kstat;
77 static kstat_t *rfsreqcnt_v2_kstat, *rfsreqcnt_v3_kstat, *rfsreqcnt_v4_kstat;
78 static kstat_t *aclproccnt_v2_kstat, *aclproccnt_v3_kstat;
79 static kstat_t *aclreqcnt_v2_kstat, *aclreqcnt_v3_kstat;
80 static kstat_t *ksum_kstat;
81 
82 static void handle_sig(int);
83 static int getstats_rpc(void);
84 static int getstats_nfs(void);
85 static int getstats_rfsproc(int);
86 static int getstats_rfsreq(int);
87 static int getstats_aclproc(void);
88 static int getstats_aclreq(void);
89 static void putstats(void);
90 static void setup(void);
91 static void cr_print(int);
92 static void sr_print(int);
93 static void cn_print(int, int);
94 static void sn_print(int, int);
95 static void ca_print(int, int);
96 static void sa_print(int, int);
97 static void req_print(kstat_t *, kstat_t *, int, int, int);
98 static void req_print_v4(kstat_t *, kstat_t *, int, int);
99 static void stat_print(const char *, kstat_t *, kstat_t *, int, int);
100 static void kstat_sum(kstat_t *, kstat_t *, kstat_t *);
101 static void stats_timer(int);
102 static void safe_zalloc(void **, uint_t, int);
103 static int safe_strtoi(char const *, char *);
104 
105 
106 static void kstat_copy(kstat_t *, kstat_t *, int);
107 static void fail(int, char *, ...);
108 static kid_t safe_kstat_read(kstat_ctl_t *, kstat_t *, void *);
109 static kid_t safe_kstat_write(kstat_ctl_t *, kstat_t *, void *);
110 
111 static void usage(void);
112 static void mi_print(void);
113 static int ignore(char *);
114 static int interval;		/* interval between stats */
115 static int count;		/* number of iterations the stat is printed */
116 #define	MAX_COLUMNS	80
117 #define	MAX_PATHS	50	/* max paths that can be taken by -m */
118 
119 /*
120  * MI4_MIRRORMOUNT is canonically defined in nfs4_clnt.h, but we cannot
121  * include that file here.
122  */
123 #define	MI4_MIRRORMOUNT 0x4000
124 #define	NFS_V4		4
125 
126 static int req_width(kstat_t *, int);
127 static int stat_width(kstat_t *, int);
128 static char *path [MAX_PATHS] = {NULL};  /* array to store the multiple paths */
129 
130 /*
131  * Struct holds the previous kstat values so
132  * we can compute deltas when using the -i flag
133  */
134 typedef struct old_kstat
135 {
136 	kstat_t kst;
137 	int tot;
138 } old_kstat_t;
139 
140 static old_kstat_t old_rpc_clts_client_kstat, old_rpc_clts_server_kstat;
141 static old_kstat_t old_rpc_cots_client_kstat, old_rpc_cots_server_kstat;
142 static old_kstat_t old_rpc_rdma_client_kstat, old_rpc_rdma_server_kstat;
143 static old_kstat_t old_nfs_client_kstat, old_nfs_server_v2_kstat;
144 static old_kstat_t old_nfs_server_v3_kstat, old_ksum_kstat;
145 static old_kstat_t old_nfs4_client_kstat, old_nfs_server_v4_kstat;
146 static old_kstat_t old_rfsproccnt_v2_kstat, old_rfsproccnt_v3_kstat;
147 static old_kstat_t old_rfsproccnt_v4_kstat, old_rfsreqcnt_v2_kstat;
148 static old_kstat_t old_rfsreqcnt_v3_kstat, old_rfsreqcnt_v4_kstat;
149 static old_kstat_t old_aclproccnt_v2_kstat, old_aclproccnt_v3_kstat;
150 static old_kstat_t old_aclreqcnt_v2_kstat, old_aclreqcnt_v3_kstat;
151 
152 
153 
154 int
155 main(int argc, char *argv[])
156 {
157 	int c, go_forever, j;
158 	int cflag = 0;		/* client stats */
159 	int sflag = 0;		/* server stats */
160 	int nflag = 0;		/* nfs stats */
161 	int rflag = 0;		/* rpc stats */
162 	int mflag = 0;		/* mount table stats */
163 	int aflag = 0;		/* print acl statistics */
164 	int vflag = 0;		/* version specified, 0 specifies all */
165 	int zflag = 0;		/* zero stats after printing */
166 	char *split_line = "*******************************************"
167 	    "*************************************";
168 
169 	interval = 0;
170 	count = 0;
171 	go_forever = 0;
172 
173 	while ((c = getopt(argc, argv, "cnrsmzav:")) != EOF) {
174 		switch (c) {
175 		case 'c':
176 			cflag++;
177 			break;
178 		case 'n':
179 			nflag++;
180 			break;
181 		case 'r':
182 			rflag++;
183 			break;
184 		case 's':
185 			sflag++;
186 			break;
187 		case 'm':
188 			mflag++;
189 			break;
190 		case 'z':
191 			if (geteuid())
192 				fail(0, "Must be root for z flag\n");
193 			zflag++;
194 			break;
195 		case 'a':
196 			aflag++;
197 			break;
198 		case 'v':
199 			vflag = atoi(optarg);
200 			if ((vflag < 2) || (vflag > 4))
201 				fail(0, "Invalid version number\n");
202 			break;
203 		case '?':
204 		default:
205 			usage();
206 		}
207 	}
208 
209 	if (((argc - optind) > 0) && !mflag) {
210 
211 		interval = safe_strtoi(argv[optind], "invalid interval");
212 		if (interval < 1)
213 			fail(0, "invalid interval\n");
214 		optind++;
215 
216 		if ((argc - optind) > 0) {
217 			count = safe_strtoi(argv[optind], "invalid count");
218 			if ((count <= 0) || (count == NULL))
219 				fail(0, "invalid count\n");
220 		}
221 		optind++;
222 
223 		if ((argc - optind) > 0)
224 			usage();
225 
226 		/*
227 		 * no count number was set, so we will loop infinitely
228 		 * at interval specified
229 		 */
230 		if (!count)
231 			go_forever = 1;
232 		stats_timer(interval);
233 	} else if (mflag) {
234 
235 		if (cflag || rflag || sflag || zflag || nflag || aflag || vflag)
236 			fail(0,
237 			    "The -m flag may not be used with any other flags");
238 
239 		for (j = 0; (argc - optind > 0) && (j < (MAX_PATHS - 1)); j++) {
240 			path[j] =  argv[optind];
241 			if (*path[j] != '/')
242 				fail(0, "Please fully qualify your pathname "
243 				    "with a leading '/'");
244 			optind++;
245 		}
246 		path[j] = NULL;
247 		if (argc - optind > 0)
248 			fprintf(stderr, "Only the first 50 paths "
249 			    "will be searched for\n");
250 	}
251 
252 	setup();
253 
254 	do {
255 		if (mflag) {
256 			mi_print();
257 		} else {
258 
259 			if (sflag &&
260 			    (rpc_clts_server_kstat == NULL ||
261 			    nfs_server_v4_kstat == NULL)) {
262 				fprintf(stderr,
263 				    "nfsstat: kernel is not configured with "
264 				    "the server nfs and rpc code.\n");
265 			}
266 
267 			/* if s and nothing else, all 3 prints are called */
268 			if (sflag || (!sflag && !cflag)) {
269 				if (rflag || (!rflag && !nflag && !aflag))
270 					sr_print(zflag);
271 				if (nflag || (!rflag && !nflag && !aflag))
272 					sn_print(zflag, vflag);
273 				if (aflag || (!rflag && !nflag && !aflag))
274 					sa_print(zflag, vflag);
275 			}
276 			if (cflag &&
277 			    (rpc_clts_client_kstat == NULL ||
278 			    nfs_client_kstat == NULL)) {
279 				fprintf(stderr,
280 				    "nfsstat: kernel is not configured with"
281 				    " the client nfs and rpc code.\n");
282 			}
283 			if (cflag || (!sflag && !cflag)) {
284 				if (rflag || (!rflag && !nflag && !aflag))
285 					cr_print(zflag);
286 				if (nflag || (!rflag && !nflag && !aflag))
287 					cn_print(zflag, vflag);
288 				if (aflag || (!rflag && !nflag && !aflag))
289 					ca_print(zflag, vflag);
290 			}
291 		}
292 
293 		if (zflag)
294 			putstats();
295 		if (interval)
296 			printf("%s\n", split_line);
297 
298 		if (interval > 0)
299 			(void) pause();
300 	} while ((--count > 0) || go_forever);
301 
302 	kstat_close(kc);
303 	free(ksum_kstat);
304 	return (0);
305 }
306 
307 
308 static int
309 getstats_rpc(void)
310 {
311 	int field_width = 0;
312 
313 	if (rpc_clts_client_kstat != NULL) {
314 		safe_kstat_read(kc, rpc_clts_client_kstat, NULL);
315 		field_width = stat_width(rpc_clts_client_kstat, field_width);
316 	}
317 
318 	if (rpc_cots_client_kstat != NULL) {
319 		safe_kstat_read(kc, rpc_cots_client_kstat, NULL);
320 		field_width = stat_width(rpc_cots_client_kstat, field_width);
321 	}
322 
323 	if (rpc_rdma_client_kstat != NULL) {
324 		safe_kstat_read(kc, rpc_rdma_client_kstat, NULL);
325 		field_width = stat_width(rpc_rdma_client_kstat, field_width);
326 	}
327 
328 	if (rpc_clts_server_kstat != NULL) {
329 		safe_kstat_read(kc, rpc_clts_server_kstat, NULL);
330 		field_width =  stat_width(rpc_clts_server_kstat, field_width);
331 	}
332 	if (rpc_cots_server_kstat != NULL) {
333 		safe_kstat_read(kc, rpc_cots_server_kstat, NULL);
334 		field_width = stat_width(rpc_cots_server_kstat, field_width);
335 	}
336 	if (rpc_rdma_server_kstat != NULL) {
337 		safe_kstat_read(kc, rpc_rdma_server_kstat, NULL);
338 		field_width = stat_width(rpc_rdma_server_kstat, field_width);
339 	}
340 	return (field_width);
341 }
342 
343 static int
344 getstats_nfs(void)
345 {
346 	int field_width = 0;
347 
348 	if (nfs_client_kstat != NULL) {
349 		safe_kstat_read(kc, nfs_client_kstat, NULL);
350 		field_width = stat_width(nfs_client_kstat, field_width);
351 	}
352 	if (nfs4_client_kstat != NULL) {
353 		safe_kstat_read(kc, nfs4_client_kstat, NULL);
354 		field_width = stat_width(nfs4_client_kstat, field_width);
355 	}
356 	if (nfs_server_v2_kstat != NULL) {
357 		safe_kstat_read(kc, nfs_server_v2_kstat, NULL);
358 		field_width = stat_width(nfs_server_v2_kstat, field_width);
359 	}
360 	if (nfs_server_v3_kstat != NULL) {
361 		safe_kstat_read(kc, nfs_server_v3_kstat, NULL);
362 		field_width = stat_width(nfs_server_v3_kstat, field_width);
363 	}
364 	if (nfs_server_v4_kstat != NULL) {
365 		safe_kstat_read(kc, nfs_server_v4_kstat, NULL);
366 		field_width = stat_width(nfs_server_v4_kstat, field_width);
367 	}
368 	return (field_width);
369 }
370 
371 static int
372 getstats_rfsproc(int ver)
373 {
374 	int field_width = 0;
375 
376 	if ((ver == 2) && (rfsproccnt_v2_kstat != NULL)) {
377 		safe_kstat_read(kc, rfsproccnt_v2_kstat, NULL);
378 		field_width = req_width(rfsproccnt_v2_kstat, field_width);
379 	}
380 	if ((ver == 3) && (rfsproccnt_v3_kstat != NULL)) {
381 		safe_kstat_read(kc, rfsproccnt_v3_kstat, NULL);
382 		field_width = req_width(rfsproccnt_v3_kstat, field_width);
383 	}
384 	if ((ver == 4) && (rfsproccnt_v4_kstat != NULL)) {
385 		safe_kstat_read(kc, rfsproccnt_v4_kstat, NULL);
386 		field_width = req_width(rfsproccnt_v4_kstat, field_width);
387 	}
388 	return (field_width);
389 }
390 
391 static int
392 getstats_rfsreq(int ver)
393 {
394 	int field_width = 0;
395 	if ((ver == 2) && (rfsreqcnt_v2_kstat != NULL)) {
396 		safe_kstat_read(kc, rfsreqcnt_v2_kstat, NULL);
397 		field_width = req_width(rfsreqcnt_v2_kstat, field_width);
398 	}
399 	if ((ver == 3) && (rfsreqcnt_v3_kstat != NULL)) {
400 		safe_kstat_read(kc, rfsreqcnt_v3_kstat, NULL);
401 		field_width = req_width(rfsreqcnt_v3_kstat,  field_width);
402 	}
403 	if ((ver == 4) && (rfsreqcnt_v4_kstat != NULL)) {
404 		safe_kstat_read(kc, rfsreqcnt_v4_kstat, NULL);
405 		field_width = req_width(rfsreqcnt_v4_kstat, field_width);
406 	}
407 	return (field_width);
408 }
409 
410 static int
411 getstats_aclproc(void)
412 {
413 	int field_width = 0;
414 	if (aclproccnt_v2_kstat != NULL) {
415 		safe_kstat_read(kc, aclproccnt_v2_kstat, NULL);
416 		field_width = req_width(aclproccnt_v2_kstat, field_width);
417 	}
418 	if (aclproccnt_v3_kstat != NULL) {
419 		safe_kstat_read(kc, aclproccnt_v3_kstat, NULL);
420 		field_width = req_width(aclproccnt_v3_kstat, field_width);
421 	}
422 	return (field_width);
423 }
424 
425 static int
426 getstats_aclreq(void)
427 {
428 	int field_width = 0;
429 	if (aclreqcnt_v2_kstat != NULL) {
430 		safe_kstat_read(kc, aclreqcnt_v2_kstat, NULL);
431 		field_width = req_width(aclreqcnt_v2_kstat, field_width);
432 	}
433 	if (aclreqcnt_v3_kstat != NULL) {
434 		safe_kstat_read(kc, aclreqcnt_v3_kstat, NULL);
435 		field_width = req_width(aclreqcnt_v3_kstat, field_width);
436 	}
437 	return (field_width);
438 }
439 
440 static void
441 putstats(void)
442 {
443 	if (rpc_clts_client_kstat != NULL)
444 		safe_kstat_write(kc, rpc_clts_client_kstat, NULL);
445 	if (rpc_cots_client_kstat != NULL)
446 		safe_kstat_write(kc, rpc_cots_client_kstat, NULL);
447 	if (rpc_rdma_client_kstat != NULL)
448 		safe_kstat_write(kc, rpc_rdma_client_kstat, NULL);
449 	if (nfs_client_kstat != NULL)
450 		safe_kstat_write(kc, nfs_client_kstat, NULL);
451 	if (nfs4_client_kstat != NULL)
452 		safe_kstat_write(kc, nfs4_client_kstat, NULL);
453 	if (rpc_clts_server_kstat != NULL)
454 		safe_kstat_write(kc, rpc_clts_server_kstat, NULL);
455 	if (rpc_cots_server_kstat != NULL)
456 		safe_kstat_write(kc, rpc_cots_server_kstat, NULL);
457 	if (rpc_rdma_server_kstat != NULL)
458 		safe_kstat_write(kc, rpc_rdma_server_kstat, NULL);
459 	if (nfs_server_v2_kstat != NULL)
460 		safe_kstat_write(kc, nfs_server_v2_kstat, NULL);
461 	if (nfs_server_v3_kstat != NULL)
462 		safe_kstat_write(kc, nfs_server_v3_kstat, NULL);
463 	if (nfs_server_v4_kstat != NULL)
464 		safe_kstat_write(kc, nfs_server_v4_kstat, NULL);
465 	if (rfsproccnt_v2_kstat != NULL)
466 		safe_kstat_write(kc, rfsproccnt_v2_kstat, NULL);
467 	if (rfsproccnt_v3_kstat != NULL)
468 		safe_kstat_write(kc, rfsproccnt_v3_kstat, NULL);
469 	if (rfsproccnt_v4_kstat != NULL)
470 		safe_kstat_write(kc, rfsproccnt_v4_kstat, NULL);
471 	if (rfsreqcnt_v2_kstat != NULL)
472 		safe_kstat_write(kc, rfsreqcnt_v2_kstat, NULL);
473 	if (rfsreqcnt_v3_kstat != NULL)
474 		safe_kstat_write(kc, rfsreqcnt_v3_kstat, NULL);
475 	if (rfsreqcnt_v4_kstat != NULL)
476 		safe_kstat_write(kc, rfsreqcnt_v4_kstat, NULL);
477 	if (aclproccnt_v2_kstat != NULL)
478 		safe_kstat_write(kc, aclproccnt_v2_kstat, NULL);
479 	if (aclproccnt_v3_kstat != NULL)
480 		safe_kstat_write(kc, aclproccnt_v3_kstat, NULL);
481 	if (aclreqcnt_v2_kstat != NULL)
482 		safe_kstat_write(kc, aclreqcnt_v2_kstat, NULL);
483 	if (aclreqcnt_v3_kstat != NULL)
484 		safe_kstat_write(kc, aclreqcnt_v3_kstat, NULL);
485 }
486 
487 static void
488 setup(void)
489 {
490 	if ((kc = kstat_open()) == NULL)
491 		fail(1, "kstat_open(): can't open /dev/kstat");
492 
493 	/* malloc space for our temporary kstat */
494 	ksum_kstat = malloc(sizeof (kstat_t));
495 	rpc_clts_client_kstat = kstat_lookup(kc, "unix", 0, "rpc_clts_client");
496 	rpc_clts_server_kstat = kstat_lookup(kc, "unix", 0, "rpc_clts_server");
497 	rpc_cots_client_kstat = kstat_lookup(kc, "unix", 0, "rpc_cots_client");
498 	rpc_cots_server_kstat = kstat_lookup(kc, "unix", 0, "rpc_cots_server");
499 	rpc_rdma_client_kstat = kstat_lookup(kc, "unix", 0, "rpc_rdma_client");
500 	rpc_rdma_server_kstat = kstat_lookup(kc, "unix", 0, "rpc_rdma_server");
501 	nfs_client_kstat = kstat_lookup(kc, "nfs", 0, "nfs_client");
502 	nfs4_client_kstat = kstat_lookup(kc, "nfs", 0, "nfs4_client");
503 	nfs_server_v2_kstat = kstat_lookup(kc, "nfs", 2, "nfs_server");
504 	nfs_server_v3_kstat = kstat_lookup(kc, "nfs", 3, "nfs_server");
505 	nfs_server_v4_kstat = kstat_lookup(kc, "nfs", 4, "nfs_server");
506 	rfsproccnt_v2_kstat = kstat_lookup(kc, "nfs", 0, "rfsproccnt_v2");
507 	rfsproccnt_v3_kstat = kstat_lookup(kc, "nfs", 0, "rfsproccnt_v3");
508 	rfsproccnt_v4_kstat = kstat_lookup(kc, "nfs", 0, "rfsproccnt_v4");
509 	rfsreqcnt_v2_kstat = kstat_lookup(kc, "nfs", 0, "rfsreqcnt_v2");
510 	rfsreqcnt_v3_kstat = kstat_lookup(kc, "nfs", 0, "rfsreqcnt_v3");
511 	rfsreqcnt_v4_kstat = kstat_lookup(kc, "nfs", 0, "rfsreqcnt_v4");
512 	aclproccnt_v2_kstat = kstat_lookup(kc, "nfs_acl", 0, "aclproccnt_v2");
513 	aclproccnt_v3_kstat = kstat_lookup(kc, "nfs_acl", 0, "aclproccnt_v3");
514 	aclreqcnt_v2_kstat = kstat_lookup(kc, "nfs_acl", 0, "aclreqcnt_v2");
515 	aclreqcnt_v3_kstat = kstat_lookup(kc, "nfs_acl", 0, "aclreqcnt_v3");
516 	if (rpc_clts_client_kstat == NULL && rpc_cots_server_kstat == NULL &&
517 	    rfsproccnt_v2_kstat == NULL && rfsreqcnt_v3_kstat == NULL)
518 		fail(0, "Multiple kstat lookups failed."
519 		    "Your kernel module may not be loaded\n");
520 }
521 
522 static int
523 req_width(kstat_t *req, int field_width)
524 {
525 	int i, nreq, per, len;
526 	char fixlen[128];
527 	kstat_named_t *knp;
528 	uint64_t tot;
529 
530 	tot = 0;
531 	knp = KSTAT_NAMED_PTR(req);
532 	for (i = 0; i < req->ks_ndata; i++)
533 		tot += knp[i].value.ui64;
534 
535 	knp = kstat_data_lookup(req, "null");
536 	nreq = req->ks_ndata - (knp - KSTAT_NAMED_PTR(req));
537 
538 	for (i = 0; i < nreq; i++) {
539 		len = strlen(knp[i].name) + 1;
540 		if (field_width < len)
541 			field_width = len;
542 		if (tot)
543 			per = (int)(knp[i].value.ui64 * 100 / tot);
544 		else
545 			per = 0;
546 		(void) sprintf(fixlen, "%" PRIu64 " %d%%",
547 		    knp[i].value.ui64, per);
548 		len = strlen(fixlen) + 1;
549 		if (field_width < len)
550 			field_width = len;
551 	}
552 	return (field_width);
553 }
554 
555 static int
556 stat_width(kstat_t *req, int field_width)
557 {
558 	int i, nreq, len;
559 	char fixlen[128];
560 	kstat_named_t *knp;
561 
562 	knp = KSTAT_NAMED_PTR(req);
563 	nreq = req->ks_ndata;
564 
565 	for (i = 0; i < nreq; i++) {
566 		len = strlen(knp[i].name) + 1;
567 		if (field_width < len)
568 			field_width = len;
569 		(void) sprintf(fixlen, "%" PRIu64, knp[i].value.ui64);
570 		len = strlen(fixlen) + 1;
571 		if (field_width < len)
572 			field_width = len;
573 	}
574 	return (field_width);
575 }
576 
577 static void
578 cr_print(int zflag)
579 {
580 	int field_width;
581 
582 	field_width = getstats_rpc();
583 	if (field_width == 0)
584 		return;
585 
586 	stat_print("\nClient rpc:\nConnection oriented:",
587 	    rpc_cots_client_kstat,
588 	    &old_rpc_cots_client_kstat.kst, field_width, zflag);
589 	stat_print("Connectionless:", rpc_clts_client_kstat,
590 	    &old_rpc_clts_client_kstat.kst, field_width, zflag);
591 	stat_print("RDMA based:", rpc_rdma_client_kstat,
592 	    &old_rpc_rdma_client_kstat.kst, field_width, zflag);
593 }
594 
595 static void
596 sr_print(int zflag)
597 {
598 	int field_width;
599 
600 	field_width = getstats_rpc();
601 	if (field_width == 0)
602 		return;
603 
604 	stat_print("\nServer rpc:\nConnection oriented:", rpc_cots_server_kstat,
605 	    &old_rpc_cots_server_kstat.kst, field_width, zflag);
606 	stat_print("Connectionless:", rpc_clts_server_kstat,
607 	    &old_rpc_clts_server_kstat.kst, field_width, zflag);
608 	stat_print("RDMA based:", rpc_rdma_server_kstat,
609 	    &old_rpc_rdma_server_kstat.kst, field_width, zflag);
610 }
611 
612 static void
613 cn_print(int zflag, int vflag)
614 {
615 	int field_width;
616 
617 	field_width = getstats_nfs();
618 	if (field_width == 0)
619 		return;
620 
621 	if (vflag == 0) {
622 		kstat_sum(nfs_client_kstat, nfs4_client_kstat, ksum_kstat);
623 		stat_print("\nClient nfs:", ksum_kstat, &old_ksum_kstat.kst,
624 		    field_width, zflag);
625 	}
626 
627 	if (vflag == 2 || vflag == 3) {
628 		stat_print("\nClient nfs:", nfs_client_kstat,
629 		    &old_nfs_client_kstat.kst, field_width, zflag);
630 	}
631 
632 	if (vflag == 4) {
633 		stat_print("\nClient nfs:", nfs4_client_kstat,
634 		    &old_nfs4_client_kstat.kst, field_width, zflag);
635 	}
636 
637 	if (vflag == 2 || vflag == 0) {
638 		field_width = getstats_rfsreq(2);
639 		req_print(rfsreqcnt_v2_kstat, &old_rfsreqcnt_v2_kstat.kst,
640 		    2, field_width, zflag);
641 	}
642 
643 	if (vflag == 3 || vflag == 0) {
644 		field_width = getstats_rfsreq(3);
645 		req_print(rfsreqcnt_v3_kstat, &old_rfsreqcnt_v3_kstat.kst, 3,
646 		    field_width, zflag);
647 	}
648 
649 	if (vflag == 4 || vflag == 0) {
650 		field_width = getstats_rfsreq(4);
651 		req_print_v4(rfsreqcnt_v4_kstat, &old_rfsreqcnt_v4_kstat.kst,
652 		    field_width, zflag);
653 	}
654 }
655 
656 static void
657 sn_print(int zflag, int vflag)
658 {
659 	int  field_width;
660 
661 	field_width = getstats_nfs();
662 	if (field_width == 0)
663 		return;
664 
665 	if (vflag == 2 || vflag == 0) {
666 		stat_print("\nServer NFSv2:", nfs_server_v2_kstat,
667 		    &old_nfs_server_v2_kstat.kst, field_width, zflag);
668 	}
669 
670 	if (vflag == 3 || vflag == 0) {
671 		stat_print("\nServer NFSv3:", nfs_server_v3_kstat,
672 		    &old_nfs_server_v3_kstat.kst, field_width, zflag);
673 	}
674 
675 	if (vflag == 4 || vflag == 0) {
676 		stat_print("\nServer NFSv4:", nfs_server_v4_kstat,
677 		    &old_nfs_server_v4_kstat.kst, field_width, zflag);
678 	}
679 
680 	if (vflag == 2 || vflag == 0) {
681 		field_width = getstats_rfsproc(2);
682 		req_print(rfsproccnt_v2_kstat, &old_rfsproccnt_v2_kstat.kst,
683 		    2, field_width, zflag);
684 	}
685 
686 	if (vflag == 3 || vflag == 0) {
687 		field_width = getstats_rfsproc(3);
688 		req_print(rfsproccnt_v3_kstat, &old_rfsproccnt_v3_kstat.kst,
689 		    3, field_width, zflag);
690 	}
691 
692 	if (vflag == 4 || vflag == 0) {
693 		field_width = getstats_rfsproc(4);
694 		req_print_v4(rfsproccnt_v4_kstat, &old_rfsproccnt_v4_kstat.kst,
695 		    field_width, zflag);
696 	}
697 }
698 
699 static void
700 ca_print(int zflag, int vflag)
701 {
702 	int  field_width;
703 
704 	field_width = getstats_aclreq();
705 	if (field_width == 0)
706 		return;
707 
708 	printf("\nClient nfs_acl:\n");
709 
710 	if (vflag == 2 || vflag == 0) {
711 		req_print(aclreqcnt_v2_kstat, &old_aclreqcnt_v2_kstat.kst, 2,
712 		    field_width, zflag);
713 	}
714 
715 	if (vflag == 3 || vflag == 0) {
716 		req_print(aclreqcnt_v3_kstat, &old_aclreqcnt_v3_kstat.kst,
717 		    3, field_width, zflag);
718 	}
719 }
720 
721 static void
722 sa_print(int zflag, int vflag)
723 {
724 	int  field_width;
725 
726 	field_width = getstats_aclproc();
727 	if (field_width == 0)
728 		return;
729 
730 	printf("\nServer nfs_acl:\n");
731 
732 	if (vflag == 2 || vflag == 0) {
733 		req_print(aclproccnt_v2_kstat, &old_aclproccnt_v2_kstat.kst,
734 		    2, field_width, zflag);
735 	}
736 
737 	if (vflag == 3 || vflag == 0) {
738 		req_print(aclproccnt_v3_kstat, &old_aclproccnt_v3_kstat.kst,
739 		    3, field_width, zflag);
740 	}
741 }
742 
743 #define	MIN(a, b)	((a) < (b) ? (a) : (b))
744 
745 static void
746 req_print(kstat_t *req, kstat_t *req_old, int ver, int field_width,
747     int zflag)
748 {
749 	int i, j, nreq, per, ncolumns;
750 	uint64_t tot, old_tot;
751 	char fixlen[128];
752 	kstat_named_t *knp;
753 	kstat_named_t *kptr;
754 	kstat_named_t *knp_old;
755 
756 	if (req == NULL)
757 		return;
758 
759 	if (field_width == 0)
760 		return;
761 
762 	ncolumns = (MAX_COLUMNS -1)/field_width;
763 	knp = kstat_data_lookup(req, "null");
764 	knp_old = KSTAT_NAMED_PTR(req_old);
765 
766 	kptr = KSTAT_NAMED_PTR(req);
767 	nreq = req->ks_ndata - (knp - KSTAT_NAMED_PTR(req));
768 
769 	tot = 0;
770 	old_tot = 0;
771 
772 	if (knp_old == NULL) {
773 		old_tot = 0;
774 	}
775 
776 	for (i = 0; i < req->ks_ndata; i++)
777 		tot += kptr[i].value.ui64;
778 
779 	if (interval && knp_old != NULL) {
780 		for (i = 0; i < req_old->ks_ndata; i++)
781 			old_tot += knp_old[i].value.ui64;
782 		tot -= old_tot;
783 	}
784 
785 	printf("Version %d: (%" PRIu64 " calls)\n", ver, tot);
786 
787 	for (i = 0; i < nreq; i += ncolumns) {
788 		for (j = i; j < MIN(i + ncolumns, nreq); j++) {
789 			printf("%-*s", field_width, knp[j].name);
790 		}
791 		printf("\n");
792 		for (j = i; j < MIN(i + ncolumns, nreq); j++) {
793 			if (tot && interval && knp_old != NULL)
794 				per = (int)((knp[j].value.ui64 -
795 				    knp_old[j].value.ui64) * 100 / tot);
796 			else if (tot)
797 				per = (int)(knp[j].value.ui64 * 100 / tot);
798 			else
799 				per = 0;
800 			(void) sprintf(fixlen, "%" PRIu64 " %d%% ",
801 			    ((interval && knp_old != NULL) ?
802 			    (knp[j].value.ui64 - knp_old[j].value.ui64)
803 			    : knp[j].value.ui64), per);
804 			printf("%-*s", field_width, fixlen);
805 		}
806 		if (zflag) {
807 			for (i = 0; i < req->ks_ndata; i++)
808 				knp[i].value.ui64 = 0;
809 		}
810 		printf("\n");
811 		if (knp_old != NULL)
812 			kstat_copy(req, req_old, 1);
813 		else
814 			kstat_copy(req, req_old, 0);
815 
816 	}
817 }
818 
819 /*
820  * Separate version of the req_print() to deal with V4 and its use of
821  * procedures and operations.  It looks odd to have the counts for
822  * both of those lumped into the same set of statistics so this
823  * function (copy of req_print() does the separation and titles).
824  */
825 
826 #define	COUNT	2
827 
828 static void
829 req_print_v4(kstat_t *req, kstat_t *req_old, int field_width, int zflag)
830 {
831 	int i, j, nreq, per, ncolumns;
832 	uint64_t tot, tot_ops, old_tot, old_tot_ops;
833 	char fixlen[128];
834 	kstat_named_t *kptr;
835 	kstat_named_t *knp;
836 	kstat_named_t *kptr_old;
837 
838 	if (req == NULL)
839 		return;
840 
841 	if (field_width == 0)
842 		return;
843 
844 	ncolumns = (MAX_COLUMNS)/field_width;
845 	kptr = KSTAT_NAMED_PTR(req);
846 	kptr_old = KSTAT_NAMED_PTR(req_old);
847 
848 	if (kptr_old == NULL) {
849 		old_tot_ops = 0;
850 		old_tot = 0;
851 	} else {
852 		old_tot =  kptr_old[0].value.ui64 + kptr_old[1].value.ui64;
853 		for (i = 2, old_tot_ops = 0; i < req_old->ks_ndata; i++)
854 			old_tot_ops += kptr_old[i].value.ui64;
855 	}
856 
857 	/* Count the number of operations sent */
858 	for (i = 2, tot_ops = 0; i < req->ks_ndata; i++)
859 		tot_ops += kptr[i].value.ui64;
860 	/* For v4 NULL/COMPOUND are the only procedures */
861 	tot = kptr[0].value.ui64 + kptr[1].value.ui64;
862 
863 	if (interval) {
864 		tot -= old_tot;
865 		tot_ops -= old_tot_ops;
866 	}
867 
868 	printf("Version 4: (%" PRIu64 " calls)\n", tot);
869 
870 	knp = kstat_data_lookup(req, "null");
871 	nreq = req->ks_ndata - (knp - KSTAT_NAMED_PTR(req));
872 
873 	for (i = 0; i < COUNT; i += ncolumns) {
874 		for (j = i; j < MIN(i + ncolumns, 2); j++) {
875 			printf("%-*s", field_width, knp[j].name);
876 		}
877 		printf("\n");
878 		for (j = i; j < MIN(i + ncolumns, 2); j++) {
879 			if (tot && interval && kptr_old != NULL)
880 				per = (int)((knp[j].value.ui64 -
881 				    kptr_old[j].value.ui64) * 100 / tot);
882 			else if (tot)
883 				per = (int)(knp[j].value.ui64 * 100 / tot);
884 			else
885 				per = 0;
886 			(void) sprintf(fixlen, "%" PRIu64 " %d%% ",
887 			    ((interval && kptr_old != NULL) ?
888 			    (knp[j].value.ui64 - kptr_old[j].value.ui64)
889 			    : knp[j].value.ui64), per);
890 			printf("%-*s", field_width, fixlen);
891 		}
892 		printf("\n");
893 	}
894 
895 	printf("Version 4: (%" PRIu64 " operations)\n", tot_ops);
896 	for (i = 2; i < nreq; i += ncolumns) {
897 		for (j = i; j < MIN(i + ncolumns, nreq); j++) {
898 			printf("%-*s", field_width, knp[j].name);
899 		}
900 		printf("\n");
901 		for (j = i; j < MIN(i + ncolumns, nreq); j++) {
902 			if (tot_ops && interval && kptr_old != NULL)
903 				per = (int)((knp[j].value.ui64 -
904 				    kptr_old[j].value.ui64) * 100 / tot_ops);
905 			else if (tot_ops)
906 				per = (int)(knp[j].value.ui64 * 100 / tot_ops);
907 			else
908 				per = 0;
909 			(void) sprintf(fixlen, "%" PRIu64 " %d%% ",
910 			    ((interval && kptr_old != NULL) ?
911 			    (knp[j].value.ui64 - kptr_old[j].value.ui64)
912 			    : knp[j].value.ui64), per);
913 			printf("%-*s", field_width, fixlen);
914 		}
915 		printf("\n");
916 	}
917 	if (zflag) {
918 		for (i = 0; i < req->ks_ndata; i++)
919 			kptr[i].value.ui64 = 0;
920 	}
921 	if (kptr_old != NULL)
922 		kstat_copy(req, req_old, 1);
923 	else
924 		kstat_copy(req, req_old, 0);
925 }
926 
927 static void
928 stat_print(const char *title_string, kstat_t *req, kstat_t  *req_old,
929     int field_width, int zflag)
930 {
931 	int i, j, nreq, ncolumns;
932 	char fixlen[128];
933 	kstat_named_t *knp;
934 	kstat_named_t *knp_old;
935 
936 	if (req == NULL)
937 		return;
938 
939 	if (field_width == 0)
940 		return;
941 
942 	printf("%s\n", title_string);
943 	ncolumns = (MAX_COLUMNS -1)/field_width;
944 
945 	/* MEANS knp =  (kstat_named_t *)req->ks_data */
946 	knp = KSTAT_NAMED_PTR(req);
947 	nreq = req->ks_ndata;
948 	knp_old = KSTAT_NAMED_PTR(req_old);
949 
950 	for (i = 0; i < nreq; i += ncolumns) {
951 		/* prints out the titles of the columns */
952 		for (j = i; j < MIN(i + ncolumns, nreq); j++) {
953 			printf("%-*s", field_width, knp[j].name);
954 		}
955 		printf("\n");
956 		/* prints out the stat numbers */
957 		for (j = i; j < MIN(i + ncolumns, nreq); j++) {
958 			(void) sprintf(fixlen, "%" PRIu64 " ",
959 			    (interval && knp_old != NULL) ?
960 			    (knp[j].value.ui64 - knp_old[j].value.ui64)
961 			    : knp[j].value.ui64);
962 			printf("%-*s", field_width, fixlen);
963 		}
964 		printf("\n");
965 
966 	}
967 	if (zflag) {
968 		for (i = 0; i < req->ks_ndata; i++)
969 			knp[i].value.ui64 = 0;
970 	}
971 
972 	if (knp_old != NULL)
973 		kstat_copy(req, req_old, 1);
974 	else
975 		kstat_copy(req, req_old, 0);
976 }
977 
978 static void
979 kstat_sum(kstat_t *kstat1, kstat_t *kstat2, kstat_t *sum)
980 {
981 	int i;
982 	kstat_named_t *knp1, *knp2, *knpsum;
983 	if (kstat1 == NULL || kstat2 == NULL)
984 		return;
985 
986 	knp1 = KSTAT_NAMED_PTR(kstat1);
987 	knp2 = KSTAT_NAMED_PTR(kstat2);
988 	if (sum->ks_data == NULL)
989 		kstat_copy(kstat1, sum, 0);
990 	knpsum = KSTAT_NAMED_PTR(sum);
991 
992 	for (i = 0; i < (kstat1->ks_ndata); i++)
993 		knpsum[i].value.ui64 =  knp1[i].value.ui64 + knp2[i].value.ui64;
994 }
995 
996 /*
997  * my_dir and my_path could be pointers
998  */
999 struct myrec {
1000 	ulong_t my_fsid;
1001 	char my_dir[MAXPATHLEN];
1002 	char *my_path;
1003 	char *ig_path;
1004 	struct myrec *next;
1005 };
1006 
1007 /*
1008  * Print the mount table info
1009  */
1010 static void
1011 mi_print(void)
1012 {
1013 	FILE *mt;
1014 	struct extmnttab m;
1015 	struct myrec *list, *mrp, *pmrp;
1016 	char *flavor;
1017 	int ignored = 0;
1018 	seconfig_t nfs_sec;
1019 	kstat_t *ksp;
1020 	struct mntinfo_kstat mik;
1021 	int transport_flag = 0;
1022 	int path_count;
1023 	int found;
1024 	char *timer_name[] = {
1025 		"Lookups",
1026 		"Reads",
1027 		"Writes",
1028 		"All"
1029 	};
1030 
1031 	mt = fopen(MNTTAB, "r");
1032 	if (mt == NULL) {
1033 		perror(MNTTAB);
1034 		exit(0);
1035 	}
1036 
1037 	list = NULL;
1038 	resetmnttab(mt);
1039 
1040 	while (getextmntent(mt, &m, sizeof (struct extmnttab)) == 0) {
1041 		/* ignore non "nfs" and save the "ignore" entries */
1042 		if (strcmp(m.mnt_fstype, MNTTYPE_NFS) != 0)
1043 			continue;
1044 		/*
1045 		 * Check to see here if user gave a path(s) to
1046 		 * only show the mount point they wanted
1047 		 * Iterate through the list of paths the user gave and see
1048 		 * if any of them match our current nfs mount
1049 		 */
1050 		if (path[0] != NULL) {
1051 			found = 0;
1052 			for (path_count = 0; path[path_count] != NULL;
1053 			    path_count++) {
1054 				if (strcmp(path[path_count], m.mnt_mountp)
1055 				    == 0) {
1056 					found = 1;
1057 					break;
1058 				}
1059 			}
1060 			if (!found)
1061 				continue;
1062 		}
1063 
1064 		if ((mrp = malloc(sizeof (struct myrec))) == 0) {
1065 			fprintf(stderr, "nfsstat: not enough memory\n");
1066 			exit(1);
1067 		}
1068 		mrp->my_fsid = makedev(m.mnt_major, m.mnt_minor);
1069 		if (ignore(m.mnt_mntopts)) {
1070 			/*
1071 			 * ignored entries cannot be ignored for this
1072 			 * option. We have to display the info for this
1073 			 * nfs mount. The ignore is an indication
1074 			 * that the actual mount point is different and
1075 			 * something is in between the nfs mount.
1076 			 * So save the mount point now
1077 			 */
1078 			if ((mrp->ig_path = malloc(
1079 			    strlen(m.mnt_mountp) + 1)) == 0) {
1080 				fprintf(stderr, "nfsstat: not enough memory\n");
1081 				exit(1);
1082 			}
1083 			(void) strcpy(mrp->ig_path, m.mnt_mountp);
1084 			ignored++;
1085 		} else {
1086 			mrp->ig_path = 0;
1087 			(void) strcpy(mrp->my_dir, m.mnt_mountp);
1088 		}
1089 		if ((mrp->my_path = strdup(m.mnt_special)) == NULL) {
1090 			fprintf(stderr, "nfsstat: not enough memory\n");
1091 			exit(1);
1092 		}
1093 		mrp->next = list;
1094 		list = mrp;
1095 	}
1096 
1097 	/*
1098 	 * If something got ignored, go to the beginning of the mnttab
1099 	 * and look for the cachefs entries since they are the one
1100 	 * causing this. The mount point saved for the ignored entries
1101 	 * is matched against the special to get the actual mount point.
1102 	 * We are interested in the acutal mount point so that the output
1103 	 * look nice too.
1104 	 */
1105 	if (ignored) {
1106 		rewind(mt);
1107 		resetmnttab(mt);
1108 		while (getextmntent(mt, &m, sizeof (struct extmnttab)) == 0) {
1109 
1110 			/* ignore non "cachefs" */
1111 			if (strcmp(m.mnt_fstype, MNTTYPE_CACHEFS) != 0)
1112 				continue;
1113 
1114 			for (mrp = list; mrp; mrp = mrp->next) {
1115 				if (mrp->ig_path == 0)
1116 					continue;
1117 				if (strcmp(mrp->ig_path, m.mnt_special) == 0) {
1118 					mrp->ig_path = 0;
1119 					(void) strcpy(mrp->my_dir,
1120 					    m.mnt_mountp);
1121 				}
1122 			}
1123 		}
1124 		/*
1125 		 * Now ignored entries which do not have
1126 		 * the my_dir initialized are really ignored; This never
1127 		 * happens unless the mnttab is corrupted.
1128 		 */
1129 		for (pmrp = 0, mrp = list; mrp; mrp = mrp->next) {
1130 			if (mrp->ig_path == 0)
1131 				pmrp = mrp;
1132 			else if (pmrp)
1133 				pmrp->next = mrp->next;
1134 			else
1135 				list = mrp->next;
1136 		}
1137 	}
1138 
1139 	(void) fclose(mt);
1140 
1141 
1142 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
1143 		int i;
1144 
1145 		if (ksp->ks_type != KSTAT_TYPE_RAW)
1146 			continue;
1147 		if (strcmp(ksp->ks_module, "nfs") != 0)
1148 			continue;
1149 		if (strcmp(ksp->ks_name, "mntinfo") != 0)
1150 			continue;
1151 
1152 		for (mrp = list; mrp; mrp = mrp->next) {
1153 			if ((mrp->my_fsid & MAXMIN) == ksp->ks_instance)
1154 				break;
1155 		}
1156 		if (mrp == 0)
1157 			continue;
1158 
1159 		if (safe_kstat_read(kc, ksp, &mik) == -1)
1160 			continue;
1161 
1162 		printf("%s from %s\n", mrp->my_dir, mrp->my_path);
1163 
1164 		/*
1165 		 * for printing rdma transport and provider string.
1166 		 * This way we avoid modifying the kernel mntinfo_kstat
1167 		 * struct for protofmly.
1168 		 */
1169 		if (strcmp(mik.mik_proto, "ibtf") == 0) {
1170 			printf(" Flags:		vers=%u,proto=rdma",
1171 			    mik.mik_vers);
1172 			transport_flag = 1;
1173 		} else {
1174 			printf(" Flags:		vers=%u,proto=%s",
1175 			    mik.mik_vers, mik.mik_proto);
1176 			transport_flag = 0;
1177 		}
1178 
1179 		/*
1180 		 *  get the secmode name from /etc/nfssec.conf.
1181 		 */
1182 		if (!nfs_getseconfig_bynumber(mik.mik_secmod, &nfs_sec)) {
1183 			flavor = nfs_sec.sc_name;
1184 		} else
1185 			flavor = NULL;
1186 
1187 		if (flavor != NULL)
1188 			printf(",sec=%s", flavor);
1189 		else
1190 			printf(",sec#=%d", mik.mik_secmod);
1191 
1192 		printf(",%s", (mik.mik_flags & MI_HARD) ? "hard" : "soft");
1193 		if (mik.mik_flags & MI_PRINTED)
1194 			printf(",printed");
1195 		printf(",%s", (mik.mik_flags & MI_INT) ? "intr" : "nointr");
1196 		if (mik.mik_flags & MI_DOWN)
1197 			printf(",down");
1198 		if (mik.mik_flags & MI_NOAC)
1199 			printf(",noac");
1200 		if (mik.mik_flags & MI_NOCTO)
1201 			printf(",nocto");
1202 		if (mik.mik_flags & MI_DYNAMIC)
1203 			printf(",dynamic");
1204 		if (mik.mik_flags & MI_LLOCK)
1205 			printf(",llock");
1206 		if (mik.mik_flags & MI_GRPID)
1207 			printf(",grpid");
1208 		if (mik.mik_flags & MI_RPCTIMESYNC)
1209 			printf(",rpctimesync");
1210 		if (mik.mik_flags & MI_LINK)
1211 			printf(",link");
1212 		if (mik.mik_flags & MI_SYMLINK)
1213 			printf(",symlink");
1214 		if (mik.mik_vers < NFS_V4 && mik.mik_flags & MI_READDIRONLY)
1215 			printf(",readdironly");
1216 		if (mik.mik_flags & MI_ACL)
1217 			printf(",acl");
1218 
1219 		if (mik.mik_vers >= NFS_V4) {
1220 			if (mik.mik_flags & MI4_MIRRORMOUNT)
1221 				printf(",mirrormount");
1222 		}
1223 
1224 		printf(",rsize=%d,wsize=%d,retrans=%d,timeo=%d",
1225 		    mik.mik_curread, mik.mik_curwrite, mik.mik_retrans,
1226 		    mik.mik_timeo);
1227 		printf("\n");
1228 		printf(" Attr cache:	acregmin=%d,acregmax=%d"
1229 		    ",acdirmin=%d,acdirmax=%d\n", mik.mik_acregmin,
1230 		    mik.mik_acregmax, mik.mik_acdirmin, mik.mik_acdirmax);
1231 
1232 		if (transport_flag) {
1233 			printf(" Transport:	proto=rdma, plugin=%s\n",
1234 			    mik.mik_proto);
1235 		}
1236 
1237 #define	srtt_to_ms(x) x, (x * 2 + x / 2)
1238 #define	dev_to_ms(x) x, (x * 5)
1239 
1240 		for (i = 0; i < NFS_CALLTYPES + 1; i++) {
1241 			int j;
1242 
1243 			j = (i == NFS_CALLTYPES ? i - 1 : i);
1244 			if (mik.mik_timers[j].srtt ||
1245 			    mik.mik_timers[j].rtxcur) {
1246 				printf(" %s:     srtt=%d (%dms), "
1247 				    "dev=%d (%dms), cur=%u (%ums)\n",
1248 				    timer_name[i],
1249 				    srtt_to_ms(mik.mik_timers[i].srtt),
1250 				    dev_to_ms(mik.mik_timers[i].deviate),
1251 				    mik.mik_timers[i].rtxcur,
1252 				    mik.mik_timers[i].rtxcur * 20);
1253 			}
1254 		}
1255 
1256 		if (strchr(mrp->my_path, ','))
1257 			printf(
1258 			    " Failover:	noresponse=%d,failover=%d,"
1259 			    "remap=%d,currserver=%s\n",
1260 			    mik.mik_noresponse, mik.mik_failover,
1261 			    mik.mik_remap, mik.mik_curserver);
1262 		printf("\n");
1263 	}
1264 }
1265 
1266 static char *mntopts[] = { MNTOPT_IGNORE, MNTOPT_DEV, NULL };
1267 #define	IGNORE  0
1268 #define	DEV	1
1269 
1270 /*
1271  * Return 1 if "ignore" appears in the options string
1272  */
1273 static int
1274 ignore(char *opts)
1275 {
1276 	char *value;
1277 	char *s;
1278 
1279 	if (opts == NULL)
1280 		return (0);
1281 	s = strdup(opts);
1282 	if (s == NULL)
1283 		return (0);
1284 	opts = s;
1285 
1286 	while (*opts != '\0') {
1287 		if (getsubopt(&opts, mntopts, &value) == IGNORE) {
1288 			free(s);
1289 			return (1);
1290 		}
1291 	}
1292 
1293 	free(s);
1294 	return (0);
1295 }
1296 
1297 void
1298 usage(void)
1299 {
1300 	fprintf(stderr, "Usage: nfsstat [-cnrsza [-v version] "
1301 	    "[interval [count]]\n");
1302 	fprintf(stderr, "Usage: nfsstat -m [pathname..]\n");
1303 	exit(1);
1304 }
1305 
1306 static void
1307 fail(int do_perror, char *message, ...)
1308 {
1309 	va_list args;
1310 
1311 	va_start(args, message);
1312 	fprintf(stderr, "nfsstat: ");
1313 	vfprintf(stderr, message, args);
1314 	va_end(args);
1315 	if (do_perror)
1316 		fprintf(stderr, ": %s", strerror(errno));
1317 	fprintf(stderr, "\n");
1318 	exit(1);
1319 }
1320 
1321 kid_t
1322 safe_kstat_read(kstat_ctl_t *kc, kstat_t *ksp, void *data)
1323 {
1324 	kid_t kstat_chain_id = kstat_read(kc, ksp, data);
1325 
1326 	if (kstat_chain_id == -1)
1327 		fail(1, "kstat_read(%x, '%s') failed", kc, ksp->ks_name);
1328 	return (kstat_chain_id);
1329 }
1330 
1331 kid_t
1332 safe_kstat_write(kstat_ctl_t *kc, kstat_t *ksp, void *data)
1333 {
1334 	kid_t kstat_chain_id = 0;
1335 
1336 	if (ksp->ks_data != NULL) {
1337 		kstat_chain_id = kstat_write(kc, ksp, data);
1338 
1339 		if (kstat_chain_id == -1)
1340 			fail(1, "kstat_write(%x, '%s') failed", kc,
1341 			    ksp->ks_name);
1342 	}
1343 	return (kstat_chain_id);
1344 }
1345 
1346 void
1347 stats_timer(int interval)
1348 {
1349 	timer_t t_id;
1350 	itimerspec_t time_struct;
1351 	struct sigevent sig_struct;
1352 	struct sigaction act;
1353 
1354 	bzero(&sig_struct, sizeof (struct sigevent));
1355 	bzero(&act, sizeof (struct sigaction));
1356 
1357 	/* Create timer */
1358 	sig_struct.sigev_notify = SIGEV_SIGNAL;
1359 	sig_struct.sigev_signo = SIGUSR1;
1360 	sig_struct.sigev_value.sival_int = 0;
1361 
1362 	if (timer_create(CLOCK_REALTIME, &sig_struct, &t_id) != 0) {
1363 		fail(1, "Timer creation failed");
1364 	}
1365 
1366 	act.sa_handler = handle_sig;
1367 
1368 	if (sigaction(SIGUSR1, &act, NULL) != 0) {
1369 		fail(1, "Could not set up signal handler");
1370 	}
1371 
1372 	time_struct.it_value.tv_sec = interval;
1373 	time_struct.it_value.tv_nsec = 0;
1374 	time_struct.it_interval.tv_sec = interval;
1375 	time_struct.it_interval.tv_nsec = 0;
1376 
1377 	/* Arm timer */
1378 	if ((timer_settime(t_id, 0, &time_struct, NULL)) != 0) {
1379 		fail(1, "Setting timer failed");
1380 	}
1381 }
1382 
1383 void
1384 handle_sig(int x)
1385 {
1386 }
1387 
1388 static void
1389 kstat_copy(kstat_t *src, kstat_t *dst, int fr)
1390 {
1391 
1392 	if (fr)
1393 		free(dst->ks_data);
1394 
1395 	*dst = *src;
1396 
1397 	if (src->ks_data != NULL) {
1398 		safe_zalloc(&dst->ks_data, src->ks_data_size, 0);
1399 		(void) memcpy(dst->ks_data, src->ks_data, src->ks_data_size);
1400 	} else {
1401 		dst->ks_data = NULL;
1402 		dst->ks_data_size = 0;
1403 	}
1404 }
1405 
1406 /*
1407  * "Safe" allocators - if we return we're guaranteed
1408  * to have the desired space. We exit via fail
1409  * if we can't get the space.
1410  */
1411 void
1412 safe_zalloc(void **ptr, uint_t size, int free_first)
1413 {
1414 	if (*ptr == NULL)
1415 		fail(1, "invalid pointer");
1416 	if (free_first && *ptr != NULL)
1417 		free(*ptr);
1418 	if ((*ptr = (void *)malloc(size)) == NULL)
1419 		fail(1, "malloc failed");
1420 	(void) memset(*ptr, 0, size);
1421 }
1422 
1423 static int
1424 safe_strtoi(char const *val, char *errmsg)
1425 {
1426 	char *end;
1427 	long tmp;
1428 	errno = 0;
1429 	tmp = strtol(val, &end, 10);
1430 	if (*end != '\0' || errno)
1431 		fail(0, "%s %s", errmsg, val);
1432 	return ((int)tmp);
1433 }
1434