15a59a8b3Srsb /* 25a59a8b3Srsb * CDDL HEADER START 35a59a8b3Srsb * 45a59a8b3Srsb * The contents of this file are subject to the terms of the 55a59a8b3Srsb * Common Development and Distribution License (the "License"). 65a59a8b3Srsb * You may not use this file except in compliance with the License. 75a59a8b3Srsb * 85a59a8b3Srsb * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 95a59a8b3Srsb * or http://www.opensolaris.org/os/licensing. 105a59a8b3Srsb * See the License for the specific language governing permissions 115a59a8b3Srsb * and limitations under the License. 125a59a8b3Srsb * 135a59a8b3Srsb * When distributing Covered Code, include this CDDL HEADER in each 145a59a8b3Srsb * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 155a59a8b3Srsb * If applicable, add the following below this CDDL HEADER, with the 165a59a8b3Srsb * fields enclosed by brackets "[]" replaced with your own identifying 175a59a8b3Srsb * information: Portions Copyright [yyyy] [name of copyright owner] 185a59a8b3Srsb * 195a59a8b3Srsb * CDDL HEADER END 205a59a8b3Srsb */ 215a59a8b3Srsb /* 22*4944376cSJohn Levon * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 235a59a8b3Srsb * Use is subject to license terms. 245a59a8b3Srsb */ 255a59a8b3Srsb 265a59a8b3Srsb #include <stdio.h> 275a59a8b3Srsb #include <kstat.h> 285a59a8b3Srsb #include <stdlib.h> 295a59a8b3Srsb #include <string.h> 305a59a8b3Srsb #include <strings.h> 315a59a8b3Srsb #include <errno.h> 325a59a8b3Srsb #include <limits.h> 335a59a8b3Srsb #include <sys/types.h> 345a59a8b3Srsb #include <time.h> 355a59a8b3Srsb #include <sys/time.h> 365a59a8b3Srsb #include <sys/uio.h> 375a59a8b3Srsb #include <sys/vnode.h> 385a59a8b3Srsb #include <sys/vfs.h> 395a59a8b3Srsb #include <sys/statvfs.h> 405a59a8b3Srsb #include <sys/fstyp.h> 415a59a8b3Srsb #include <sys/fsid.h> 425a59a8b3Srsb #include <sys/mnttab.h> 435a59a8b3Srsb #include <values.h> 445a59a8b3Srsb #include <poll.h> 455a59a8b3Srsb #include <ctype.h> 465a59a8b3Srsb #include <libintl.h> 4713c7b6acSrsb #include <locale.h> 4800c76d6fStc35445 #include <signal.h> 4900c76d6fStc35445 5000c76d6fStc35445 #include "statcommon.h" 515a59a8b3Srsb 523a401a6bSrsb /* 533a401a6bSrsb * For now, parsable output is turned off. Once we gather feedback and 543a401a6bSrsb * stablize the output format, we'll turn it back on. This prevents 553a401a6bSrsb * the situation where users build tools which depend on a specific 563a401a6bSrsb * format before we declare the output stable. 573a401a6bSrsb */ 583a401a6bSrsb #define PARSABLE_OUTPUT 0 593a401a6bSrsb 603a401a6bSrsb #if PARSABLE_OUTPUT 613a401a6bSrsb #define OPTIONS "FPT:afginv" 623a401a6bSrsb #else 633a401a6bSrsb #define OPTIONS "FT:afginv" 643a401a6bSrsb #endif 655a59a8b3Srsb 665a59a8b3Srsb /* Time stamp values */ 675a59a8b3Srsb #define NODATE 0 /* Default: No time stamp */ 685a59a8b3Srsb #define DDATE 1 /* Standard date format */ 695a59a8b3Srsb #define UDATE 2 /* Internal representation of Unix time */ 705a59a8b3Srsb 715a59a8b3Srsb #define RETRY_DELAY 250 /* Timeout for poll() */ 723a401a6bSrsb #define HEADERLINES 12 /* Number of lines between display headers */ 735a59a8b3Srsb 745a59a8b3Srsb #define LBUFSZ 64 /* Generic size for local buffer */ 755a59a8b3Srsb 765a59a8b3Srsb /* 775a59a8b3Srsb * The following are used for the nicenum() function 785a59a8b3Srsb */ 795a59a8b3Srsb #define KILO_VAL 1024 805a59a8b3Srsb #define ONE_INDEX 3 815a59a8b3Srsb 825a59a8b3Srsb #define NENTITY_INIT 1 /* Initial number of entities to allocate */ 835a59a8b3Srsb 845a59a8b3Srsb /* 855a59a8b3Srsb * We need to have a mechanism for an old/previous and new/current vopstat 865a59a8b3Srsb * structure. We only need two per entity and we can swap between them. 875a59a8b3Srsb */ 885a59a8b3Srsb #define VS_SIZE 2 /* Size of vopstat array */ 895a59a8b3Srsb #define CUR_INDEX (vs_i) 905a59a8b3Srsb #define PREV_INDEX ((vs_i == 0) ? 1 : 0) /* Opposite of CUR_INDEX */ 915a59a8b3Srsb #define BUMP_INDEX() vs_i = ((vs_i == 0) ? 1 : 0) 925a59a8b3Srsb 935a59a8b3Srsb /* 945a59a8b3Srsb * An "entity" is anything we're collecting statistics on, it could 955a59a8b3Srsb * be a mountpoint or an FS-type. 965a59a8b3Srsb * e_name is the name of the entity (e.g. mount point or FS-type) 975a59a8b3Srsb * e_ksname is the name of the associated kstat 985a59a8b3Srsb * e_vs is an array of vopstats. This is used to keep track of "previous" 995a59a8b3Srsb * and "current" vopstats. 1005a59a8b3Srsb */ 1015a59a8b3Srsb typedef struct entity { 1025a59a8b3Srsb char *e_name; /* name of entity */ 1035a59a8b3Srsb vopstats_t *e_vs; /* Array of vopstats */ 1045a59a8b3Srsb ulong_t e_fsid; /* fsid for ENTYPE_MNTPT only */ 1055a59a8b3Srsb int e_type; /* type of entity */ 1065a59a8b3Srsb char e_ksname[KSTAT_STRLEN]; /* kstat name */ 1075a59a8b3Srsb } entity_t; 1085a59a8b3Srsb 1095a59a8b3Srsb /* Types of entities (e_type) */ 1105a59a8b3Srsb #define ENTYPE_UNKNOWN 0 /* UNKNOWN must be zero since we calloc() */ 1115a59a8b3Srsb #define ENTYPE_FSTYPE 1 1125a59a8b3Srsb #define ENTYPE_MNTPT 2 1135a59a8b3Srsb 1145a59a8b3Srsb /* If more sub-one units are added, make sure to adjust ONE_INDEX above */ 1155a59a8b3Srsb static char units[] = "num KMGTPE"; 1165a59a8b3Srsb 11700c76d6fStc35445 char *cmdname; /* name of this command */ 11800c76d6fStc35445 int caught_cont = 0; /* have caught a SIGCONT */ 1195a59a8b3Srsb 120*4944376cSJohn Levon extern uint_t timestamp_fmt; /* print timestamp with stats */ 121*4944376cSJohn Levon 1225a59a8b3Srsb static int vs_i = 0; /* Index of current vs[] slot */ 1235a59a8b3Srsb 1245a59a8b3Srsb static void 1255a59a8b3Srsb usage() 1265a59a8b3Srsb { 1273a401a6bSrsb (void) fprintf(stderr, gettext( 1283a401a6bSrsb "Usage: %s [-a|f|i|n|v] [-T d|u] {-F | {fstype | fspath}...} " 1295a59a8b3Srsb "[interval [count]]\n"), cmdname); 1305a59a8b3Srsb exit(2); 1315a59a8b3Srsb } 1325a59a8b3Srsb 1335a59a8b3Srsb /* 1345a59a8b3Srsb * Given a 64-bit number and a starting unit (e.g., n - nanoseconds), 1355a59a8b3Srsb * convert the number to a 5-character representation including any 1365a59a8b3Srsb * decimal point and single-character unit. Put that representation 1375a59a8b3Srsb * into the array "buf" (which had better be big enough). 1385a59a8b3Srsb */ 1395a59a8b3Srsb char * 1405a59a8b3Srsb nicenum(uint64_t num, char unit, char *buf) 1415a59a8b3Srsb { 1425a59a8b3Srsb uint64_t n = num; 1435a59a8b3Srsb int unit_index; 1445a59a8b3Srsb int index; 1455a59a8b3Srsb char u; 1465a59a8b3Srsb 1475a59a8b3Srsb /* If the user passed in a NUL/zero unit, use the blank value for 1 */ 1485a59a8b3Srsb if (unit == '\0') 1495a59a8b3Srsb unit = ' '; 1505a59a8b3Srsb 1515a59a8b3Srsb unit_index = 0; 1525a59a8b3Srsb while (units[unit_index] != unit) { 1535a59a8b3Srsb unit_index++; 1545a59a8b3Srsb if (unit_index > sizeof (units) - 1) { 1555a59a8b3Srsb (void) sprintf(buf, "??"); 1565a59a8b3Srsb return (buf); 1575a59a8b3Srsb } 1585a59a8b3Srsb } 1595a59a8b3Srsb 1605a59a8b3Srsb index = 0; 1615a59a8b3Srsb while (n >= KILO_VAL) { 1625a59a8b3Srsb n = (n + (KILO_VAL / 2)) / KILO_VAL; /* Round up or down */ 1635a59a8b3Srsb index++; 1645a59a8b3Srsb unit_index++; 1655a59a8b3Srsb } 1665a59a8b3Srsb 1675a59a8b3Srsb if (unit_index >= sizeof (units) - 1) { 1685a59a8b3Srsb (void) sprintf(buf, "??"); 1695a59a8b3Srsb return (buf); 1705a59a8b3Srsb } 1715a59a8b3Srsb 1725a59a8b3Srsb u = units[unit_index]; 1735a59a8b3Srsb 1745a59a8b3Srsb if (unit_index == ONE_INDEX) { 1755a59a8b3Srsb (void) sprintf(buf, "%llu", (u_longlong_t)n); 1765a59a8b3Srsb } else if (n < 10 && (num & (num - 1)) != 0) { 1775a59a8b3Srsb (void) sprintf(buf, "%.2f%c", 1785a59a8b3Srsb (double)num / (1ULL << 10 * index), u); 1795a59a8b3Srsb } else if (n < 100 && (num & (num - 1)) != 0) { 1805a59a8b3Srsb (void) sprintf(buf, "%.1f%c", 1815a59a8b3Srsb (double)num / (1ULL << 10 * index), u); 1825a59a8b3Srsb } else { 1835a59a8b3Srsb (void) sprintf(buf, "%llu%c", (u_longlong_t)n, u); 1845a59a8b3Srsb } 1855a59a8b3Srsb 1865a59a8b3Srsb return (buf); 1875a59a8b3Srsb } 1885a59a8b3Srsb 1895a59a8b3Srsb 1905a59a8b3Srsb #define RAWVAL(ptr, member) ((ptr)->member.value.ui64) 1915a59a8b3Srsb #define DELTA(member) \ 1925a59a8b3Srsb (newvsp->member.value.ui64 - (oldvsp ? oldvsp->member.value.ui64 : 0)) 1933a401a6bSrsb 1945a59a8b3Srsb #define PRINTSTAT(isnice, nicestring, rawstring, rawval, unit, buf) \ 1955a59a8b3Srsb (isnice) ? \ 1965a59a8b3Srsb (void) printf((nicestring), nicenum(rawval, unit, buf)) \ 1975a59a8b3Srsb : \ 1985a59a8b3Srsb (void) printf((rawstring), (rawval)) 1995a59a8b3Srsb 2005a59a8b3Srsb /* Values for display flag */ 2015a59a8b3Srsb #define DISP_HEADER 0x1 2025a59a8b3Srsb #define DISP_RAW 0x2 2035a59a8b3Srsb 2045a59a8b3Srsb /* 2055a59a8b3Srsb * The policy for dealing with multiple flags is dealt with here. 2065a59a8b3Srsb * Currently, if we are displaying raw output, then don't allow 2075a59a8b3Srsb * headers to be printed. 2085a59a8b3Srsb */ 2095a59a8b3Srsb int 2105a59a8b3Srsb dispflag_policy(int printhdr, int dispflag) 2115a59a8b3Srsb { 2125a59a8b3Srsb /* If we're not displaying raw output, then allow headers to print */ 2135a59a8b3Srsb if ((dispflag & DISP_RAW) == 0) { 2145a59a8b3Srsb if (printhdr) { 2155a59a8b3Srsb dispflag |= DISP_HEADER; 2165a59a8b3Srsb } 2175a59a8b3Srsb } 2185a59a8b3Srsb 2195a59a8b3Srsb return (dispflag); 2205a59a8b3Srsb } 2215a59a8b3Srsb 2225a59a8b3Srsb static void 2235a59a8b3Srsb dflt_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 2245a59a8b3Srsb { 2255a59a8b3Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 2265a59a8b3Srsb longlong_t nnewfile; 2275a59a8b3Srsb longlong_t nnamerm; 2285a59a8b3Srsb longlong_t nnamechg; 2295a59a8b3Srsb longlong_t nattrret; 2305a59a8b3Srsb longlong_t nattrchg; 2315a59a8b3Srsb longlong_t nlookup; 2325a59a8b3Srsb longlong_t nreaddir; 2335a59a8b3Srsb longlong_t ndataread; 2345a59a8b3Srsb longlong_t ndatawrite; 2355a59a8b3Srsb longlong_t readthruput; 2365a59a8b3Srsb longlong_t writethruput; 2375a59a8b3Srsb char buf[LBUFSZ]; 2385a59a8b3Srsb 2395a59a8b3Srsb nnewfile = DELTA(ncreate) + DELTA(nmkdir) + DELTA(nsymlink); 2405a59a8b3Srsb nnamerm = DELTA(nremove) + DELTA(nrmdir); 2415a59a8b3Srsb nnamechg = DELTA(nrename) + DELTA(nlink) + DELTA(nsymlink); 2425a59a8b3Srsb nattrret = DELTA(ngetattr) + DELTA(naccess) + 2435a59a8b3Srsb DELTA(ngetsecattr) + DELTA(nfid); 2445a59a8b3Srsb nattrchg = DELTA(nsetattr) + DELTA(nsetsecattr) + DELTA(nspace); 2455a59a8b3Srsb nlookup = DELTA(nlookup); 2465a59a8b3Srsb nreaddir = DELTA(nreaddir); 2475a59a8b3Srsb ndataread = DELTA(nread); 2485a59a8b3Srsb ndatawrite = DELTA(nwrite); 2495a59a8b3Srsb readthruput = DELTA(read_bytes); 2505a59a8b3Srsb writethruput = DELTA(write_bytes); 2515a59a8b3Srsb 2525a59a8b3Srsb if (dispflag & DISP_HEADER) { 253757bea67Srsb (void) printf( 2545a59a8b3Srsb " new name name attr attr lookup rddir read read write write\n" 255757bea67Srsb " file remov chng get set ops ops ops bytes ops bytes\n"); 2565a59a8b3Srsb } 2575a59a8b3Srsb 2585a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnewfile, ' ', buf); 2595a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnamerm, ' ', buf); 2605a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnamechg, ' ', buf); 2615a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nattrret, ' ', buf); 2625a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nattrchg, ' ', buf); 2635a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", nlookup, ' ', buf); 2645a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nreaddir, ' ', buf); 2655a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", ndataread, ' ', buf); 2665a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", readthruput, ' ', buf); 2675a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", ndatawrite, ' ', buf); 2685a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", writethruput, ' ', buf); 2695a59a8b3Srsb (void) printf("%s\n", name); 2705a59a8b3Srsb } 2715a59a8b3Srsb 2725a59a8b3Srsb static void 2735a59a8b3Srsb io_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 2745a59a8b3Srsb { 2755a59a8b3Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 2765a59a8b3Srsb char buf[LBUFSZ]; 2775a59a8b3Srsb 2785a59a8b3Srsb if (dispflag & DISP_HEADER) { 279757bea67Srsb (void) printf( 2805a59a8b3Srsb " read read write write rddir rddir rwlock rwulock\n" 281757bea67Srsb " ops bytes ops bytes ops bytes ops ops\n"); 2825a59a8b3Srsb } 2835a59a8b3Srsb 2845a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nread), ' ', buf); 2855a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(read_bytes), ' ', buf); 2865a59a8b3Srsb 2875a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nwrite), ' ', buf); 2885a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(write_bytes), ' ', buf); 2895a59a8b3Srsb 2905a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf); 2915a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(readdir_bytes), ' ', buf); 2925a59a8b3Srsb 2935a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nrwlock), ' ', buf); 2945a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrwunlock), ' ', buf); 2955a59a8b3Srsb 2965a59a8b3Srsb (void) printf("%s\n", name); 2975a59a8b3Srsb } 2985a59a8b3Srsb 2995a59a8b3Srsb static void 3005a59a8b3Srsb vm_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 3015a59a8b3Srsb { 3025a59a8b3Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 3035a59a8b3Srsb char buf[LBUFSZ]; 3045a59a8b3Srsb 3055a59a8b3Srsb if (dispflag & DISP_HEADER) { 306757bea67Srsb (void) printf(" map addmap delmap getpag putpag pagio\n"); 3075a59a8b3Srsb } 3085a59a8b3Srsb 3095a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmap), ' ', buf); 3105a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(naddmap), ' ', buf); 3115a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ndelmap), ' ', buf); 3125a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetpage), ' ', buf); 3135a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nputpage), ' ', buf); 3145a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(npageio), ' ', buf); 3155a59a8b3Srsb (void) printf("%s\n", name); 3165a59a8b3Srsb } 3175a59a8b3Srsb 3185a59a8b3Srsb static void 3195a59a8b3Srsb attr_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 3205a59a8b3Srsb { 3215a59a8b3Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 3225a59a8b3Srsb char buf[LBUFSZ]; 3235a59a8b3Srsb 3245a59a8b3Srsb if (dispflag & DISP_HEADER) { 325757bea67Srsb (void) printf("getattr setattr getsec setsec\n"); 3265a59a8b3Srsb } 3275a59a8b3Srsb 3285a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetattr), ' ', buf); 3295a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetattr), ' ', buf); 3305a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetsecattr), ' ', buf); 3315a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetsecattr), ' ', buf); 3325a59a8b3Srsb 3335a59a8b3Srsb (void) printf("%s\n", name); 3345a59a8b3Srsb } 3355a59a8b3Srsb 3365a59a8b3Srsb static void 3375a59a8b3Srsb naming_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 3385a59a8b3Srsb { 3395a59a8b3Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 3405a59a8b3Srsb char buf[LBUFSZ]; 3415a59a8b3Srsb 3425a59a8b3Srsb if (dispflag & DISP_HEADER) { 343757bea67Srsb (void) printf( 344757bea67Srsb "lookup creat remov link renam mkdir rmdir rddir symlnk rdlnk\n"); 3455a59a8b3Srsb } 3465a59a8b3Srsb 3475a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlookup), ' ', buf); 3485a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(ncreate), ' ', buf); 3495a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nremove), ' ', buf); 3505a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlink), ' ', buf); 3515a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrename), ' ', buf); 3525a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmkdir), ' ', buf); 3535a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrmdir), ' ', buf); 3545a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf); 3555a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsymlink), ' ', buf); 3565a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreadlink), ' ', buf); 3575a59a8b3Srsb (void) printf("%s\n", name); 3585a59a8b3Srsb } 3595a59a8b3Srsb 3605a59a8b3Srsb 3615a59a8b3Srsb #define PRINT_VOPSTAT_CMN(niceflag, vop) \ 3625a59a8b3Srsb if (niceflag) \ 3635a59a8b3Srsb (void) printf("%10s ", #vop); \ 3645a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(n##vop), ' ', buf); 3655a59a8b3Srsb 3665a59a8b3Srsb #define PRINT_VOPSTAT(niceflag, vop) \ 3675a59a8b3Srsb PRINT_VOPSTAT_CMN(niceflag, vop); \ 3685a59a8b3Srsb if (niceflag) \ 3695a59a8b3Srsb (void) printf("\n"); 3705a59a8b3Srsb 3715a59a8b3Srsb #define PRINT_VOPSTAT_IO(niceflag, vop) \ 3725a59a8b3Srsb PRINT_VOPSTAT_CMN(niceflag, vop); \ 3735a59a8b3Srsb PRINTSTAT(niceflag, " %5s\n", "%lld:", \ 3745a59a8b3Srsb DELTA(vop##_bytes), ' ', buf); 3755a59a8b3Srsb 3765a59a8b3Srsb static void 3775a59a8b3Srsb vop_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 3785a59a8b3Srsb { 3795a59a8b3Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 3805a59a8b3Srsb char buf[LBUFSZ]; 3815a59a8b3Srsb 3825a59a8b3Srsb if (niceflag) { 3835a59a8b3Srsb (void) printf("%s\n", name); 384757bea67Srsb (void) printf(" operation #ops bytes\n"); 3855a59a8b3Srsb } 3865a59a8b3Srsb 3875a59a8b3Srsb PRINT_VOPSTAT(niceflag, open); 3885a59a8b3Srsb PRINT_VOPSTAT(niceflag, close); 3895a59a8b3Srsb PRINT_VOPSTAT_IO(niceflag, read); 3905a59a8b3Srsb PRINT_VOPSTAT_IO(niceflag, write); 3915a59a8b3Srsb PRINT_VOPSTAT(niceflag, ioctl); 3925a59a8b3Srsb PRINT_VOPSTAT(niceflag, setfl); 3935a59a8b3Srsb PRINT_VOPSTAT(niceflag, getattr); 3945a59a8b3Srsb PRINT_VOPSTAT(niceflag, setattr); 3955a59a8b3Srsb PRINT_VOPSTAT(niceflag, access); 3965a59a8b3Srsb PRINT_VOPSTAT(niceflag, lookup); 3975a59a8b3Srsb PRINT_VOPSTAT(niceflag, create); 3985a59a8b3Srsb PRINT_VOPSTAT(niceflag, remove); 3995a59a8b3Srsb PRINT_VOPSTAT(niceflag, link); 4005a59a8b3Srsb PRINT_VOPSTAT(niceflag, rename); 4015a59a8b3Srsb PRINT_VOPSTAT(niceflag, mkdir); 4025a59a8b3Srsb PRINT_VOPSTAT(niceflag, rmdir); 4035a59a8b3Srsb PRINT_VOPSTAT_IO(niceflag, readdir); 4045a59a8b3Srsb PRINT_VOPSTAT(niceflag, symlink); 4055a59a8b3Srsb PRINT_VOPSTAT(niceflag, readlink); 4065a59a8b3Srsb PRINT_VOPSTAT(niceflag, fsync); 4075a59a8b3Srsb PRINT_VOPSTAT(niceflag, inactive); 4085a59a8b3Srsb PRINT_VOPSTAT(niceflag, fid); 4095a59a8b3Srsb PRINT_VOPSTAT(niceflag, rwlock); 4105a59a8b3Srsb PRINT_VOPSTAT(niceflag, rwunlock); 4115a59a8b3Srsb PRINT_VOPSTAT(niceflag, seek); 4125a59a8b3Srsb PRINT_VOPSTAT(niceflag, cmp); 4135a59a8b3Srsb PRINT_VOPSTAT(niceflag, frlock); 4145a59a8b3Srsb PRINT_VOPSTAT(niceflag, space); 4155a59a8b3Srsb PRINT_VOPSTAT(niceflag, realvp); 4165a59a8b3Srsb PRINT_VOPSTAT(niceflag, getpage); 4175a59a8b3Srsb PRINT_VOPSTAT(niceflag, putpage); 4185a59a8b3Srsb PRINT_VOPSTAT(niceflag, map); 4195a59a8b3Srsb PRINT_VOPSTAT(niceflag, addmap); 4205a59a8b3Srsb PRINT_VOPSTAT(niceflag, delmap); 4215a59a8b3Srsb PRINT_VOPSTAT(niceflag, poll); 4225a59a8b3Srsb PRINT_VOPSTAT(niceflag, dump); 4235a59a8b3Srsb PRINT_VOPSTAT(niceflag, pathconf); 4245a59a8b3Srsb PRINT_VOPSTAT(niceflag, pageio); 4255a59a8b3Srsb PRINT_VOPSTAT(niceflag, dumpctl); 4265a59a8b3Srsb PRINT_VOPSTAT(niceflag, dispose); 4275a59a8b3Srsb PRINT_VOPSTAT(niceflag, getsecattr); 4285a59a8b3Srsb PRINT_VOPSTAT(niceflag, setsecattr); 4295a59a8b3Srsb PRINT_VOPSTAT(niceflag, shrlock); 4305a59a8b3Srsb PRINT_VOPSTAT(niceflag, vnevent); 4315a59a8b3Srsb 4325a59a8b3Srsb if (niceflag) { 4335a59a8b3Srsb /* Make it easier on the eyes */ 4345a59a8b3Srsb (void) printf("\n"); 4355a59a8b3Srsb } else { 4365a59a8b3Srsb (void) printf("%s\n", name); 4375a59a8b3Srsb } 4385a59a8b3Srsb } 4395a59a8b3Srsb 4405a59a8b3Srsb 4415a59a8b3Srsb /* 4425a59a8b3Srsb * Retrieve the vopstats. If kspp (pointer to kstat_t pointer) is non-NULL, 4435a59a8b3Srsb * then pass it back to the caller. 4445a59a8b3Srsb * 4455a59a8b3Srsb * Returns 0 on success, non-zero on failure. 4465a59a8b3Srsb */ 4475a59a8b3Srsb int 4485a59a8b3Srsb get_vopstats(kstat_ctl_t *kc, char *ksname, vopstats_t *vsp, kstat_t **kspp) 4495a59a8b3Srsb { 4505a59a8b3Srsb kstat_t *ksp; 4515a59a8b3Srsb 4525a59a8b3Srsb if (ksname == NULL || *ksname == 0) 4535a59a8b3Srsb return (1); 4545a59a8b3Srsb 4555a59a8b3Srsb errno = 0; 4565a59a8b3Srsb /* wait for a possibly up-to-date chain */ 4575a59a8b3Srsb while (kstat_chain_update(kc) == -1) { 4585a59a8b3Srsb if (errno == EAGAIN) { 4595a59a8b3Srsb errno = 0; 4605a59a8b3Srsb (void) poll(NULL, 0, RETRY_DELAY); 4615a59a8b3Srsb continue; 4625a59a8b3Srsb } 463757bea67Srsb perror("kstat_chain_update"); 4645a59a8b3Srsb exit(1); 4655a59a8b3Srsb } 4665a59a8b3Srsb 4675a59a8b3Srsb if ((ksp = kstat_lookup(kc, NULL, -1, ksname)) == NULL) { 4685a59a8b3Srsb return (1); 4695a59a8b3Srsb } 4705a59a8b3Srsb 4715a59a8b3Srsb if (kstat_read(kc, ksp, vsp) == -1) { 4725a59a8b3Srsb return (1); 4735a59a8b3Srsb } 4745a59a8b3Srsb 4755a59a8b3Srsb if (kspp) 4765a59a8b3Srsb *kspp = ksp; 4775a59a8b3Srsb 4785a59a8b3Srsb return (0); 4795a59a8b3Srsb } 4805a59a8b3Srsb 4815a59a8b3Srsb /* 4825a59a8b3Srsb * Given a file system type name, determine if it's part of the 4835a59a8b3Srsb * exception list of file systems that are not to be displayed. 4845a59a8b3Srsb */ 4855a59a8b3Srsb int 4865a59a8b3Srsb is_exception(char *fsname) 4875a59a8b3Srsb { 4885a59a8b3Srsb char **xlp; /* Pointer into the exception list */ 4895a59a8b3Srsb 4905a59a8b3Srsb static char *exception_list[] = { 4915a59a8b3Srsb "specfs", 4925a59a8b3Srsb "fifofs", 4935a59a8b3Srsb "fd", 4945a59a8b3Srsb "swapfs", 4955a59a8b3Srsb "ctfs", 4965a59a8b3Srsb "objfs", 4975a59a8b3Srsb "nfsdyn", 4985a59a8b3Srsb NULL 4995a59a8b3Srsb }; 5005a59a8b3Srsb 5015a59a8b3Srsb for (xlp = &exception_list[0]; *xlp != NULL; xlp++) { 5025a59a8b3Srsb if (strcmp(fsname, *xlp) == 0) 5035a59a8b3Srsb return (1); 5045a59a8b3Srsb } 5055a59a8b3Srsb 5065a59a8b3Srsb return (0); 5075a59a8b3Srsb } 5085a59a8b3Srsb 5095a59a8b3Srsb /* 5105a59a8b3Srsb * Plain and simple, build an array of names for fstypes 5115a59a8b3Srsb * Returns 0, if it encounters a problem. 5125a59a8b3Srsb */ 5135a59a8b3Srsb int 5145a59a8b3Srsb build_fstype_list(char ***fstypep) 5155a59a8b3Srsb { 5165a59a8b3Srsb int i; 5175a59a8b3Srsb int nfstype; 5185a59a8b3Srsb char buf[FSTYPSZ + 1]; 5195a59a8b3Srsb 5205a59a8b3Srsb if ((nfstype = sysfs(GETNFSTYP)) < 0) { 521757bea67Srsb perror("sysfs(GETNFSTYP)"); 5225a59a8b3Srsb return (0); 5235a59a8b3Srsb } 5245a59a8b3Srsb 5255a59a8b3Srsb if ((*fstypep = calloc(nfstype, sizeof (char *))) == NULL) { 526757bea67Srsb perror("calloc() fstypes"); 5275a59a8b3Srsb return (0); 5285a59a8b3Srsb } 5295a59a8b3Srsb 5305a59a8b3Srsb for (i = 1; i < nfstype; i++) { 5315a59a8b3Srsb if (sysfs(GETFSTYP, i, buf) < 0) { 532757bea67Srsb perror("sysfs(GETFSTYP)"); 5335a59a8b3Srsb return (0); 5345a59a8b3Srsb } 5355a59a8b3Srsb 5365a59a8b3Srsb if (buf[0] == 0) 5375a59a8b3Srsb continue; 5385a59a8b3Srsb 5395a59a8b3Srsb /* If this is part of the exception list, move on */ 5405a59a8b3Srsb if (is_exception(buf)) 5415a59a8b3Srsb continue; 5425a59a8b3Srsb 5435a59a8b3Srsb if (((*fstypep)[i] = strdup(buf)) == NULL) { 544757bea67Srsb perror("strdup() fstype name"); 5455a59a8b3Srsb return (0); 5465a59a8b3Srsb } 5475a59a8b3Srsb } 5485a59a8b3Srsb 5495a59a8b3Srsb return (i); 5505a59a8b3Srsb } 5515a59a8b3Srsb 5525a59a8b3Srsb /* 5535a59a8b3Srsb * After we're done with getopts(), process the rest of the 5545a59a8b3Srsb * operands. We have three cases and this is the priority: 5555a59a8b3Srsb * 5565a59a8b3Srsb * 1) [ operand... ] interval count 5575a59a8b3Srsb * 2) [ operand... ] interval 5585a59a8b3Srsb * 3) [ operand... ] 5595a59a8b3Srsb * 5605a59a8b3Srsb * The trick is that any of the operands might start with a number or even 5615a59a8b3Srsb * be made up exclusively of numbers (and we have to handle negative numbers 5625a59a8b3Srsb * in case a user/script gets out of line). If we find two operands at the 5635a59a8b3Srsb * end of the list then we claim case 1. If we find only one operand at the 5645a59a8b3Srsb * end made up only of number, then we claim case 2. Otherwise, case 3. 5655a59a8b3Srsb * BTW, argc, argv don't change. 5665a59a8b3Srsb */ 5675a59a8b3Srsb int 5685a59a8b3Srsb parse_operands( 5695a59a8b3Srsb int argc, 5705a59a8b3Srsb char **argv, 5715a59a8b3Srsb int optind, 5725a59a8b3Srsb long *interval, 5735a59a8b3Srsb long *count, 5745a59a8b3Srsb entity_t **entityp) /* Array of stat-able entities */ 5755a59a8b3Srsb { 5765a59a8b3Srsb int nentities = 0; /* Number of entities found */ 5775a59a8b3Srsb int out_of_range; /* Set if 2nd-to-last operand out-of-range */ 5785a59a8b3Srsb 5795a59a8b3Srsb if (argc == optind) 5805a59a8b3Srsb return (nentities); /* None found, returns 0 */ 5815a59a8b3Srsb /* 5825a59a8b3Srsb * We know exactly what the maximum number of entities is going 5835a59a8b3Srsb * to be: argc - optind 5845a59a8b3Srsb */ 5855a59a8b3Srsb if ((*entityp = calloc((argc - optind), sizeof (entity_t))) == NULL) { 586757bea67Srsb perror("calloc() entities"); 5875a59a8b3Srsb return (-1); 5885a59a8b3Srsb } 5895a59a8b3Srsb 5905a59a8b3Srsb for (/* void */; argc > optind; optind++) { 5915a59a8b3Srsb char *endptr; 5925a59a8b3Srsb 5935a59a8b3Srsb /* If we have more than two operands left to process */ 5945a59a8b3Srsb if ((argc - optind) > 2) { 5955a59a8b3Srsb (*entityp)[nentities++].e_name = strdup(argv[optind]); 5965a59a8b3Srsb continue; 5975a59a8b3Srsb } 5985a59a8b3Srsb 5995a59a8b3Srsb /* If we're here, then we only have one or two operands left */ 6005a59a8b3Srsb errno = 0; 6015a59a8b3Srsb out_of_range = 0; 6025a59a8b3Srsb *interval = strtol(argv[optind], &endptr, 10); 6035a59a8b3Srsb if (*endptr && !isdigit((int)*endptr)) { 6045a59a8b3Srsb /* Operand was not a number */ 6055a59a8b3Srsb (*entityp)[nentities++].e_name = strdup(argv[optind]); 6065a59a8b3Srsb continue; 6075a59a8b3Srsb } else if (errno == ERANGE || *interval <= 0 || 6085a59a8b3Srsb *interval > MAXLONG) { 6095a59a8b3Srsb /* Operand was a number, just out of range */ 6105a59a8b3Srsb out_of_range++; 6115a59a8b3Srsb } 6125a59a8b3Srsb 6135a59a8b3Srsb /* 6145a59a8b3Srsb * The last operand we saw was a number. If it happened to 6155a59a8b3Srsb * be the last operand, then it is the interval... 6165a59a8b3Srsb */ 6175a59a8b3Srsb if ((argc - optind) == 1) { 6185a59a8b3Srsb /* ...but we need to check the range. */ 6195a59a8b3Srsb if (out_of_range) { 6205a59a8b3Srsb (void) fprintf(stderr, gettext( 6215a59a8b3Srsb "interval must be between 1 and " 6225a59a8b3Srsb "%ld (inclusive)\n"), MAXLONG); 6235a59a8b3Srsb return (-1); 6245a59a8b3Srsb } else { 6255a59a8b3Srsb /* 6265a59a8b3Srsb * The value of the interval is valid. Set 6275a59a8b3Srsb * count to something really big so it goes 6285a59a8b3Srsb * virtually forever. 6295a59a8b3Srsb */ 6305a59a8b3Srsb *count = MAXLONG; 6315a59a8b3Srsb break; 6325a59a8b3Srsb } 6335a59a8b3Srsb } 6345a59a8b3Srsb 6355a59a8b3Srsb /* 6365a59a8b3Srsb * At this point, we *might* have the interval, but if the 6375a59a8b3Srsb * next operand isn't a number, then we don't have either 6385a59a8b3Srsb * the interval nor the count. Both must be set to the 6395a59a8b3Srsb * defaults. In that case, both the current and the previous 6405a59a8b3Srsb * operands are stat-able entities. 6415a59a8b3Srsb */ 6425a59a8b3Srsb errno = 0; 6435a59a8b3Srsb *count = strtol(argv[optind + 1], &endptr, 10); 6445a59a8b3Srsb if (*endptr && !isdigit((int)*endptr)) { 6455a59a8b3Srsb /* 6465a59a8b3Srsb * Faked out! The last operand wasn't a number so 6475a59a8b3Srsb * the current and previous operands should be 6485a59a8b3Srsb * stat-able entities. We also need to reset interval. 6495a59a8b3Srsb */ 6505a59a8b3Srsb *interval = 0; 6515a59a8b3Srsb (*entityp)[nentities++].e_name = strdup(argv[optind++]); 6525a59a8b3Srsb (*entityp)[nentities++].e_name = strdup(argv[optind++]); 6535a59a8b3Srsb } else if (out_of_range || errno == ERANGE || *count <= 0) { 6545a59a8b3Srsb (void) fprintf(stderr, gettext( 6555a59a8b3Srsb "Both interval and count must be between 1 " 6565a59a8b3Srsb "and %ld (inclusive)\n"), MAXLONG); 6575a59a8b3Srsb return (-1); 6585a59a8b3Srsb } 6595a59a8b3Srsb break; /* Done! */ 6605a59a8b3Srsb } 6615a59a8b3Srsb return (nentities); 6625a59a8b3Srsb } 6635a59a8b3Srsb 6645a59a8b3Srsb /* 6655a59a8b3Srsb * set_mntpt() looks at the entity's name (e_name) and finds its 6665a59a8b3Srsb * mountpoint. To do this, we need to build a list of mountpoints 6675a59a8b3Srsb * from /etc/mnttab. We only need to do this once and we don't do it 6685a59a8b3Srsb * if we don't need to look at any mountpoints. 6695a59a8b3Srsb * Returns 0 on success, non-zero if it couldn't find a mount-point. 6705a59a8b3Srsb */ 6715a59a8b3Srsb int 6725a59a8b3Srsb set_mntpt(entity_t *ep) 6735a59a8b3Srsb { 6745a59a8b3Srsb static struct mnt { 6755a59a8b3Srsb struct mnt *m_next; 6765a59a8b3Srsb char *m_mntpt; 6775a59a8b3Srsb ulong_t m_fsid; /* From statvfs(), set only as needed */ 6785a59a8b3Srsb } *mnt_list = NULL; /* Linked list of mount-points */ 6795a59a8b3Srsb struct mnt *mntp; 68006f33e8dSrsb struct statvfs64 statvfsbuf; 6815a59a8b3Srsb char *original_name = ep->e_name; 6825a59a8b3Srsb char path[PATH_MAX]; 6835a59a8b3Srsb 6845a59a8b3Srsb if (original_name == NULL) /* Shouldn't happen */ 6855a59a8b3Srsb return (1); 6865a59a8b3Srsb 6875a59a8b3Srsb /* We only set up mnt_list the first time this is called */ 6885a59a8b3Srsb if (mnt_list == NULL) { 6895a59a8b3Srsb FILE *fp; 6905a59a8b3Srsb struct mnttab mnttab; 6915a59a8b3Srsb 6925a59a8b3Srsb if ((fp = fopen(MNTTAB, "r")) == NULL) { 6935a59a8b3Srsb perror(MNTTAB); 6945a59a8b3Srsb return (1); 6955a59a8b3Srsb } 6965a59a8b3Srsb resetmnttab(fp); 6975a59a8b3Srsb /* 6985a59a8b3Srsb * We insert at the front of the list so that when we 6995a59a8b3Srsb * search entries we'll have the last mounted entries 7005a59a8b3Srsb * first in the list so that we can match the longest 7015a59a8b3Srsb * mountpoint. 7025a59a8b3Srsb */ 7035a59a8b3Srsb while (getmntent(fp, &mnttab) == 0) { 7045a59a8b3Srsb if ((mntp = malloc(sizeof (*mntp))) == NULL) { 705757bea67Srsb perror("malloc() mount list"); 7065a59a8b3Srsb return (1); 7075a59a8b3Srsb } 7085a59a8b3Srsb mntp->m_mntpt = strdup(mnttab.mnt_mountp); 7095a59a8b3Srsb mntp->m_next = mnt_list; 7105a59a8b3Srsb mnt_list = mntp; 7115a59a8b3Srsb } 7125a59a8b3Srsb (void) fclose(fp); 7135a59a8b3Srsb } 7145a59a8b3Srsb 7155a59a8b3Srsb if (realpath(original_name, path) == NULL) { 7165a59a8b3Srsb perror(original_name); 7175a59a8b3Srsb return (1); 7185a59a8b3Srsb } 7195a59a8b3Srsb 7205a59a8b3Srsb /* 7215a59a8b3Srsb * Now that we have the path, walk through the mnt_list and 7225a59a8b3Srsb * look for the first (best) match. 7235a59a8b3Srsb */ 7245a59a8b3Srsb for (mntp = mnt_list; mntp; mntp = mntp->m_next) { 7255a59a8b3Srsb if (strncmp(path, mntp->m_mntpt, strlen(mntp->m_mntpt)) == 0) { 7265a59a8b3Srsb if (mntp->m_fsid == 0) { 72706f33e8dSrsb if (statvfs64(mntp->m_mntpt, &statvfsbuf)) { 7285a59a8b3Srsb /* Can't statvfs so no match */ 7295a59a8b3Srsb continue; 7305a59a8b3Srsb } else { 7315a59a8b3Srsb mntp->m_fsid = statvfsbuf.f_fsid; 7325a59a8b3Srsb } 7335a59a8b3Srsb } 7345a59a8b3Srsb 7355a59a8b3Srsb if (ep->e_fsid != mntp->m_fsid) { 7365a59a8b3Srsb /* No match - Move on */ 7375a59a8b3Srsb continue; 7385a59a8b3Srsb } 7395a59a8b3Srsb 7405a59a8b3Srsb break; 7415a59a8b3Srsb } 7425a59a8b3Srsb } 7435a59a8b3Srsb 7445a59a8b3Srsb if (mntp == NULL) { 7455a59a8b3Srsb (void) fprintf(stderr, gettext( 7465a59a8b3Srsb "Can't find mount point for %s\n"), path); 7475a59a8b3Srsb return (1); 7485a59a8b3Srsb } 7495a59a8b3Srsb 7505a59a8b3Srsb ep->e_name = strdup(mntp->m_mntpt); 7515a59a8b3Srsb free(original_name); 7525a59a8b3Srsb return (0); 7535a59a8b3Srsb } 7545a59a8b3Srsb 7555a59a8b3Srsb /* 7565a59a8b3Srsb * We have an array of entities that are potentially stat-able. Using 7575a59a8b3Srsb * the name (e_name) of the entity, attempt to construct a ksname suitable 7585a59a8b3Srsb * for use by kstat_lookup(3kstat) and fill it into the e_ksname member. 7595a59a8b3Srsb * 7605a59a8b3Srsb * We check the e_name against the list of file system types. If there is 7615a59a8b3Srsb * no match then test to see if the path is valid. If the path is valid, 7625a59a8b3Srsb * then determine the mountpoint. 7635a59a8b3Srsb */ 7645a59a8b3Srsb void 7655a59a8b3Srsb set_ksnames(entity_t *entities, int nentities, char **fstypes, int nfstypes) 7665a59a8b3Srsb { 7675a59a8b3Srsb int i, j; 76806f33e8dSrsb struct statvfs64 statvfsbuf; 7695a59a8b3Srsb 7705a59a8b3Srsb for (i = 0; i < nentities; i++) { 7715a59a8b3Srsb entity_t *ep = &entities[i]; 7725a59a8b3Srsb 7735a59a8b3Srsb /* Check the name against the list of fstypes */ 7745a59a8b3Srsb for (j = 1; j < nfstypes; j++) { 7755a59a8b3Srsb if (fstypes[j] && ep->e_name && 7765a59a8b3Srsb strcmp(ep->e_name, fstypes[j]) == 0) { 7775a59a8b3Srsb /* It's a file system type */ 7785a59a8b3Srsb ep->e_type = ENTYPE_FSTYPE; 779f5cd957fSRobert Harris (void) snprintf(ep->e_ksname, KSTAT_STRLEN, 780f5cd957fSRobert Harris "%s%s", VOPSTATS_STR, ep->e_name); 7815a59a8b3Srsb /* Now allocate the vopstats array */ 7825a59a8b3Srsb ep->e_vs = calloc(VS_SIZE, sizeof (vopstats_t)); 7835a59a8b3Srsb if (entities[i].e_vs == NULL) { 784757bea67Srsb perror("calloc() fstype vopstats"); 7855a59a8b3Srsb exit(1); 7865a59a8b3Srsb } 7875a59a8b3Srsb break; 7885a59a8b3Srsb } 7895a59a8b3Srsb } 7905a59a8b3Srsb if (j < nfstypes) /* Found it! */ 7915a59a8b3Srsb continue; 7925a59a8b3Srsb 7935a59a8b3Srsb /* 7945a59a8b3Srsb * If the entity in the exception list of fstypes, then 7955a59a8b3Srsb * null out the entry so it isn't displayed and move along. 7965a59a8b3Srsb */ 7975a59a8b3Srsb if (is_exception(ep->e_name)) { 7985a59a8b3Srsb ep->e_ksname[0] = 0; 7995a59a8b3Srsb continue; 8005a59a8b3Srsb } 8015a59a8b3Srsb 8025a59a8b3Srsb /* If we didn't find it, see if it's a path */ 80306f33e8dSrsb if (ep->e_name == NULL || statvfs64(ep->e_name, &statvfsbuf)) { 8045a59a8b3Srsb /* Error - Make sure the entry is nulled out */ 8055a59a8b3Srsb ep->e_ksname[0] = 0; 8065a59a8b3Srsb continue; 8075a59a8b3Srsb } 8085a59a8b3Srsb (void) snprintf(ep->e_ksname, KSTAT_STRLEN, "%s%lx", 8095a59a8b3Srsb VOPSTATS_STR, statvfsbuf.f_fsid); 8105a59a8b3Srsb ep->e_fsid = statvfsbuf.f_fsid; 8115a59a8b3Srsb if (set_mntpt(ep)) { 8125a59a8b3Srsb (void) fprintf(stderr, 8135a59a8b3Srsb gettext("Can't determine type of \"%s\"\n"), 8145a59a8b3Srsb ep->e_name ? ep->e_name : gettext("<NULL>")); 8155a59a8b3Srsb } else { 8165a59a8b3Srsb ep->e_type = ENTYPE_MNTPT; 8175a59a8b3Srsb } 8185a59a8b3Srsb 8195a59a8b3Srsb /* Now allocate the vopstats array */ 8205a59a8b3Srsb ep->e_vs = calloc(VS_SIZE, sizeof (vopstats_t)); 8215a59a8b3Srsb if (entities[i].e_vs == NULL) { 822757bea67Srsb perror("calloc() vopstats array"); 8235a59a8b3Srsb exit(1); 8245a59a8b3Srsb } 8255a59a8b3Srsb } 8265a59a8b3Srsb } 8275a59a8b3Srsb 8285a59a8b3Srsb /* 8295a59a8b3Srsb * The idea is that 'dspfunc' should only be modified from the default 8305a59a8b3Srsb * once since the display options are mutually exclusive. If 'dspfunc' 8315a59a8b3Srsb * only contains the default display function, then all is good and we 8325a59a8b3Srsb * can set it to the new display function. Otherwise, bail. 8335a59a8b3Srsb */ 8345a59a8b3Srsb void 8355a59a8b3Srsb set_dispfunc( 8365a59a8b3Srsb void (**dspfunc)(char *, vopstats_t *, vopstats_t *, int), 8375a59a8b3Srsb void (*newfunc)(char *, vopstats_t *, vopstats_t *, int)) 8385a59a8b3Srsb { 8395a59a8b3Srsb if (*dspfunc != dflt_display) { 8405a59a8b3Srsb (void) fprintf(stderr, gettext( 8415a59a8b3Srsb "%s: Display options -{a|f|i|n|v} are mutually exclusive\n"), 8425a59a8b3Srsb cmdname); 8435a59a8b3Srsb usage(); 8445a59a8b3Srsb } 8455a59a8b3Srsb *dspfunc = newfunc; 8465a59a8b3Srsb } 8475a59a8b3Srsb 8485a59a8b3Srsb int 8495a59a8b3Srsb main(int argc, char *argv[]) 8505a59a8b3Srsb { 8515a59a8b3Srsb int c; 8525a59a8b3Srsb int i, j; /* Generic counters */ 8535a59a8b3Srsb int nentities_found; 854*4944376cSJohn Levon int linesout = 0; /* Keeps track of lines printed */ 8555a59a8b3Srsb int printhdr = 0; /* Print a header? 0 = no, 1 = yes */ 8565a59a8b3Srsb int nfstypes; /* Number of fstypes */ 8575a59a8b3Srsb int dispflag = 0; /* Flags for display control */ 8585a59a8b3Srsb long count = 0; /* Number of iterations for display */ 85900c76d6fStc35445 int forever; /* Run forever */ 8605a59a8b3Srsb long interval = 0; 8615a59a8b3Srsb boolean_t fstypes_only = B_FALSE; /* Display fstypes only */ 8625a59a8b3Srsb char **fstypes; /* Array of names of all fstypes */ 8635a59a8b3Srsb int nentities; /* Number of stat-able entities */ 8645a59a8b3Srsb entity_t *entities; /* Array of stat-able entities */ 8655a59a8b3Srsb kstat_ctl_t *kc; 8665a59a8b3Srsb void (*dfunc)(char *, vopstats_t *, vopstats_t *, int) = dflt_display; 86700c76d6fStc35445 hrtime_t start_n; /* Start time */ 86800c76d6fStc35445 hrtime_t period_n; /* Interval in nanoseconds */ 8695a59a8b3Srsb 8705a59a8b3Srsb extern int optind; 8715a59a8b3Srsb 87213c7b6acSrsb (void) setlocale(LC_ALL, ""); 87313c7b6acSrsb #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 87413c7b6acSrsb #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 87513c7b6acSrsb #endif 87613c7b6acSrsb (void) textdomain(TEXT_DOMAIN); 87713c7b6acSrsb 878f5cd957fSRobert Harris /* Don't let buffering interfere with piped output. */ 879f5cd957fSRobert Harris (void) setvbuf(stdout, NULL, _IOLBF, 0); 880f5cd957fSRobert Harris 8815a59a8b3Srsb cmdname = argv[0]; 8825a59a8b3Srsb while ((c = getopt(argc, argv, OPTIONS)) != EOF) { 8835a59a8b3Srsb switch (c) { 8845a59a8b3Srsb 8855a59a8b3Srsb default: 8865a59a8b3Srsb usage(); 8875a59a8b3Srsb break; 8885a59a8b3Srsb 8893a401a6bSrsb case 'F': /* Only display available FStypes */ 8903a401a6bSrsb fstypes_only = B_TRUE; 8913a401a6bSrsb break; 8923a401a6bSrsb 8933a401a6bSrsb #if PARSABLE_OUTPUT 8945a59a8b3Srsb case 'P': /* Parsable output */ 8955a59a8b3Srsb dispflag |= DISP_RAW; 8965a59a8b3Srsb break; 8973a401a6bSrsb #endif /* PARSABLE_OUTPUT */ 8985a59a8b3Srsb 8995a59a8b3Srsb case 'T': /* Timestamp */ 9005a59a8b3Srsb if (optarg) { 9015a59a8b3Srsb if (strcmp(optarg, "u") == 0) { 902*4944376cSJohn Levon timestamp_fmt = UDATE; 9035a59a8b3Srsb } else if (strcmp(optarg, "d") == 0) { 904*4944376cSJohn Levon timestamp_fmt = DDATE; 9055a59a8b3Srsb } 9065a59a8b3Srsb } 9075a59a8b3Srsb 9085a59a8b3Srsb /* If it was never set properly... */ 909*4944376cSJohn Levon if (timestamp_fmt == NODATE) { 910f5cd957fSRobert Harris (void) fprintf(stderr, gettext("%s: -T option " 911f5cd957fSRobert Harris "requires either 'u' or 'd'\n"), cmdname); 9125a59a8b3Srsb usage(); 9135a59a8b3Srsb } 9145a59a8b3Srsb break; 9155a59a8b3Srsb 9165a59a8b3Srsb case 'a': 9175a59a8b3Srsb set_dispfunc(&dfunc, attr_display); 9185a59a8b3Srsb break; 9195a59a8b3Srsb 9205a59a8b3Srsb case 'f': 9215a59a8b3Srsb set_dispfunc(&dfunc, vop_display); 9225a59a8b3Srsb break; 9235a59a8b3Srsb 9245a59a8b3Srsb case 'i': 9255a59a8b3Srsb set_dispfunc(&dfunc, io_display); 9265a59a8b3Srsb break; 9275a59a8b3Srsb 9285a59a8b3Srsb case 'n': 9295a59a8b3Srsb set_dispfunc(&dfunc, naming_display); 9305a59a8b3Srsb break; 9315a59a8b3Srsb 9325a59a8b3Srsb case 'v': 9335a59a8b3Srsb set_dispfunc(&dfunc, vm_display); 9345a59a8b3Srsb break; 9355a59a8b3Srsb } 9365a59a8b3Srsb } 9375a59a8b3Srsb 9383a401a6bSrsb #if PARSABLE_OUTPUT 939*4944376cSJohn Levon if ((dispflag & DISP_RAW) && (timestamp_fmt != NODATE)) { 9405a59a8b3Srsb (void) fprintf(stderr, gettext( 9415a59a8b3Srsb "-P and -T options are mutually exclusive\n")); 9425a59a8b3Srsb usage(); 9435a59a8b3Srsb } 9443a401a6bSrsb #endif /* PARSABLE_OUTPUT */ 9455a59a8b3Srsb 9465a59a8b3Srsb /* Gather the list of filesystem types */ 9475a59a8b3Srsb if ((nfstypes = build_fstype_list(&fstypes)) == 0) { 9485a59a8b3Srsb (void) fprintf(stderr, 9495a59a8b3Srsb gettext("Can't build list of fstypes\n")); 9505a59a8b3Srsb exit(1); 9515a59a8b3Srsb } 9525a59a8b3Srsb 9535a59a8b3Srsb nentities = parse_operands( 9545a59a8b3Srsb argc, argv, optind, &interval, &count, &entities); 95500c76d6fStc35445 forever = count == MAXLONG; 95600c76d6fStc35445 period_n = (hrtime_t)interval * NANOSEC; 9575a59a8b3Srsb 9585a59a8b3Srsb if (nentities == -1) /* Set of operands didn't parse properly */ 9595a59a8b3Srsb usage(); 9605a59a8b3Srsb 9613a401a6bSrsb if ((nentities == 0) && (fstypes_only == B_FALSE)) { 9623a401a6bSrsb (void) fprintf(stderr, gettext( 9633a401a6bSrsb "Must specify -F or at least one fstype or mount point\n")); 9643a401a6bSrsb usage(); 9653a401a6bSrsb } 9665a59a8b3Srsb 9673a401a6bSrsb if ((nentities > 0) && (fstypes_only == B_TRUE)) { 9683a401a6bSrsb (void) fprintf(stderr, gettext( 9693a401a6bSrsb "Cannot use -F with fstypes or mount points\n")); 9703a401a6bSrsb usage(); 9713a401a6bSrsb } 9723a401a6bSrsb 9733a401a6bSrsb /* 9743a401a6bSrsb * If we had no operands (except for interval/count) and we 9753a401a6bSrsb * requested FStypes only (-F), then fill in the entities[] 9763a401a6bSrsb * array with all available fstypes. 9773a401a6bSrsb */ 9783a401a6bSrsb if ((nentities == 0) && (fstypes_only == B_TRUE)) { 9795a59a8b3Srsb if ((entities = calloc(nfstypes, sizeof (entity_t))) == NULL) { 980757bea67Srsb perror("calloc() fstype stats"); 9815a59a8b3Srsb exit(1); 9825a59a8b3Srsb } 9835a59a8b3Srsb 9845a59a8b3Srsb for (i = 1; i < nfstypes; i++) { 9855a59a8b3Srsb if (fstypes[i]) { 9865a59a8b3Srsb entities[nentities].e_name = strdup(fstypes[i]); 9875a59a8b3Srsb nentities++; 9885a59a8b3Srsb } 9895a59a8b3Srsb } 9905a59a8b3Srsb } 9915a59a8b3Srsb 9925a59a8b3Srsb set_ksnames(entities, nentities, fstypes, nfstypes); 9935a59a8b3Srsb 9945a59a8b3Srsb if ((kc = kstat_open()) == NULL) { 995757bea67Srsb perror("kstat_open"); 9965a59a8b3Srsb exit(1); 9975a59a8b3Srsb } 9985a59a8b3Srsb 99900c76d6fStc35445 /* Set start time */ 100000c76d6fStc35445 start_n = gethrtime(); 100100c76d6fStc35445 1002*4944376cSJohn Levon /* Initial timestamp */ 1003*4944376cSJohn Levon if (timestamp_fmt != NODATE) { 1004*4944376cSJohn Levon print_timestamp(); 1005*4944376cSJohn Levon linesout++; 1006*4944376cSJohn Levon } 1007*4944376cSJohn Levon 10085a59a8b3Srsb /* 10095a59a8b3Srsb * The following loop walks through the entities[] list to "prime 10105a59a8b3Srsb * the pump" 10115a59a8b3Srsb */ 1012*4944376cSJohn Levon for (j = 0, printhdr = 1; j < nentities; j++) { 10135a59a8b3Srsb entity_t *ent = &entities[j]; 10145a59a8b3Srsb vopstats_t *vsp = &ent->e_vs[CUR_INDEX]; 10155a59a8b3Srsb kstat_t *ksp = NULL; 10165a59a8b3Srsb 10175a59a8b3Srsb if (get_vopstats(kc, ent->e_ksname, vsp, &ksp) == 0) { 10185a59a8b3Srsb (*dfunc)(ent->e_name, NULL, vsp, 1019*4944376cSJohn Levon dispflag_policy(printhdr, dispflag)); 10205a59a8b3Srsb linesout++; 10215a59a8b3Srsb } else { 10225a59a8b3Srsb /* 10235a59a8b3Srsb * If we can't find it the first time through, then 10245a59a8b3Srsb * get rid of it. 10255a59a8b3Srsb */ 10265a59a8b3Srsb entities[j].e_ksname[0] = 0; 10275a59a8b3Srsb 10285a59a8b3Srsb /* 10293a401a6bSrsb * If we're only displaying FStypes (-F) then don't 10305a59a8b3Srsb * complain about any file systems that might not 10315a59a8b3Srsb * be loaded. Otherwise, let the user know that 10325a59a8b3Srsb * he chose poorly. 10335a59a8b3Srsb */ 10345a59a8b3Srsb if (fstypes_only == B_FALSE) { 10355a59a8b3Srsb (void) fprintf(stderr, gettext( 10365a59a8b3Srsb "No statistics available for %s\n"), 10375a59a8b3Srsb entities[j].e_name); 10385a59a8b3Srsb } 10395a59a8b3Srsb } 1040*4944376cSJohn Levon printhdr = 0; 10415a59a8b3Srsb } 10425a59a8b3Srsb 104300c76d6fStc35445 if (count > 1) 104400c76d6fStc35445 /* Set up signal handler for SIGCONT */ 104500c76d6fStc35445 if (signal(SIGCONT, cont_handler) == SIG_ERR) 104600c76d6fStc35445 fail(1, "signal failed"); 104700c76d6fStc35445 104800c76d6fStc35445 10495a59a8b3Srsb BUMP_INDEX(); /* Swap the previous/current indices */ 105000c76d6fStc35445 i = 1; 105100c76d6fStc35445 while (forever || i++ <= count) { 10525a59a8b3Srsb /* 10535a59a8b3Srsb * No telling how many lines will be printed in any interval. 10545a59a8b3Srsb * There should be a minimum of HEADERLINES between any 10555a59a8b3Srsb * header. If we exceed that, no big deal. 10565a59a8b3Srsb */ 10575a59a8b3Srsb if (linesout > HEADERLINES) { 10585a59a8b3Srsb linesout = 0; 10595a59a8b3Srsb printhdr = 1; 10605a59a8b3Srsb } 106100c76d6fStc35445 /* Have a kip */ 106200c76d6fStc35445 sleep_until(&start_n, period_n, forever, &caught_cont); 10635a59a8b3Srsb 1064*4944376cSJohn Levon if (timestamp_fmt != NODATE) { 1065*4944376cSJohn Levon print_timestamp(); 10665a59a8b3Srsb linesout++; 10675a59a8b3Srsb } 10685a59a8b3Srsb 10695a59a8b3Srsb for (j = 0, nentities_found = 0; j < nentities; j++) { 10705a59a8b3Srsb entity_t *ent = &entities[j]; 10715a59a8b3Srsb 10725a59a8b3Srsb /* 10735a59a8b3Srsb * If this entry has been cleared, don't attempt 10745a59a8b3Srsb * to process it. 10755a59a8b3Srsb */ 10765a59a8b3Srsb if (ent->e_ksname[0] == 0) { 10775a59a8b3Srsb continue; 10785a59a8b3Srsb } 10795a59a8b3Srsb 10805a59a8b3Srsb if (get_vopstats(kc, ent->e_ksname, 10815a59a8b3Srsb &ent->e_vs[CUR_INDEX], NULL) == 0) { 10825a59a8b3Srsb (*dfunc)(ent->e_name, &ent->e_vs[PREV_INDEX], 10835a59a8b3Srsb &ent->e_vs[CUR_INDEX], 10845a59a8b3Srsb dispflag_policy(printhdr, dispflag)); 10855a59a8b3Srsb linesout++; 10865a59a8b3Srsb nentities_found++; 10875a59a8b3Srsb } else { 10885a59a8b3Srsb if (ent->e_type == ENTYPE_MNTPT) { 10895a59a8b3Srsb (void) printf(gettext( 10905a59a8b3Srsb "<<mount point no longer " 10915a59a8b3Srsb "available: %s>>\n"), ent->e_name); 10925a59a8b3Srsb } else if (ent->e_type == ENTYPE_FSTYPE) { 10935a59a8b3Srsb (void) printf(gettext( 10945a59a8b3Srsb "<<file system module no longer " 10955a59a8b3Srsb "loaded: %s>>\n"), ent->e_name); 10965a59a8b3Srsb } else { 10975a59a8b3Srsb (void) printf(gettext( 10985a59a8b3Srsb "<<%s no longer available>>\n"), 10995a59a8b3Srsb ent->e_name); 11005a59a8b3Srsb } 11015a59a8b3Srsb /* Disable this so it doesn't print again */ 11025a59a8b3Srsb ent->e_ksname[0] = 0; 11035a59a8b3Srsb } 11045a59a8b3Srsb printhdr = 0; /* Always shut this off */ 11055a59a8b3Srsb } 11065a59a8b3Srsb BUMP_INDEX(); /* Bump the previous/current indices */ 11075a59a8b3Srsb 11085a59a8b3Srsb /* 11095a59a8b3Srsb * If the entities we were observing are no longer there 11105a59a8b3Srsb * (file system modules unloaded, file systems unmounted) 11115a59a8b3Srsb * then we're done. 11125a59a8b3Srsb */ 11135a59a8b3Srsb if (nentities_found == 0) 11145a59a8b3Srsb break; 11155a59a8b3Srsb } 11165a59a8b3Srsb 11175a59a8b3Srsb return (0); 11185a59a8b3Srsb } 1119