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