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