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