1*1488Srsb /* 2*1488Srsb * CDDL HEADER START 3*1488Srsb * 4*1488Srsb * The contents of this file are subject to the terms of the 5*1488Srsb * Common Development and Distribution License (the "License"). 6*1488Srsb * You may not use this file except in compliance with the License. 7*1488Srsb * 8*1488Srsb * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*1488Srsb * or http://www.opensolaris.org/os/licensing. 10*1488Srsb * See the License for the specific language governing permissions 11*1488Srsb * and limitations under the License. 12*1488Srsb * 13*1488Srsb * When distributing Covered Code, include this CDDL HEADER in each 14*1488Srsb * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*1488Srsb * If applicable, add the following below this CDDL HEADER, with the 16*1488Srsb * fields enclosed by brackets "[]" replaced with your own identifying 17*1488Srsb * information: Portions Copyright [yyyy] [name of copyright owner] 18*1488Srsb * 19*1488Srsb * CDDL HEADER END 20*1488Srsb */ 21*1488Srsb /* 22*1488Srsb * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23*1488Srsb * Use is subject to license terms. 24*1488Srsb */ 25*1488Srsb 26*1488Srsb #pragma ident "%Z%%M% %I% %E% SMI" 27*1488Srsb 28*1488Srsb #include <stdio.h> 29*1488Srsb #include <kstat.h> 30*1488Srsb #include <stdlib.h> 31*1488Srsb #include <string.h> 32*1488Srsb #include <strings.h> 33*1488Srsb #include <errno.h> 34*1488Srsb #include <limits.h> 35*1488Srsb #include <sys/types.h> 36*1488Srsb #include <time.h> 37*1488Srsb #include <sys/time.h> 38*1488Srsb #include <sys/uio.h> 39*1488Srsb #include <sys/vnode.h> 40*1488Srsb #include <sys/vfs.h> 41*1488Srsb #include <sys/statvfs.h> 42*1488Srsb #include <sys/fstyp.h> 43*1488Srsb #include <sys/fsid.h> 44*1488Srsb #include <sys/mnttab.h> 45*1488Srsb #include <values.h> 46*1488Srsb #include <poll.h> 47*1488Srsb #include <ctype.h> 48*1488Srsb #include <libintl.h> 49*1488Srsb 50*1488Srsb #define OPTIONS "PT:afginv" 51*1488Srsb 52*1488Srsb /* Time stamp values */ 53*1488Srsb #define NODATE 0 /* Default: No time stamp */ 54*1488Srsb #define DDATE 1 /* Standard date format */ 55*1488Srsb #define UDATE 2 /* Internal representation of Unix time */ 56*1488Srsb 57*1488Srsb #define RETRY_DELAY 250 /* Timeout for poll() */ 58*1488Srsb #define HEADERLINES 22 /* Number of lines between display headers */ 59*1488Srsb 60*1488Srsb #define LBUFSZ 64 /* Generic size for local buffer */ 61*1488Srsb 62*1488Srsb /* 63*1488Srsb * The following are used for the nicenum() function 64*1488Srsb */ 65*1488Srsb #define KILO_VAL 1024 66*1488Srsb #define ONE_INDEX 3 67*1488Srsb 68*1488Srsb #define NENTITY_INIT 1 /* Initial number of entities to allocate */ 69*1488Srsb 70*1488Srsb /* 71*1488Srsb * We need to have a mechanism for an old/previous and new/current vopstat 72*1488Srsb * structure. We only need two per entity and we can swap between them. 73*1488Srsb */ 74*1488Srsb #define VS_SIZE 2 /* Size of vopstat array */ 75*1488Srsb #define CUR_INDEX (vs_i) 76*1488Srsb #define PREV_INDEX ((vs_i == 0) ? 1 : 0) /* Opposite of CUR_INDEX */ 77*1488Srsb #define BUMP_INDEX() vs_i = ((vs_i == 0) ? 1 : 0) 78*1488Srsb 79*1488Srsb /* 80*1488Srsb * An "entity" is anything we're collecting statistics on, it could 81*1488Srsb * be a mountpoint or an FS-type. 82*1488Srsb * e_name is the name of the entity (e.g. mount point or FS-type) 83*1488Srsb * e_ksname is the name of the associated kstat 84*1488Srsb * e_vs is an array of vopstats. This is used to keep track of "previous" 85*1488Srsb * and "current" vopstats. 86*1488Srsb */ 87*1488Srsb typedef struct entity { 88*1488Srsb char *e_name; /* name of entity */ 89*1488Srsb vopstats_t *e_vs; /* Array of vopstats */ 90*1488Srsb ulong_t e_fsid; /* fsid for ENTYPE_MNTPT only */ 91*1488Srsb int e_type; /* type of entity */ 92*1488Srsb char e_ksname[KSTAT_STRLEN]; /* kstat name */ 93*1488Srsb } entity_t; 94*1488Srsb 95*1488Srsb /* Types of entities (e_type) */ 96*1488Srsb #define ENTYPE_UNKNOWN 0 /* UNKNOWN must be zero since we calloc() */ 97*1488Srsb #define ENTYPE_FSTYPE 1 98*1488Srsb #define ENTYPE_MNTPT 2 99*1488Srsb 100*1488Srsb /* If more sub-one units are added, make sure to adjust ONE_INDEX above */ 101*1488Srsb static char units[] = "num KMGTPE"; 102*1488Srsb 103*1488Srsb static char *cmdname; /* name of this command */ 104*1488Srsb 105*1488Srsb static int vs_i = 0; /* Index of current vs[] slot */ 106*1488Srsb 107*1488Srsb static void 108*1488Srsb usage() 109*1488Srsb { 110*1488Srsb (void) fprintf(stderr, 111*1488Srsb gettext("Usage: %s [-a|f|i|n|v] [-P] [ fstype | fspath ]... " 112*1488Srsb "[interval [count]]\n"), cmdname); 113*1488Srsb exit(2); 114*1488Srsb } 115*1488Srsb 116*1488Srsb /* 117*1488Srsb * Given a 64-bit number and a starting unit (e.g., n - nanoseconds), 118*1488Srsb * convert the number to a 5-character representation including any 119*1488Srsb * decimal point and single-character unit. Put that representation 120*1488Srsb * into the array "buf" (which had better be big enough). 121*1488Srsb */ 122*1488Srsb char * 123*1488Srsb nicenum(uint64_t num, char unit, char *buf) 124*1488Srsb { 125*1488Srsb uint64_t n = num; 126*1488Srsb int unit_index; 127*1488Srsb int index; 128*1488Srsb char u; 129*1488Srsb 130*1488Srsb /* If the user passed in a NUL/zero unit, use the blank value for 1 */ 131*1488Srsb if (unit == '\0') 132*1488Srsb unit = ' '; 133*1488Srsb 134*1488Srsb unit_index = 0; 135*1488Srsb while (units[unit_index] != unit) { 136*1488Srsb unit_index++; 137*1488Srsb if (unit_index > sizeof (units) - 1) { 138*1488Srsb (void) sprintf(buf, "??"); 139*1488Srsb return (buf); 140*1488Srsb } 141*1488Srsb } 142*1488Srsb 143*1488Srsb index = 0; 144*1488Srsb while (n >= KILO_VAL) { 145*1488Srsb n = (n + (KILO_VAL / 2)) / KILO_VAL; /* Round up or down */ 146*1488Srsb index++; 147*1488Srsb unit_index++; 148*1488Srsb } 149*1488Srsb 150*1488Srsb if (unit_index >= sizeof (units) - 1) { 151*1488Srsb (void) sprintf(buf, "??"); 152*1488Srsb return (buf); 153*1488Srsb } 154*1488Srsb 155*1488Srsb u = units[unit_index]; 156*1488Srsb 157*1488Srsb if (unit_index == ONE_INDEX) { 158*1488Srsb (void) sprintf(buf, "%llu", (u_longlong_t)n); 159*1488Srsb } else if (n < 10 && (num & (num - 1)) != 0) { 160*1488Srsb (void) sprintf(buf, "%.2f%c", 161*1488Srsb (double)num / (1ULL << 10 * index), u); 162*1488Srsb } else if (n < 100 && (num & (num - 1)) != 0) { 163*1488Srsb (void) sprintf(buf, "%.1f%c", 164*1488Srsb (double)num / (1ULL << 10 * index), u); 165*1488Srsb } else { 166*1488Srsb (void) sprintf(buf, "%llu%c", (u_longlong_t)n, u); 167*1488Srsb } 168*1488Srsb 169*1488Srsb return (buf); 170*1488Srsb } 171*1488Srsb 172*1488Srsb 173*1488Srsb #define RAWVAL(ptr, member) ((ptr)->member.value.ui64) 174*1488Srsb #define DELTA(member) \ 175*1488Srsb (newvsp->member.value.ui64 - (oldvsp ? oldvsp->member.value.ui64 : 0)) 176*1488Srsb #define OLDPRINTSTAT(isnice, nicestring, niceval, rawstring, rawval) \ 177*1488Srsb (isnice) ? \ 178*1488Srsb (void) printf((nicestring), (niceval)) \ 179*1488Srsb : \ 180*1488Srsb (void) printf((rawstring), (rawval)) 181*1488Srsb #define PRINTSTAT(isnice, nicestring, rawstring, rawval, unit, buf) \ 182*1488Srsb (isnice) ? \ 183*1488Srsb (void) printf((nicestring), nicenum(rawval, unit, buf)) \ 184*1488Srsb : \ 185*1488Srsb (void) printf((rawstring), (rawval)) 186*1488Srsb 187*1488Srsb /* Values for display flag */ 188*1488Srsb #define DISP_HEADER 0x1 189*1488Srsb #define DISP_RAW 0x2 190*1488Srsb 191*1488Srsb /* 192*1488Srsb * The policy for dealing with multiple flags is dealt with here. 193*1488Srsb * Currently, if we are displaying raw output, then don't allow 194*1488Srsb * headers to be printed. 195*1488Srsb */ 196*1488Srsb int 197*1488Srsb dispflag_policy(int printhdr, int dispflag) 198*1488Srsb { 199*1488Srsb /* If we're not displaying raw output, then allow headers to print */ 200*1488Srsb if ((dispflag & DISP_RAW) == 0) { 201*1488Srsb if (printhdr) { 202*1488Srsb dispflag |= DISP_HEADER; 203*1488Srsb } 204*1488Srsb } 205*1488Srsb 206*1488Srsb return (dispflag); 207*1488Srsb } 208*1488Srsb 209*1488Srsb static void 210*1488Srsb dflt_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 211*1488Srsb { 212*1488Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 213*1488Srsb longlong_t nnewfile; 214*1488Srsb longlong_t nnamerm; 215*1488Srsb longlong_t nnamechg; 216*1488Srsb longlong_t nattrret; 217*1488Srsb longlong_t nattrchg; 218*1488Srsb longlong_t nlookup; 219*1488Srsb longlong_t nreaddir; 220*1488Srsb longlong_t ndataread; 221*1488Srsb longlong_t ndatawrite; 222*1488Srsb longlong_t readthruput; 223*1488Srsb longlong_t writethruput; 224*1488Srsb char buf[LBUFSZ]; 225*1488Srsb 226*1488Srsb nnewfile = DELTA(ncreate) + DELTA(nmkdir) + DELTA(nsymlink); 227*1488Srsb nnamerm = DELTA(nremove) + DELTA(nrmdir); 228*1488Srsb nnamechg = DELTA(nrename) + DELTA(nlink) + DELTA(nsymlink); 229*1488Srsb nattrret = DELTA(ngetattr) + DELTA(naccess) + 230*1488Srsb DELTA(ngetsecattr) + DELTA(nfid); 231*1488Srsb nattrchg = DELTA(nsetattr) + DELTA(nsetsecattr) + DELTA(nspace); 232*1488Srsb nlookup = DELTA(nlookup); 233*1488Srsb nreaddir = DELTA(nreaddir); 234*1488Srsb ndataread = DELTA(nread); 235*1488Srsb ndatawrite = DELTA(nwrite); 236*1488Srsb readthruput = DELTA(read_bytes); 237*1488Srsb writethruput = DELTA(write_bytes); 238*1488Srsb 239*1488Srsb if (dispflag & DISP_HEADER) { 240*1488Srsb (void) printf(gettext( 241*1488Srsb " new name name attr attr lookup rddir read read write write\n" 242*1488Srsb " file remov chng get set ops ops ops bytes ops bytes\n")); 243*1488Srsb } 244*1488Srsb 245*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnewfile, ' ', buf); 246*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnamerm, ' ', buf); 247*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnamechg, ' ', buf); 248*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nattrret, ' ', buf); 249*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nattrchg, ' ', buf); 250*1488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", nlookup, ' ', buf); 251*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nreaddir, ' ', buf); 252*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", ndataread, ' ', buf); 253*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", readthruput, ' ', buf); 254*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", ndatawrite, ' ', buf); 255*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", writethruput, ' ', buf); 256*1488Srsb (void) printf("%s\n", name); 257*1488Srsb } 258*1488Srsb 259*1488Srsb static void 260*1488Srsb io_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 261*1488Srsb { 262*1488Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 263*1488Srsb char buf[LBUFSZ]; 264*1488Srsb 265*1488Srsb if (dispflag & DISP_HEADER) { 266*1488Srsb (void) printf(gettext( 267*1488Srsb " read read write write rddir rddir rwlock rwulock\n" 268*1488Srsb " ops bytes ops bytes ops bytes ops ops\n")); 269*1488Srsb } 270*1488Srsb 271*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nread), ' ', buf); 272*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(read_bytes), ' ', buf); 273*1488Srsb 274*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nwrite), ' ', buf); 275*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(write_bytes), ' ', buf); 276*1488Srsb 277*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf); 278*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(readdir_bytes), ' ', buf); 279*1488Srsb 280*1488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nrwlock), ' ', buf); 281*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrwunlock), ' ', buf); 282*1488Srsb 283*1488Srsb (void) printf("%s\n", name); 284*1488Srsb } 285*1488Srsb 286*1488Srsb static void 287*1488Srsb vm_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 288*1488Srsb { 289*1488Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 290*1488Srsb char buf[LBUFSZ]; 291*1488Srsb 292*1488Srsb if (dispflag & DISP_HEADER) { 293*1488Srsb (void) printf( 294*1488Srsb gettext(" map addmap delmap getpag putpag pagio\n")); 295*1488Srsb } 296*1488Srsb 297*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmap), ' ', buf); 298*1488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(naddmap), ' ', buf); 299*1488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ndelmap), ' ', buf); 300*1488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetpage), ' ', buf); 301*1488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nputpage), ' ', buf); 302*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(npageio), ' ', buf); 303*1488Srsb (void) printf("%s\n", name); 304*1488Srsb } 305*1488Srsb 306*1488Srsb static void 307*1488Srsb attr_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 308*1488Srsb { 309*1488Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 310*1488Srsb char buf[LBUFSZ]; 311*1488Srsb 312*1488Srsb if (dispflag & DISP_HEADER) { 313*1488Srsb (void) printf(gettext("getattr setattr getsec setsec\n")); 314*1488Srsb } 315*1488Srsb 316*1488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetattr), ' ', buf); 317*1488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetattr), ' ', buf); 318*1488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetsecattr), ' ', buf); 319*1488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetsecattr), ' ', buf); 320*1488Srsb 321*1488Srsb (void) printf("%s\n", name); 322*1488Srsb } 323*1488Srsb 324*1488Srsb static void 325*1488Srsb naming_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 326*1488Srsb { 327*1488Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 328*1488Srsb char buf[LBUFSZ]; 329*1488Srsb 330*1488Srsb if (dispflag & DISP_HEADER) { 331*1488Srsb (void) printf(gettext( 332*1488Srsb "lookup creat remov link renam mkdir rmdir rddir symlnk rdlnk\n")); 333*1488Srsb } 334*1488Srsb 335*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlookup), ' ', buf); 336*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(ncreate), ' ', buf); 337*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nremove), ' ', buf); 338*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlink), ' ', buf); 339*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrename), ' ', buf); 340*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmkdir), ' ', buf); 341*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrmdir), ' ', buf); 342*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf); 343*1488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsymlink), ' ', buf); 344*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreadlink), ' ', buf); 345*1488Srsb (void) printf("%s\n", name); 346*1488Srsb } 347*1488Srsb 348*1488Srsb 349*1488Srsb #define PRINT_VOPSTAT_CMN(niceflag, vop) \ 350*1488Srsb if (niceflag) \ 351*1488Srsb (void) printf("%10s ", #vop); \ 352*1488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(n##vop), ' ', buf); 353*1488Srsb 354*1488Srsb #define PRINT_VOPSTAT(niceflag, vop) \ 355*1488Srsb PRINT_VOPSTAT_CMN(niceflag, vop); \ 356*1488Srsb if (niceflag) \ 357*1488Srsb (void) printf("\n"); 358*1488Srsb 359*1488Srsb #define PRINT_VOPSTAT_IO(niceflag, vop) \ 360*1488Srsb PRINT_VOPSTAT_CMN(niceflag, vop); \ 361*1488Srsb PRINTSTAT(niceflag, " %5s\n", "%lld:", \ 362*1488Srsb DELTA(vop##_bytes), ' ', buf); 363*1488Srsb 364*1488Srsb static void 365*1488Srsb vop_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 366*1488Srsb { 367*1488Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 368*1488Srsb char buf[LBUFSZ]; 369*1488Srsb 370*1488Srsb if (niceflag) { 371*1488Srsb (void) printf("%s\n", name); 372*1488Srsb (void) printf(gettext(" operation #ops bytes\n")); 373*1488Srsb } 374*1488Srsb 375*1488Srsb PRINT_VOPSTAT(niceflag, open); 376*1488Srsb PRINT_VOPSTAT(niceflag, close); 377*1488Srsb PRINT_VOPSTAT_IO(niceflag, read); 378*1488Srsb PRINT_VOPSTAT_IO(niceflag, write); 379*1488Srsb PRINT_VOPSTAT(niceflag, ioctl); 380*1488Srsb PRINT_VOPSTAT(niceflag, setfl); 381*1488Srsb PRINT_VOPSTAT(niceflag, getattr); 382*1488Srsb PRINT_VOPSTAT(niceflag, setattr); 383*1488Srsb PRINT_VOPSTAT(niceflag, access); 384*1488Srsb PRINT_VOPSTAT(niceflag, lookup); 385*1488Srsb PRINT_VOPSTAT(niceflag, create); 386*1488Srsb PRINT_VOPSTAT(niceflag, remove); 387*1488Srsb PRINT_VOPSTAT(niceflag, link); 388*1488Srsb PRINT_VOPSTAT(niceflag, rename); 389*1488Srsb PRINT_VOPSTAT(niceflag, mkdir); 390*1488Srsb PRINT_VOPSTAT(niceflag, rmdir); 391*1488Srsb PRINT_VOPSTAT_IO(niceflag, readdir); 392*1488Srsb PRINT_VOPSTAT(niceflag, symlink); 393*1488Srsb PRINT_VOPSTAT(niceflag, readlink); 394*1488Srsb PRINT_VOPSTAT(niceflag, fsync); 395*1488Srsb PRINT_VOPSTAT(niceflag, inactive); 396*1488Srsb PRINT_VOPSTAT(niceflag, fid); 397*1488Srsb PRINT_VOPSTAT(niceflag, rwlock); 398*1488Srsb PRINT_VOPSTAT(niceflag, rwunlock); 399*1488Srsb PRINT_VOPSTAT(niceflag, seek); 400*1488Srsb PRINT_VOPSTAT(niceflag, cmp); 401*1488Srsb PRINT_VOPSTAT(niceflag, frlock); 402*1488Srsb PRINT_VOPSTAT(niceflag, space); 403*1488Srsb PRINT_VOPSTAT(niceflag, realvp); 404*1488Srsb PRINT_VOPSTAT(niceflag, getpage); 405*1488Srsb PRINT_VOPSTAT(niceflag, putpage); 406*1488Srsb PRINT_VOPSTAT(niceflag, map); 407*1488Srsb PRINT_VOPSTAT(niceflag, addmap); 408*1488Srsb PRINT_VOPSTAT(niceflag, delmap); 409*1488Srsb PRINT_VOPSTAT(niceflag, poll); 410*1488Srsb PRINT_VOPSTAT(niceflag, dump); 411*1488Srsb PRINT_VOPSTAT(niceflag, pathconf); 412*1488Srsb PRINT_VOPSTAT(niceflag, pageio); 413*1488Srsb PRINT_VOPSTAT(niceflag, dumpctl); 414*1488Srsb PRINT_VOPSTAT(niceflag, dispose); 415*1488Srsb PRINT_VOPSTAT(niceflag, getsecattr); 416*1488Srsb PRINT_VOPSTAT(niceflag, setsecattr); 417*1488Srsb PRINT_VOPSTAT(niceflag, shrlock); 418*1488Srsb PRINT_VOPSTAT(niceflag, vnevent); 419*1488Srsb 420*1488Srsb if (niceflag) { 421*1488Srsb /* Make it easier on the eyes */ 422*1488Srsb (void) printf("\n"); 423*1488Srsb } else { 424*1488Srsb (void) printf("%s\n", name); 425*1488Srsb } 426*1488Srsb } 427*1488Srsb 428*1488Srsb 429*1488Srsb /* 430*1488Srsb * Retrieve the vopstats. If kspp (pointer to kstat_t pointer) is non-NULL, 431*1488Srsb * then pass it back to the caller. 432*1488Srsb * 433*1488Srsb * Returns 0 on success, non-zero on failure. 434*1488Srsb */ 435*1488Srsb int 436*1488Srsb get_vopstats(kstat_ctl_t *kc, char *ksname, vopstats_t *vsp, kstat_t **kspp) 437*1488Srsb { 438*1488Srsb kstat_t *ksp; 439*1488Srsb 440*1488Srsb if (ksname == NULL || *ksname == 0) 441*1488Srsb return (1); 442*1488Srsb 443*1488Srsb errno = 0; 444*1488Srsb /* wait for a possibly up-to-date chain */ 445*1488Srsb while (kstat_chain_update(kc) == -1) { 446*1488Srsb if (errno == EAGAIN) { 447*1488Srsb errno = 0; 448*1488Srsb (void) poll(NULL, 0, RETRY_DELAY); 449*1488Srsb continue; 450*1488Srsb } 451*1488Srsb perror(gettext("kstat_chain_update")); 452*1488Srsb exit(1); 453*1488Srsb } 454*1488Srsb 455*1488Srsb if ((ksp = kstat_lookup(kc, NULL, -1, ksname)) == NULL) { 456*1488Srsb return (1); 457*1488Srsb } 458*1488Srsb 459*1488Srsb if (kstat_read(kc, ksp, vsp) == -1) { 460*1488Srsb return (1); 461*1488Srsb } 462*1488Srsb 463*1488Srsb if (kspp) 464*1488Srsb *kspp = ksp; 465*1488Srsb 466*1488Srsb return (0); 467*1488Srsb } 468*1488Srsb 469*1488Srsb /* 470*1488Srsb * Given a file system type name, determine if it's part of the 471*1488Srsb * exception list of file systems that are not to be displayed. 472*1488Srsb */ 473*1488Srsb int 474*1488Srsb is_exception(char *fsname) 475*1488Srsb { 476*1488Srsb char **xlp; /* Pointer into the exception list */ 477*1488Srsb 478*1488Srsb static char *exception_list[] = { 479*1488Srsb "specfs", 480*1488Srsb "fifofs", 481*1488Srsb "fd", 482*1488Srsb "swapfs", 483*1488Srsb "ctfs", 484*1488Srsb "objfs", 485*1488Srsb "nfsdyn", 486*1488Srsb NULL 487*1488Srsb }; 488*1488Srsb 489*1488Srsb for (xlp = &exception_list[0]; *xlp != NULL; xlp++) { 490*1488Srsb if (strcmp(fsname, *xlp) == 0) 491*1488Srsb return (1); 492*1488Srsb } 493*1488Srsb 494*1488Srsb return (0); 495*1488Srsb } 496*1488Srsb 497*1488Srsb /* 498*1488Srsb * Plain and simple, build an array of names for fstypes 499*1488Srsb * Returns 0, if it encounters a problem. 500*1488Srsb */ 501*1488Srsb int 502*1488Srsb build_fstype_list(char ***fstypep) 503*1488Srsb { 504*1488Srsb int i; 505*1488Srsb int nfstype; 506*1488Srsb char buf[FSTYPSZ + 1]; 507*1488Srsb 508*1488Srsb if ((nfstype = sysfs(GETNFSTYP)) < 0) { 509*1488Srsb perror(gettext("sysfs(GETNFSTYP)")); 510*1488Srsb return (0); 511*1488Srsb } 512*1488Srsb 513*1488Srsb if ((*fstypep = calloc(nfstype, sizeof (char *))) == NULL) { 514*1488Srsb perror(gettext("calloc on fstypes")); 515*1488Srsb return (0); 516*1488Srsb } 517*1488Srsb 518*1488Srsb for (i = 1; i < nfstype; i++) { 519*1488Srsb if (sysfs(GETFSTYP, i, buf) < 0) { 520*1488Srsb perror(gettext("sysfs(GETFSTYP)")); 521*1488Srsb return (0); 522*1488Srsb } 523*1488Srsb 524*1488Srsb if (buf[0] == 0) 525*1488Srsb continue; 526*1488Srsb 527*1488Srsb /* If this is part of the exception list, move on */ 528*1488Srsb if (is_exception(buf)) 529*1488Srsb continue; 530*1488Srsb 531*1488Srsb if (((*fstypep)[i] = strdup(buf)) == NULL) { 532*1488Srsb perror(gettext("strdup() of fstype name")); 533*1488Srsb return (0); 534*1488Srsb } 535*1488Srsb } 536*1488Srsb 537*1488Srsb return (i); 538*1488Srsb } 539*1488Srsb 540*1488Srsb /* 541*1488Srsb * After we're done with getopts(), process the rest of the 542*1488Srsb * operands. We have three cases and this is the priority: 543*1488Srsb * 544*1488Srsb * 1) [ operand... ] interval count 545*1488Srsb * 2) [ operand... ] interval 546*1488Srsb * 3) [ operand... ] 547*1488Srsb * 548*1488Srsb * The trick is that any of the operands might start with a number or even 549*1488Srsb * be made up exclusively of numbers (and we have to handle negative numbers 550*1488Srsb * in case a user/script gets out of line). If we find two operands at the 551*1488Srsb * end of the list then we claim case 1. If we find only one operand at the 552*1488Srsb * end made up only of number, then we claim case 2. Otherwise, case 3. 553*1488Srsb * BTW, argc, argv don't change. 554*1488Srsb */ 555*1488Srsb int 556*1488Srsb parse_operands( 557*1488Srsb int argc, 558*1488Srsb char **argv, 559*1488Srsb int optind, 560*1488Srsb long *interval, 561*1488Srsb long *count, 562*1488Srsb entity_t **entityp) /* Array of stat-able entities */ 563*1488Srsb { 564*1488Srsb int nentities = 0; /* Number of entities found */ 565*1488Srsb int out_of_range; /* Set if 2nd-to-last operand out-of-range */ 566*1488Srsb 567*1488Srsb if (argc == optind) 568*1488Srsb return (nentities); /* None found, returns 0 */ 569*1488Srsb /* 570*1488Srsb * We know exactly what the maximum number of entities is going 571*1488Srsb * to be: argc - optind 572*1488Srsb */ 573*1488Srsb if ((*entityp = calloc((argc - optind), sizeof (entity_t))) == NULL) { 574*1488Srsb perror(gettext("calloc")); 575*1488Srsb return (-1); 576*1488Srsb } 577*1488Srsb 578*1488Srsb for (/* void */; argc > optind; optind++) { 579*1488Srsb char *endptr; 580*1488Srsb 581*1488Srsb /* If we have more than two operands left to process */ 582*1488Srsb if ((argc - optind) > 2) { 583*1488Srsb (*entityp)[nentities++].e_name = strdup(argv[optind]); 584*1488Srsb continue; 585*1488Srsb } 586*1488Srsb 587*1488Srsb /* If we're here, then we only have one or two operands left */ 588*1488Srsb errno = 0; 589*1488Srsb out_of_range = 0; 590*1488Srsb *interval = strtol(argv[optind], &endptr, 10); 591*1488Srsb if (*endptr && !isdigit((int)*endptr)) { 592*1488Srsb /* Operand was not a number */ 593*1488Srsb (*entityp)[nentities++].e_name = strdup(argv[optind]); 594*1488Srsb continue; 595*1488Srsb } else if (errno == ERANGE || *interval <= 0 || 596*1488Srsb *interval > MAXLONG) { 597*1488Srsb /* Operand was a number, just out of range */ 598*1488Srsb out_of_range++; 599*1488Srsb } 600*1488Srsb 601*1488Srsb /* 602*1488Srsb * The last operand we saw was a number. If it happened to 603*1488Srsb * be the last operand, then it is the interval... 604*1488Srsb */ 605*1488Srsb if ((argc - optind) == 1) { 606*1488Srsb /* ...but we need to check the range. */ 607*1488Srsb if (out_of_range) { 608*1488Srsb (void) fprintf(stderr, gettext( 609*1488Srsb "interval must be between 1 and " 610*1488Srsb "%ld (inclusive)\n"), MAXLONG); 611*1488Srsb return (-1); 612*1488Srsb } else { 613*1488Srsb /* 614*1488Srsb * The value of the interval is valid. Set 615*1488Srsb * count to something really big so it goes 616*1488Srsb * virtually forever. 617*1488Srsb */ 618*1488Srsb *count = MAXLONG; 619*1488Srsb break; 620*1488Srsb } 621*1488Srsb } 622*1488Srsb 623*1488Srsb /* 624*1488Srsb * At this point, we *might* have the interval, but if the 625*1488Srsb * next operand isn't a number, then we don't have either 626*1488Srsb * the interval nor the count. Both must be set to the 627*1488Srsb * defaults. In that case, both the current and the previous 628*1488Srsb * operands are stat-able entities. 629*1488Srsb */ 630*1488Srsb errno = 0; 631*1488Srsb *count = strtol(argv[optind + 1], &endptr, 10); 632*1488Srsb if (*endptr && !isdigit((int)*endptr)) { 633*1488Srsb /* 634*1488Srsb * Faked out! The last operand wasn't a number so 635*1488Srsb * the current and previous operands should be 636*1488Srsb * stat-able entities. We also need to reset interval. 637*1488Srsb */ 638*1488Srsb *interval = 0; 639*1488Srsb (*entityp)[nentities++].e_name = strdup(argv[optind++]); 640*1488Srsb (*entityp)[nentities++].e_name = strdup(argv[optind++]); 641*1488Srsb } else if (out_of_range || errno == ERANGE || *count <= 0) { 642*1488Srsb (void) fprintf(stderr, gettext( 643*1488Srsb "Both interval and count must be between 1 " 644*1488Srsb "and %ld (inclusive)\n"), MAXLONG); 645*1488Srsb return (-1); 646*1488Srsb } 647*1488Srsb break; /* Done! */ 648*1488Srsb } 649*1488Srsb return (nentities); 650*1488Srsb } 651*1488Srsb 652*1488Srsb /* 653*1488Srsb * set_mntpt() looks at the entity's name (e_name) and finds its 654*1488Srsb * mountpoint. To do this, we need to build a list of mountpoints 655*1488Srsb * from /etc/mnttab. We only need to do this once and we don't do it 656*1488Srsb * if we don't need to look at any mountpoints. 657*1488Srsb * Returns 0 on success, non-zero if it couldn't find a mount-point. 658*1488Srsb */ 659*1488Srsb int 660*1488Srsb set_mntpt(entity_t *ep) 661*1488Srsb { 662*1488Srsb static struct mnt { 663*1488Srsb struct mnt *m_next; 664*1488Srsb char *m_mntpt; 665*1488Srsb ulong_t m_fsid; /* From statvfs(), set only as needed */ 666*1488Srsb } *mnt_list = NULL; /* Linked list of mount-points */ 667*1488Srsb struct mnt *mntp; 668*1488Srsb struct statvfs statvfsbuf; 669*1488Srsb char *original_name = ep->e_name; 670*1488Srsb char path[PATH_MAX]; 671*1488Srsb 672*1488Srsb if (original_name == NULL) /* Shouldn't happen */ 673*1488Srsb return (1); 674*1488Srsb 675*1488Srsb /* We only set up mnt_list the first time this is called */ 676*1488Srsb if (mnt_list == NULL) { 677*1488Srsb FILE *fp; 678*1488Srsb struct mnttab mnttab; 679*1488Srsb 680*1488Srsb if ((fp = fopen(MNTTAB, "r")) == NULL) { 681*1488Srsb perror(MNTTAB); 682*1488Srsb return (1); 683*1488Srsb } 684*1488Srsb resetmnttab(fp); 685*1488Srsb /* 686*1488Srsb * We insert at the front of the list so that when we 687*1488Srsb * search entries we'll have the last mounted entries 688*1488Srsb * first in the list so that we can match the longest 689*1488Srsb * mountpoint. 690*1488Srsb */ 691*1488Srsb while (getmntent(fp, &mnttab) == 0) { 692*1488Srsb if ((mntp = malloc(sizeof (*mntp))) == NULL) { 693*1488Srsb perror(gettext("Can't create mount list")); 694*1488Srsb return (1); 695*1488Srsb } 696*1488Srsb mntp->m_mntpt = strdup(mnttab.mnt_mountp); 697*1488Srsb mntp->m_next = mnt_list; 698*1488Srsb mnt_list = mntp; 699*1488Srsb } 700*1488Srsb (void) fclose(fp); 701*1488Srsb } 702*1488Srsb 703*1488Srsb if (realpath(original_name, path) == NULL) { 704*1488Srsb perror(original_name); 705*1488Srsb return (1); 706*1488Srsb } 707*1488Srsb 708*1488Srsb /* 709*1488Srsb * Now that we have the path, walk through the mnt_list and 710*1488Srsb * look for the first (best) match. 711*1488Srsb */ 712*1488Srsb for (mntp = mnt_list; mntp; mntp = mntp->m_next) { 713*1488Srsb if (strncmp(path, mntp->m_mntpt, strlen(mntp->m_mntpt)) == 0) { 714*1488Srsb if (mntp->m_fsid == 0) { 715*1488Srsb if (statvfs(mntp->m_mntpt, &statvfsbuf)) { 716*1488Srsb /* Can't statvfs so no match */ 717*1488Srsb continue; 718*1488Srsb } else { 719*1488Srsb mntp->m_fsid = statvfsbuf.f_fsid; 720*1488Srsb } 721*1488Srsb } 722*1488Srsb 723*1488Srsb if (ep->e_fsid != mntp->m_fsid) { 724*1488Srsb /* No match - Move on */ 725*1488Srsb continue; 726*1488Srsb } 727*1488Srsb 728*1488Srsb break; 729*1488Srsb } 730*1488Srsb } 731*1488Srsb 732*1488Srsb if (mntp == NULL) { 733*1488Srsb (void) fprintf(stderr, gettext( 734*1488Srsb "Can't find mount point for %s\n"), path); 735*1488Srsb return (1); 736*1488Srsb } 737*1488Srsb 738*1488Srsb ep->e_name = strdup(mntp->m_mntpt); 739*1488Srsb free(original_name); 740*1488Srsb return (0); 741*1488Srsb } 742*1488Srsb 743*1488Srsb /* 744*1488Srsb * We have an array of entities that are potentially stat-able. Using 745*1488Srsb * the name (e_name) of the entity, attempt to construct a ksname suitable 746*1488Srsb * for use by kstat_lookup(3kstat) and fill it into the e_ksname member. 747*1488Srsb * 748*1488Srsb * We check the e_name against the list of file system types. If there is 749*1488Srsb * no match then test to see if the path is valid. If the path is valid, 750*1488Srsb * then determine the mountpoint. 751*1488Srsb */ 752*1488Srsb void 753*1488Srsb set_ksnames(entity_t *entities, int nentities, char **fstypes, int nfstypes) 754*1488Srsb { 755*1488Srsb int i, j; 756*1488Srsb struct statvfs statvfsbuf; 757*1488Srsb 758*1488Srsb for (i = 0; i < nentities; i++) { 759*1488Srsb entity_t *ep = &entities[i]; 760*1488Srsb 761*1488Srsb /* Check the name against the list of fstypes */ 762*1488Srsb for (j = 1; j < nfstypes; j++) { 763*1488Srsb if (fstypes[j] && ep->e_name && 764*1488Srsb strcmp(ep->e_name, fstypes[j]) == 0) { 765*1488Srsb /* It's a file system type */ 766*1488Srsb ep->e_type = ENTYPE_FSTYPE; 767*1488Srsb (void) snprintf(ep->e_ksname, 768*1488Srsb KSTAT_STRLEN, "%s%s", 769*1488Srsb VOPSTATS_STR, ep->e_name); 770*1488Srsb /* Now allocate the vopstats array */ 771*1488Srsb ep->e_vs = calloc(VS_SIZE, sizeof (vopstats_t)); 772*1488Srsb if (entities[i].e_vs == NULL) { 773*1488Srsb perror(gettext("calloc() vopstats")); 774*1488Srsb exit(1); 775*1488Srsb } 776*1488Srsb break; 777*1488Srsb } 778*1488Srsb } 779*1488Srsb if (j < nfstypes) /* Found it! */ 780*1488Srsb continue; 781*1488Srsb 782*1488Srsb /* 783*1488Srsb * If the entity in the exception list of fstypes, then 784*1488Srsb * null out the entry so it isn't displayed and move along. 785*1488Srsb */ 786*1488Srsb if (is_exception(ep->e_name)) { 787*1488Srsb ep->e_ksname[0] = 0; 788*1488Srsb continue; 789*1488Srsb } 790*1488Srsb 791*1488Srsb /* If we didn't find it, see if it's a path */ 792*1488Srsb if (ep->e_name == NULL || statvfs(ep->e_name, &statvfsbuf)) { 793*1488Srsb /* Error - Make sure the entry is nulled out */ 794*1488Srsb ep->e_ksname[0] = 0; 795*1488Srsb continue; 796*1488Srsb } 797*1488Srsb (void) snprintf(ep->e_ksname, KSTAT_STRLEN, "%s%lx", 798*1488Srsb VOPSTATS_STR, statvfsbuf.f_fsid); 799*1488Srsb ep->e_fsid = statvfsbuf.f_fsid; 800*1488Srsb if (set_mntpt(ep)) { 801*1488Srsb (void) fprintf(stderr, 802*1488Srsb gettext("Can't determine type of \"%s\"\n"), 803*1488Srsb ep->e_name ? ep->e_name : gettext("<NULL>")); 804*1488Srsb } else { 805*1488Srsb ep->e_type = ENTYPE_MNTPT; 806*1488Srsb } 807*1488Srsb 808*1488Srsb /* Now allocate the vopstats array */ 809*1488Srsb ep->e_vs = calloc(VS_SIZE, sizeof (vopstats_t)); 810*1488Srsb if (entities[i].e_vs == NULL) { 811*1488Srsb perror(gettext("Can't calloc vopstats")); 812*1488Srsb exit(1); 813*1488Srsb } 814*1488Srsb } 815*1488Srsb } 816*1488Srsb 817*1488Srsb void 818*1488Srsb print_time(int type) 819*1488Srsb { 820*1488Srsb time_t t; 821*1488Srsb 822*1488Srsb if (time(&t) != -1) { 823*1488Srsb if (type == UDATE) { 824*1488Srsb (void) printf("%ld\n", t); 825*1488Srsb } else if (type == DDATE) { 826*1488Srsb char *dstr; 827*1488Srsb 828*1488Srsb dstr = ctime(&t); 829*1488Srsb if (dstr) { 830*1488Srsb (void) printf("%s", dstr); 831*1488Srsb } 832*1488Srsb } 833*1488Srsb } 834*1488Srsb } 835*1488Srsb 836*1488Srsb /* 837*1488Srsb * The idea is that 'dspfunc' should only be modified from the default 838*1488Srsb * once since the display options are mutually exclusive. If 'dspfunc' 839*1488Srsb * only contains the default display function, then all is good and we 840*1488Srsb * can set it to the new display function. Otherwise, bail. 841*1488Srsb */ 842*1488Srsb void 843*1488Srsb set_dispfunc( 844*1488Srsb void (**dspfunc)(char *, vopstats_t *, vopstats_t *, int), 845*1488Srsb void (*newfunc)(char *, vopstats_t *, vopstats_t *, int)) 846*1488Srsb { 847*1488Srsb if (*dspfunc != dflt_display) { 848*1488Srsb (void) fprintf(stderr, gettext( 849*1488Srsb "%s: Display options -{a|f|i|n|v} are mutually exclusive\n"), 850*1488Srsb cmdname); 851*1488Srsb usage(); 852*1488Srsb } 853*1488Srsb *dspfunc = newfunc; 854*1488Srsb } 855*1488Srsb 856*1488Srsb int 857*1488Srsb main(int argc, char *argv[]) 858*1488Srsb { 859*1488Srsb int c; 860*1488Srsb int i, j; /* Generic counters */ 861*1488Srsb int nentities_found; 862*1488Srsb int linesout; /* Keeps track of lines printed */ 863*1488Srsb int printhdr = 0; /* Print a header? 0 = no, 1 = yes */ 864*1488Srsb int nfstypes; /* Number of fstypes */ 865*1488Srsb int dispflag = 0; /* Flags for display control */ 866*1488Srsb int timestamp = NODATE; /* Default: no time stamp */ 867*1488Srsb long count = 0; /* Number of iterations for display */ 868*1488Srsb long interval = 0; 869*1488Srsb boolean_t fstypes_only = B_FALSE; /* Display fstypes only */ 870*1488Srsb char **fstypes; /* Array of names of all fstypes */ 871*1488Srsb int nentities; /* Number of stat-able entities */ 872*1488Srsb entity_t *entities; /* Array of stat-able entities */ 873*1488Srsb kstat_ctl_t *kc; 874*1488Srsb void (*dfunc)(char *, vopstats_t *, vopstats_t *, int) = dflt_display; 875*1488Srsb 876*1488Srsb extern int optind; 877*1488Srsb 878*1488Srsb cmdname = argv[0]; 879*1488Srsb while ((c = getopt(argc, argv, OPTIONS)) != EOF) { 880*1488Srsb switch (c) { 881*1488Srsb 882*1488Srsb default: 883*1488Srsb usage(); 884*1488Srsb break; 885*1488Srsb 886*1488Srsb case 'P': /* Parsable output */ 887*1488Srsb dispflag |= DISP_RAW; 888*1488Srsb break; 889*1488Srsb 890*1488Srsb case 'T': /* Timestamp */ 891*1488Srsb if (optarg) { 892*1488Srsb if (strcmp(optarg, "u") == 0) { 893*1488Srsb timestamp = UDATE; 894*1488Srsb } else if (strcmp(optarg, "d") == 0) { 895*1488Srsb timestamp = DDATE; 896*1488Srsb } 897*1488Srsb } 898*1488Srsb 899*1488Srsb /* If it was never set properly... */ 900*1488Srsb if (timestamp == NODATE) { 901*1488Srsb (void) fprintf(stderr, gettext( 902*1488Srsb "%s: -T option requires either 'u' or 'd'\n"), 903*1488Srsb cmdname); 904*1488Srsb usage(); 905*1488Srsb } 906*1488Srsb break; 907*1488Srsb 908*1488Srsb case 'a': 909*1488Srsb set_dispfunc(&dfunc, attr_display); 910*1488Srsb break; 911*1488Srsb 912*1488Srsb case 'f': 913*1488Srsb set_dispfunc(&dfunc, vop_display); 914*1488Srsb break; 915*1488Srsb 916*1488Srsb case 'i': 917*1488Srsb set_dispfunc(&dfunc, io_display); 918*1488Srsb break; 919*1488Srsb 920*1488Srsb case 'n': 921*1488Srsb set_dispfunc(&dfunc, naming_display); 922*1488Srsb break; 923*1488Srsb 924*1488Srsb case 'v': 925*1488Srsb set_dispfunc(&dfunc, vm_display); 926*1488Srsb break; 927*1488Srsb } 928*1488Srsb } 929*1488Srsb 930*1488Srsb if ((dispflag & DISP_RAW) && (timestamp != NODATE)) { 931*1488Srsb (void) fprintf(stderr, gettext( 932*1488Srsb "-P and -T options are mutually exclusive\n")); 933*1488Srsb usage(); 934*1488Srsb } 935*1488Srsb 936*1488Srsb /* Gather the list of filesystem types */ 937*1488Srsb if ((nfstypes = build_fstype_list(&fstypes)) == 0) { 938*1488Srsb (void) fprintf(stderr, 939*1488Srsb gettext("Can't build list of fstypes\n")); 940*1488Srsb exit(1); 941*1488Srsb } 942*1488Srsb 943*1488Srsb nentities = parse_operands( 944*1488Srsb argc, argv, optind, &interval, &count, &entities); 945*1488Srsb 946*1488Srsb if (nentities == -1) /* Set of operands didn't parse properly */ 947*1488Srsb usage(); 948*1488Srsb 949*1488Srsb /* 950*1488Srsb * If we had no operands (except for interval/count) then we 951*1488Srsb * fill in the entities[] array with all the fstypes. 952*1488Srsb */ 953*1488Srsb if (nentities == 0) { 954*1488Srsb fstypes_only = B_TRUE; 955*1488Srsb 956*1488Srsb if ((entities = calloc(nfstypes, sizeof (entity_t))) == NULL) { 957*1488Srsb (void) fprintf(stderr, 958*1488Srsb gettext("Can't calloc fstype stats\n")); 959*1488Srsb exit(1); 960*1488Srsb } 961*1488Srsb 962*1488Srsb for (i = 1; i < nfstypes; i++) { 963*1488Srsb if (fstypes[i]) { 964*1488Srsb entities[nentities].e_name = strdup(fstypes[i]); 965*1488Srsb nentities++; 966*1488Srsb } 967*1488Srsb } 968*1488Srsb } 969*1488Srsb 970*1488Srsb set_ksnames(entities, nentities, fstypes, nfstypes); 971*1488Srsb 972*1488Srsb if ((kc = kstat_open()) == NULL) { 973*1488Srsb perror(gettext("kstat_open")); 974*1488Srsb exit(1); 975*1488Srsb } 976*1488Srsb 977*1488Srsb /* 978*1488Srsb * The following loop walks through the entities[] list to "prime 979*1488Srsb * the pump" 980*1488Srsb */ 981*1488Srsb for (j = 0, linesout = 0; j < nentities; j++) { 982*1488Srsb entity_t *ent = &entities[j]; 983*1488Srsb vopstats_t *vsp = &ent->e_vs[CUR_INDEX]; 984*1488Srsb kstat_t *ksp = NULL; 985*1488Srsb 986*1488Srsb if (get_vopstats(kc, ent->e_ksname, vsp, &ksp) == 0) { 987*1488Srsb (*dfunc)(ent->e_name, NULL, vsp, 988*1488Srsb dispflag_policy(linesout == 0, dispflag)); 989*1488Srsb linesout++; 990*1488Srsb } else { 991*1488Srsb /* 992*1488Srsb * If we can't find it the first time through, then 993*1488Srsb * get rid of it. 994*1488Srsb */ 995*1488Srsb entities[j].e_ksname[0] = 0; 996*1488Srsb 997*1488Srsb /* 998*1488Srsb * If we're only displaying the fstypes (default 999*1488Srsb * with no other entities requested) then don't 1000*1488Srsb * complain about any file systems that might not 1001*1488Srsb * be loaded. Otherwise, let the user know that 1002*1488Srsb * he chose poorly. 1003*1488Srsb */ 1004*1488Srsb if (fstypes_only == B_FALSE) { 1005*1488Srsb (void) fprintf(stderr, gettext( 1006*1488Srsb "No statistics available for %s\n"), 1007*1488Srsb entities[j].e_name); 1008*1488Srsb } 1009*1488Srsb } 1010*1488Srsb } 1011*1488Srsb 1012*1488Srsb BUMP_INDEX(); /* Swap the previous/current indices */ 1013*1488Srsb for (i = 1; i <= count; i++) { 1014*1488Srsb /* 1015*1488Srsb * No telling how many lines will be printed in any interval. 1016*1488Srsb * There should be a minimum of HEADERLINES between any 1017*1488Srsb * header. If we exceed that, no big deal. 1018*1488Srsb */ 1019*1488Srsb if (linesout > HEADERLINES) { 1020*1488Srsb linesout = 0; 1021*1488Srsb printhdr = 1; 1022*1488Srsb } 1023*1488Srsb (void) poll(NULL, 0, interval*1000); 1024*1488Srsb 1025*1488Srsb if (timestamp) { 1026*1488Srsb print_time(timestamp); 1027*1488Srsb linesout++; 1028*1488Srsb } 1029*1488Srsb 1030*1488Srsb for (j = 0, nentities_found = 0; j < nentities; j++) { 1031*1488Srsb entity_t *ent = &entities[j]; 1032*1488Srsb 1033*1488Srsb /* 1034*1488Srsb * If this entry has been cleared, don't attempt 1035*1488Srsb * to process it. 1036*1488Srsb */ 1037*1488Srsb if (ent->e_ksname[0] == 0) { 1038*1488Srsb continue; 1039*1488Srsb } 1040*1488Srsb 1041*1488Srsb if (get_vopstats(kc, ent->e_ksname, 1042*1488Srsb &ent->e_vs[CUR_INDEX], NULL) == 0) { 1043*1488Srsb (*dfunc)(ent->e_name, &ent->e_vs[PREV_INDEX], 1044*1488Srsb &ent->e_vs[CUR_INDEX], 1045*1488Srsb dispflag_policy(printhdr, dispflag)); 1046*1488Srsb linesout++; 1047*1488Srsb nentities_found++; 1048*1488Srsb } else { 1049*1488Srsb if (ent->e_type == ENTYPE_MNTPT) { 1050*1488Srsb (void) printf(gettext( 1051*1488Srsb "<<mount point no longer " 1052*1488Srsb "available: %s>>\n"), ent->e_name); 1053*1488Srsb } else if (ent->e_type == ENTYPE_FSTYPE) { 1054*1488Srsb (void) printf(gettext( 1055*1488Srsb "<<file system module no longer " 1056*1488Srsb "loaded: %s>>\n"), ent->e_name); 1057*1488Srsb } else { 1058*1488Srsb (void) printf(gettext( 1059*1488Srsb "<<%s no longer available>>\n"), 1060*1488Srsb ent->e_name); 1061*1488Srsb } 1062*1488Srsb /* Disable this so it doesn't print again */ 1063*1488Srsb ent->e_ksname[0] = 0; 1064*1488Srsb } 1065*1488Srsb printhdr = 0; /* Always shut this off */ 1066*1488Srsb } 1067*1488Srsb BUMP_INDEX(); /* Bump the previous/current indices */ 1068*1488Srsb 1069*1488Srsb /* 1070*1488Srsb * If the entities we were observing are no longer there 1071*1488Srsb * (file system modules unloaded, file systems unmounted) 1072*1488Srsb * then we're done. 1073*1488Srsb */ 1074*1488Srsb if (nentities_found == 0) 1075*1488Srsb break; 1076*1488Srsb } 1077*1488Srsb 1078*1488Srsb return (0); 1079*1488Srsb } 1080