11488Srsb /* 21488Srsb * CDDL HEADER START 31488Srsb * 41488Srsb * The contents of this file are subject to the terms of the 51488Srsb * Common Development and Distribution License (the "License"). 61488Srsb * You may not use this file except in compliance with the License. 71488Srsb * 81488Srsb * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91488Srsb * or http://www.opensolaris.org/os/licensing. 101488Srsb * See the License for the specific language governing permissions 111488Srsb * and limitations under the License. 121488Srsb * 131488Srsb * When distributing Covered Code, include this CDDL HEADER in each 141488Srsb * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151488Srsb * If applicable, add the following below this CDDL HEADER, with the 161488Srsb * fields enclosed by brackets "[]" replaced with your own identifying 171488Srsb * information: Portions Copyright [yyyy] [name of copyright owner] 181488Srsb * 191488Srsb * CDDL HEADER END 201488Srsb */ 211488Srsb /* 22*5461Stc35445 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 231488Srsb * Use is subject to license terms. 241488Srsb */ 251488Srsb 261488Srsb #pragma ident "%Z%%M% %I% %E% SMI" 271488Srsb 281488Srsb #include <stdio.h> 291488Srsb #include <kstat.h> 301488Srsb #include <stdlib.h> 311488Srsb #include <string.h> 321488Srsb #include <strings.h> 331488Srsb #include <errno.h> 341488Srsb #include <limits.h> 351488Srsb #include <sys/types.h> 361488Srsb #include <time.h> 371967Srsb #include <langinfo.h> 381488Srsb #include <sys/time.h> 391488Srsb #include <sys/uio.h> 401488Srsb #include <sys/vnode.h> 411488Srsb #include <sys/vfs.h> 421488Srsb #include <sys/statvfs.h> 431488Srsb #include <sys/fstyp.h> 441488Srsb #include <sys/fsid.h> 451488Srsb #include <sys/mnttab.h> 461488Srsb #include <values.h> 471488Srsb #include <poll.h> 481488Srsb #include <ctype.h> 491488Srsb #include <libintl.h> 501967Srsb #include <locale.h> 51*5461Stc35445 #include <signal.h> 52*5461Stc35445 53*5461Stc35445 #include "statcommon.h" 541488Srsb 551680Srsb /* 561680Srsb * For now, parsable output is turned off. Once we gather feedback and 571680Srsb * stablize the output format, we'll turn it back on. This prevents 581680Srsb * the situation where users build tools which depend on a specific 591680Srsb * format before we declare the output stable. 601680Srsb */ 611680Srsb #define PARSABLE_OUTPUT 0 621680Srsb 631680Srsb #if PARSABLE_OUTPUT 641680Srsb #define OPTIONS "FPT:afginv" 651680Srsb #else 661680Srsb #define OPTIONS "FT:afginv" 671680Srsb #endif 681488Srsb 691488Srsb /* Time stamp values */ 701488Srsb #define NODATE 0 /* Default: No time stamp */ 711488Srsb #define DDATE 1 /* Standard date format */ 721488Srsb #define UDATE 2 /* Internal representation of Unix time */ 731488Srsb 741488Srsb #define RETRY_DELAY 250 /* Timeout for poll() */ 751680Srsb #define HEADERLINES 12 /* Number of lines between display headers */ 761488Srsb 771488Srsb #define LBUFSZ 64 /* Generic size for local buffer */ 781488Srsb 791488Srsb /* 801488Srsb * The following are used for the nicenum() function 811488Srsb */ 821488Srsb #define KILO_VAL 1024 831488Srsb #define ONE_INDEX 3 841488Srsb 851488Srsb #define NENTITY_INIT 1 /* Initial number of entities to allocate */ 861488Srsb 871488Srsb /* 881488Srsb * We need to have a mechanism for an old/previous and new/current vopstat 891488Srsb * structure. We only need two per entity and we can swap between them. 901488Srsb */ 911488Srsb #define VS_SIZE 2 /* Size of vopstat array */ 921488Srsb #define CUR_INDEX (vs_i) 931488Srsb #define PREV_INDEX ((vs_i == 0) ? 1 : 0) /* Opposite of CUR_INDEX */ 941488Srsb #define BUMP_INDEX() vs_i = ((vs_i == 0) ? 1 : 0) 951488Srsb 961488Srsb /* 971488Srsb * An "entity" is anything we're collecting statistics on, it could 981488Srsb * be a mountpoint or an FS-type. 991488Srsb * e_name is the name of the entity (e.g. mount point or FS-type) 1001488Srsb * e_ksname is the name of the associated kstat 1011488Srsb * e_vs is an array of vopstats. This is used to keep track of "previous" 1021488Srsb * and "current" vopstats. 1031488Srsb */ 1041488Srsb typedef struct entity { 1051488Srsb char *e_name; /* name of entity */ 1061488Srsb vopstats_t *e_vs; /* Array of vopstats */ 1071488Srsb ulong_t e_fsid; /* fsid for ENTYPE_MNTPT only */ 1081488Srsb int e_type; /* type of entity */ 1091488Srsb char e_ksname[KSTAT_STRLEN]; /* kstat name */ 1101488Srsb } entity_t; 1111488Srsb 1121488Srsb /* Types of entities (e_type) */ 1131488Srsb #define ENTYPE_UNKNOWN 0 /* UNKNOWN must be zero since we calloc() */ 1141488Srsb #define ENTYPE_FSTYPE 1 1151488Srsb #define ENTYPE_MNTPT 2 1161488Srsb 1171488Srsb /* If more sub-one units are added, make sure to adjust ONE_INDEX above */ 1181488Srsb static char units[] = "num KMGTPE"; 1191488Srsb 120*5461Stc35445 char *cmdname; /* name of this command */ 121*5461Stc35445 int caught_cont = 0; /* have caught a SIGCONT */ 1221488Srsb 1231488Srsb static int vs_i = 0; /* Index of current vs[] slot */ 1241488Srsb 1251488Srsb static void 1261488Srsb usage() 1271488Srsb { 1281680Srsb (void) fprintf(stderr, gettext( 1291680Srsb "Usage: %s [-a|f|i|n|v] [-T d|u] {-F | {fstype | fspath}...} " 1301488Srsb "[interval [count]]\n"), cmdname); 1311488Srsb exit(2); 1321488Srsb } 1331488Srsb 1341488Srsb /* 1351488Srsb * Given a 64-bit number and a starting unit (e.g., n - nanoseconds), 1361488Srsb * convert the number to a 5-character representation including any 1371488Srsb * decimal point and single-character unit. Put that representation 1381488Srsb * into the array "buf" (which had better be big enough). 1391488Srsb */ 1401488Srsb char * 1411488Srsb nicenum(uint64_t num, char unit, char *buf) 1421488Srsb { 1431488Srsb uint64_t n = num; 1441488Srsb int unit_index; 1451488Srsb int index; 1461488Srsb char u; 1471488Srsb 1481488Srsb /* If the user passed in a NUL/zero unit, use the blank value for 1 */ 1491488Srsb if (unit == '\0') 1501488Srsb unit = ' '; 1511488Srsb 1521488Srsb unit_index = 0; 1531488Srsb while (units[unit_index] != unit) { 1541488Srsb unit_index++; 1551488Srsb if (unit_index > sizeof (units) - 1) { 1561488Srsb (void) sprintf(buf, "??"); 1571488Srsb return (buf); 1581488Srsb } 1591488Srsb } 1601488Srsb 1611488Srsb index = 0; 1621488Srsb while (n >= KILO_VAL) { 1631488Srsb n = (n + (KILO_VAL / 2)) / KILO_VAL; /* Round up or down */ 1641488Srsb index++; 1651488Srsb unit_index++; 1661488Srsb } 1671488Srsb 1681488Srsb if (unit_index >= sizeof (units) - 1) { 1691488Srsb (void) sprintf(buf, "??"); 1701488Srsb return (buf); 1711488Srsb } 1721488Srsb 1731488Srsb u = units[unit_index]; 1741488Srsb 1751488Srsb if (unit_index == ONE_INDEX) { 1761488Srsb (void) sprintf(buf, "%llu", (u_longlong_t)n); 1771488Srsb } else if (n < 10 && (num & (num - 1)) != 0) { 1781488Srsb (void) sprintf(buf, "%.2f%c", 1791488Srsb (double)num / (1ULL << 10 * index), u); 1801488Srsb } else if (n < 100 && (num & (num - 1)) != 0) { 1811488Srsb (void) sprintf(buf, "%.1f%c", 1821488Srsb (double)num / (1ULL << 10 * index), u); 1831488Srsb } else { 1841488Srsb (void) sprintf(buf, "%llu%c", (u_longlong_t)n, u); 1851488Srsb } 1861488Srsb 1871488Srsb return (buf); 1881488Srsb } 1891488Srsb 1901488Srsb 1911488Srsb #define RAWVAL(ptr, member) ((ptr)->member.value.ui64) 1921488Srsb #define DELTA(member) \ 1931488Srsb (newvsp->member.value.ui64 - (oldvsp ? oldvsp->member.value.ui64 : 0)) 1941680Srsb 1951488Srsb #define PRINTSTAT(isnice, nicestring, rawstring, rawval, unit, buf) \ 1961488Srsb (isnice) ? \ 1971488Srsb (void) printf((nicestring), nicenum(rawval, unit, buf)) \ 1981488Srsb : \ 1991488Srsb (void) printf((rawstring), (rawval)) 2001488Srsb 2011488Srsb /* Values for display flag */ 2021488Srsb #define DISP_HEADER 0x1 2031488Srsb #define DISP_RAW 0x2 2041488Srsb 2051488Srsb /* 2061488Srsb * The policy for dealing with multiple flags is dealt with here. 2071488Srsb * Currently, if we are displaying raw output, then don't allow 2081488Srsb * headers to be printed. 2091488Srsb */ 2101488Srsb int 2111488Srsb dispflag_policy(int printhdr, int dispflag) 2121488Srsb { 2131488Srsb /* If we're not displaying raw output, then allow headers to print */ 2141488Srsb if ((dispflag & DISP_RAW) == 0) { 2151488Srsb if (printhdr) { 2161488Srsb dispflag |= DISP_HEADER; 2171488Srsb } 2181488Srsb } 2191488Srsb 2201488Srsb return (dispflag); 2211488Srsb } 2221488Srsb 2231488Srsb static void 2241488Srsb dflt_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 2251488Srsb { 2261488Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 2271488Srsb longlong_t nnewfile; 2281488Srsb longlong_t nnamerm; 2291488Srsb longlong_t nnamechg; 2301488Srsb longlong_t nattrret; 2311488Srsb longlong_t nattrchg; 2321488Srsb longlong_t nlookup; 2331488Srsb longlong_t nreaddir; 2341488Srsb longlong_t ndataread; 2351488Srsb longlong_t ndatawrite; 2361488Srsb longlong_t readthruput; 2371488Srsb longlong_t writethruput; 2381488Srsb char buf[LBUFSZ]; 2391488Srsb 2401488Srsb nnewfile = DELTA(ncreate) + DELTA(nmkdir) + DELTA(nsymlink); 2411488Srsb nnamerm = DELTA(nremove) + DELTA(nrmdir); 2421488Srsb nnamechg = DELTA(nrename) + DELTA(nlink) + DELTA(nsymlink); 2431488Srsb nattrret = DELTA(ngetattr) + DELTA(naccess) + 2441488Srsb DELTA(ngetsecattr) + DELTA(nfid); 2451488Srsb nattrchg = DELTA(nsetattr) + DELTA(nsetsecattr) + DELTA(nspace); 2461488Srsb nlookup = DELTA(nlookup); 2471488Srsb nreaddir = DELTA(nreaddir); 2481488Srsb ndataread = DELTA(nread); 2491488Srsb ndatawrite = DELTA(nwrite); 2501488Srsb readthruput = DELTA(read_bytes); 2511488Srsb writethruput = DELTA(write_bytes); 2521488Srsb 2531488Srsb if (dispflag & DISP_HEADER) { 2542054Srsb (void) printf( 2551488Srsb " new name name attr attr lookup rddir read read write write\n" 2562054Srsb " file remov chng get set ops ops ops bytes ops bytes\n"); 2571488Srsb } 2581488Srsb 2591488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnewfile, ' ', buf); 2601488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnamerm, ' ', buf); 2611488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnamechg, ' ', buf); 2621488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nattrret, ' ', buf); 2631488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nattrchg, ' ', buf); 2641488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", nlookup, ' ', buf); 2651488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nreaddir, ' ', buf); 2661488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", ndataread, ' ', buf); 2671488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", readthruput, ' ', buf); 2681488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", ndatawrite, ' ', buf); 2691488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", writethruput, ' ', buf); 2701488Srsb (void) printf("%s\n", name); 2711488Srsb } 2721488Srsb 2731488Srsb static void 2741488Srsb io_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 2751488Srsb { 2761488Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 2771488Srsb char buf[LBUFSZ]; 2781488Srsb 2791488Srsb if (dispflag & DISP_HEADER) { 2802054Srsb (void) printf( 2811488Srsb " read read write write rddir rddir rwlock rwulock\n" 2822054Srsb " ops bytes ops bytes ops bytes ops ops\n"); 2831488Srsb } 2841488Srsb 2851488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nread), ' ', buf); 2861488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(read_bytes), ' ', buf); 2871488Srsb 2881488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nwrite), ' ', buf); 2891488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(write_bytes), ' ', buf); 2901488Srsb 2911488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf); 2921488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(readdir_bytes), ' ', buf); 2931488Srsb 2941488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nrwlock), ' ', buf); 2951488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrwunlock), ' ', buf); 2961488Srsb 2971488Srsb (void) printf("%s\n", name); 2981488Srsb } 2991488Srsb 3001488Srsb static void 3011488Srsb vm_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 3021488Srsb { 3031488Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 3041488Srsb char buf[LBUFSZ]; 3051488Srsb 3061488Srsb if (dispflag & DISP_HEADER) { 3072054Srsb (void) printf(" map addmap delmap getpag putpag pagio\n"); 3081488Srsb } 3091488Srsb 3101488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmap), ' ', buf); 3111488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(naddmap), ' ', buf); 3121488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ndelmap), ' ', buf); 3131488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetpage), ' ', buf); 3141488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nputpage), ' ', buf); 3151488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(npageio), ' ', buf); 3161488Srsb (void) printf("%s\n", name); 3171488Srsb } 3181488Srsb 3191488Srsb static void 3201488Srsb attr_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 3211488Srsb { 3221488Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 3231488Srsb char buf[LBUFSZ]; 3241488Srsb 3251488Srsb if (dispflag & DISP_HEADER) { 3262054Srsb (void) printf("getattr setattr getsec setsec\n"); 3271488Srsb } 3281488Srsb 3291488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetattr), ' ', buf); 3301488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetattr), ' ', buf); 3311488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetsecattr), ' ', buf); 3321488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetsecattr), ' ', buf); 3331488Srsb 3341488Srsb (void) printf("%s\n", name); 3351488Srsb } 3361488Srsb 3371488Srsb static void 3381488Srsb naming_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 3391488Srsb { 3401488Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 3411488Srsb char buf[LBUFSZ]; 3421488Srsb 3431488Srsb if (dispflag & DISP_HEADER) { 3442054Srsb (void) printf( 3452054Srsb "lookup creat remov link renam mkdir rmdir rddir symlnk rdlnk\n"); 3461488Srsb } 3471488Srsb 3481488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlookup), ' ', buf); 3491488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(ncreate), ' ', buf); 3501488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nremove), ' ', buf); 3511488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlink), ' ', buf); 3521488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrename), ' ', buf); 3531488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmkdir), ' ', buf); 3541488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrmdir), ' ', buf); 3551488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf); 3561488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsymlink), ' ', buf); 3571488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreadlink), ' ', buf); 3581488Srsb (void) printf("%s\n", name); 3591488Srsb } 3601488Srsb 3611488Srsb 3621488Srsb #define PRINT_VOPSTAT_CMN(niceflag, vop) \ 3631488Srsb if (niceflag) \ 3641488Srsb (void) printf("%10s ", #vop); \ 3651488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(n##vop), ' ', buf); 3661488Srsb 3671488Srsb #define PRINT_VOPSTAT(niceflag, vop) \ 3681488Srsb PRINT_VOPSTAT_CMN(niceflag, vop); \ 3691488Srsb if (niceflag) \ 3701488Srsb (void) printf("\n"); 3711488Srsb 3721488Srsb #define PRINT_VOPSTAT_IO(niceflag, vop) \ 3731488Srsb PRINT_VOPSTAT_CMN(niceflag, vop); \ 3741488Srsb PRINTSTAT(niceflag, " %5s\n", "%lld:", \ 3751488Srsb DELTA(vop##_bytes), ' ', buf); 3761488Srsb 3771488Srsb static void 3781488Srsb vop_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 3791488Srsb { 3801488Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 3811488Srsb char buf[LBUFSZ]; 3821488Srsb 3831488Srsb if (niceflag) { 3841488Srsb (void) printf("%s\n", name); 3852054Srsb (void) printf(" operation #ops bytes\n"); 3861488Srsb } 3871488Srsb 3881488Srsb PRINT_VOPSTAT(niceflag, open); 3891488Srsb PRINT_VOPSTAT(niceflag, close); 3901488Srsb PRINT_VOPSTAT_IO(niceflag, read); 3911488Srsb PRINT_VOPSTAT_IO(niceflag, write); 3921488Srsb PRINT_VOPSTAT(niceflag, ioctl); 3931488Srsb PRINT_VOPSTAT(niceflag, setfl); 3941488Srsb PRINT_VOPSTAT(niceflag, getattr); 3951488Srsb PRINT_VOPSTAT(niceflag, setattr); 3961488Srsb PRINT_VOPSTAT(niceflag, access); 3971488Srsb PRINT_VOPSTAT(niceflag, lookup); 3981488Srsb PRINT_VOPSTAT(niceflag, create); 3991488Srsb PRINT_VOPSTAT(niceflag, remove); 4001488Srsb PRINT_VOPSTAT(niceflag, link); 4011488Srsb PRINT_VOPSTAT(niceflag, rename); 4021488Srsb PRINT_VOPSTAT(niceflag, mkdir); 4031488Srsb PRINT_VOPSTAT(niceflag, rmdir); 4041488Srsb PRINT_VOPSTAT_IO(niceflag, readdir); 4051488Srsb PRINT_VOPSTAT(niceflag, symlink); 4061488Srsb PRINT_VOPSTAT(niceflag, readlink); 4071488Srsb PRINT_VOPSTAT(niceflag, fsync); 4081488Srsb PRINT_VOPSTAT(niceflag, inactive); 4091488Srsb PRINT_VOPSTAT(niceflag, fid); 4101488Srsb PRINT_VOPSTAT(niceflag, rwlock); 4111488Srsb PRINT_VOPSTAT(niceflag, rwunlock); 4121488Srsb PRINT_VOPSTAT(niceflag, seek); 4131488Srsb PRINT_VOPSTAT(niceflag, cmp); 4141488Srsb PRINT_VOPSTAT(niceflag, frlock); 4151488Srsb PRINT_VOPSTAT(niceflag, space); 4161488Srsb PRINT_VOPSTAT(niceflag, realvp); 4171488Srsb PRINT_VOPSTAT(niceflag, getpage); 4181488Srsb PRINT_VOPSTAT(niceflag, putpage); 4191488Srsb PRINT_VOPSTAT(niceflag, map); 4201488Srsb PRINT_VOPSTAT(niceflag, addmap); 4211488Srsb PRINT_VOPSTAT(niceflag, delmap); 4221488Srsb PRINT_VOPSTAT(niceflag, poll); 4231488Srsb PRINT_VOPSTAT(niceflag, dump); 4241488Srsb PRINT_VOPSTAT(niceflag, pathconf); 4251488Srsb PRINT_VOPSTAT(niceflag, pageio); 4261488Srsb PRINT_VOPSTAT(niceflag, dumpctl); 4271488Srsb PRINT_VOPSTAT(niceflag, dispose); 4281488Srsb PRINT_VOPSTAT(niceflag, getsecattr); 4291488Srsb PRINT_VOPSTAT(niceflag, setsecattr); 4301488Srsb PRINT_VOPSTAT(niceflag, shrlock); 4311488Srsb PRINT_VOPSTAT(niceflag, vnevent); 4321488Srsb 4331488Srsb if (niceflag) { 4341488Srsb /* Make it easier on the eyes */ 4351488Srsb (void) printf("\n"); 4361488Srsb } else { 4371488Srsb (void) printf("%s\n", name); 4381488Srsb } 4391488Srsb } 4401488Srsb 4411488Srsb 4421488Srsb /* 4431488Srsb * Retrieve the vopstats. If kspp (pointer to kstat_t pointer) is non-NULL, 4441488Srsb * then pass it back to the caller. 4451488Srsb * 4461488Srsb * Returns 0 on success, non-zero on failure. 4471488Srsb */ 4481488Srsb int 4491488Srsb get_vopstats(kstat_ctl_t *kc, char *ksname, vopstats_t *vsp, kstat_t **kspp) 4501488Srsb { 4511488Srsb kstat_t *ksp; 4521488Srsb 4531488Srsb if (ksname == NULL || *ksname == 0) 4541488Srsb return (1); 4551488Srsb 4561488Srsb errno = 0; 4571488Srsb /* wait for a possibly up-to-date chain */ 4581488Srsb while (kstat_chain_update(kc) == -1) { 4591488Srsb if (errno == EAGAIN) { 4601488Srsb errno = 0; 4611488Srsb (void) poll(NULL, 0, RETRY_DELAY); 4621488Srsb continue; 4631488Srsb } 4642054Srsb perror("kstat_chain_update"); 4651488Srsb exit(1); 4661488Srsb } 4671488Srsb 4681488Srsb if ((ksp = kstat_lookup(kc, NULL, -1, ksname)) == NULL) { 4691488Srsb return (1); 4701488Srsb } 4711488Srsb 4721488Srsb if (kstat_read(kc, ksp, vsp) == -1) { 4731488Srsb return (1); 4741488Srsb } 4751488Srsb 4761488Srsb if (kspp) 4771488Srsb *kspp = ksp; 4781488Srsb 4791488Srsb return (0); 4801488Srsb } 4811488Srsb 4821488Srsb /* 4831488Srsb * Given a file system type name, determine if it's part of the 4841488Srsb * exception list of file systems that are not to be displayed. 4851488Srsb */ 4861488Srsb int 4871488Srsb is_exception(char *fsname) 4881488Srsb { 4891488Srsb char **xlp; /* Pointer into the exception list */ 4901488Srsb 4911488Srsb static char *exception_list[] = { 4921488Srsb "specfs", 4931488Srsb "fifofs", 4941488Srsb "fd", 4951488Srsb "swapfs", 4961488Srsb "ctfs", 4971488Srsb "objfs", 4981488Srsb "nfsdyn", 4991488Srsb NULL 5001488Srsb }; 5011488Srsb 5021488Srsb for (xlp = &exception_list[0]; *xlp != NULL; xlp++) { 5031488Srsb if (strcmp(fsname, *xlp) == 0) 5041488Srsb return (1); 5051488Srsb } 5061488Srsb 5071488Srsb return (0); 5081488Srsb } 5091488Srsb 5101488Srsb /* 5111488Srsb * Plain and simple, build an array of names for fstypes 5121488Srsb * Returns 0, if it encounters a problem. 5131488Srsb */ 5141488Srsb int 5151488Srsb build_fstype_list(char ***fstypep) 5161488Srsb { 5171488Srsb int i; 5181488Srsb int nfstype; 5191488Srsb char buf[FSTYPSZ + 1]; 5201488Srsb 5211488Srsb if ((nfstype = sysfs(GETNFSTYP)) < 0) { 5222054Srsb perror("sysfs(GETNFSTYP)"); 5231488Srsb return (0); 5241488Srsb } 5251488Srsb 5261488Srsb if ((*fstypep = calloc(nfstype, sizeof (char *))) == NULL) { 5272054Srsb perror("calloc() fstypes"); 5281488Srsb return (0); 5291488Srsb } 5301488Srsb 5311488Srsb for (i = 1; i < nfstype; i++) { 5321488Srsb if (sysfs(GETFSTYP, i, buf) < 0) { 5332054Srsb perror("sysfs(GETFSTYP)"); 5341488Srsb return (0); 5351488Srsb } 5361488Srsb 5371488Srsb if (buf[0] == 0) 5381488Srsb continue; 5391488Srsb 5401488Srsb /* If this is part of the exception list, move on */ 5411488Srsb if (is_exception(buf)) 5421488Srsb continue; 5431488Srsb 5441488Srsb if (((*fstypep)[i] = strdup(buf)) == NULL) { 5452054Srsb perror("strdup() fstype name"); 5461488Srsb return (0); 5471488Srsb } 5481488Srsb } 5491488Srsb 5501488Srsb return (i); 5511488Srsb } 5521488Srsb 5531488Srsb /* 5541488Srsb * After we're done with getopts(), process the rest of the 5551488Srsb * operands. We have three cases and this is the priority: 5561488Srsb * 5571488Srsb * 1) [ operand... ] interval count 5581488Srsb * 2) [ operand... ] interval 5591488Srsb * 3) [ operand... ] 5601488Srsb * 5611488Srsb * The trick is that any of the operands might start with a number or even 5621488Srsb * be made up exclusively of numbers (and we have to handle negative numbers 5631488Srsb * in case a user/script gets out of line). If we find two operands at the 5641488Srsb * end of the list then we claim case 1. If we find only one operand at the 5651488Srsb * end made up only of number, then we claim case 2. Otherwise, case 3. 5661488Srsb * BTW, argc, argv don't change. 5671488Srsb */ 5681488Srsb int 5691488Srsb parse_operands( 5701488Srsb int argc, 5711488Srsb char **argv, 5721488Srsb int optind, 5731488Srsb long *interval, 5741488Srsb long *count, 5751488Srsb entity_t **entityp) /* Array of stat-able entities */ 5761488Srsb { 5771488Srsb int nentities = 0; /* Number of entities found */ 5781488Srsb int out_of_range; /* Set if 2nd-to-last operand out-of-range */ 5791488Srsb 5801488Srsb if (argc == optind) 5811488Srsb return (nentities); /* None found, returns 0 */ 5821488Srsb /* 5831488Srsb * We know exactly what the maximum number of entities is going 5841488Srsb * to be: argc - optind 5851488Srsb */ 5861488Srsb if ((*entityp = calloc((argc - optind), sizeof (entity_t))) == NULL) { 5872054Srsb perror("calloc() entities"); 5881488Srsb return (-1); 5891488Srsb } 5901488Srsb 5911488Srsb for (/* void */; argc > optind; optind++) { 5921488Srsb char *endptr; 5931488Srsb 5941488Srsb /* If we have more than two operands left to process */ 5951488Srsb if ((argc - optind) > 2) { 5961488Srsb (*entityp)[nentities++].e_name = strdup(argv[optind]); 5971488Srsb continue; 5981488Srsb } 5991488Srsb 6001488Srsb /* If we're here, then we only have one or two operands left */ 6011488Srsb errno = 0; 6021488Srsb out_of_range = 0; 6031488Srsb *interval = strtol(argv[optind], &endptr, 10); 6041488Srsb if (*endptr && !isdigit((int)*endptr)) { 6051488Srsb /* Operand was not a number */ 6061488Srsb (*entityp)[nentities++].e_name = strdup(argv[optind]); 6071488Srsb continue; 6081488Srsb } else if (errno == ERANGE || *interval <= 0 || 6091488Srsb *interval > MAXLONG) { 6101488Srsb /* Operand was a number, just out of range */ 6111488Srsb out_of_range++; 6121488Srsb } 6131488Srsb 6141488Srsb /* 6151488Srsb * The last operand we saw was a number. If it happened to 6161488Srsb * be the last operand, then it is the interval... 6171488Srsb */ 6181488Srsb if ((argc - optind) == 1) { 6191488Srsb /* ...but we need to check the range. */ 6201488Srsb if (out_of_range) { 6211488Srsb (void) fprintf(stderr, gettext( 6221488Srsb "interval must be between 1 and " 6231488Srsb "%ld (inclusive)\n"), MAXLONG); 6241488Srsb return (-1); 6251488Srsb } else { 6261488Srsb /* 6271488Srsb * The value of the interval is valid. Set 6281488Srsb * count to something really big so it goes 6291488Srsb * virtually forever. 6301488Srsb */ 6311488Srsb *count = MAXLONG; 6321488Srsb break; 6331488Srsb } 6341488Srsb } 6351488Srsb 6361488Srsb /* 6371488Srsb * At this point, we *might* have the interval, but if the 6381488Srsb * next operand isn't a number, then we don't have either 6391488Srsb * the interval nor the count. Both must be set to the 6401488Srsb * defaults. In that case, both the current and the previous 6411488Srsb * operands are stat-able entities. 6421488Srsb */ 6431488Srsb errno = 0; 6441488Srsb *count = strtol(argv[optind + 1], &endptr, 10); 6451488Srsb if (*endptr && !isdigit((int)*endptr)) { 6461488Srsb /* 6471488Srsb * Faked out! The last operand wasn't a number so 6481488Srsb * the current and previous operands should be 6491488Srsb * stat-able entities. We also need to reset interval. 6501488Srsb */ 6511488Srsb *interval = 0; 6521488Srsb (*entityp)[nentities++].e_name = strdup(argv[optind++]); 6531488Srsb (*entityp)[nentities++].e_name = strdup(argv[optind++]); 6541488Srsb } else if (out_of_range || errno == ERANGE || *count <= 0) { 6551488Srsb (void) fprintf(stderr, gettext( 6561488Srsb "Both interval and count must be between 1 " 6571488Srsb "and %ld (inclusive)\n"), MAXLONG); 6581488Srsb return (-1); 6591488Srsb } 6601488Srsb break; /* Done! */ 6611488Srsb } 6621488Srsb return (nentities); 6631488Srsb } 6641488Srsb 6651488Srsb /* 6661488Srsb * set_mntpt() looks at the entity's name (e_name) and finds its 6671488Srsb * mountpoint. To do this, we need to build a list of mountpoints 6681488Srsb * from /etc/mnttab. We only need to do this once and we don't do it 6691488Srsb * if we don't need to look at any mountpoints. 6701488Srsb * Returns 0 on success, non-zero if it couldn't find a mount-point. 6711488Srsb */ 6721488Srsb int 6731488Srsb set_mntpt(entity_t *ep) 6741488Srsb { 6751488Srsb static struct mnt { 6761488Srsb struct mnt *m_next; 6771488Srsb char *m_mntpt; 6781488Srsb ulong_t m_fsid; /* From statvfs(), set only as needed */ 6791488Srsb } *mnt_list = NULL; /* Linked list of mount-points */ 6801488Srsb struct mnt *mntp; 6812396Srsb struct statvfs64 statvfsbuf; 6821488Srsb char *original_name = ep->e_name; 6831488Srsb char path[PATH_MAX]; 6841488Srsb 6851488Srsb if (original_name == NULL) /* Shouldn't happen */ 6861488Srsb return (1); 6871488Srsb 6881488Srsb /* We only set up mnt_list the first time this is called */ 6891488Srsb if (mnt_list == NULL) { 6901488Srsb FILE *fp; 6911488Srsb struct mnttab mnttab; 6921488Srsb 6931488Srsb if ((fp = fopen(MNTTAB, "r")) == NULL) { 6941488Srsb perror(MNTTAB); 6951488Srsb return (1); 6961488Srsb } 6971488Srsb resetmnttab(fp); 6981488Srsb /* 6991488Srsb * We insert at the front of the list so that when we 7001488Srsb * search entries we'll have the last mounted entries 7011488Srsb * first in the list so that we can match the longest 7021488Srsb * mountpoint. 7031488Srsb */ 7041488Srsb while (getmntent(fp, &mnttab) == 0) { 7051488Srsb if ((mntp = malloc(sizeof (*mntp))) == NULL) { 7062054Srsb perror("malloc() mount list"); 7071488Srsb return (1); 7081488Srsb } 7091488Srsb mntp->m_mntpt = strdup(mnttab.mnt_mountp); 7101488Srsb mntp->m_next = mnt_list; 7111488Srsb mnt_list = mntp; 7121488Srsb } 7131488Srsb (void) fclose(fp); 7141488Srsb } 7151488Srsb 7161488Srsb if (realpath(original_name, path) == NULL) { 7171488Srsb perror(original_name); 7181488Srsb return (1); 7191488Srsb } 7201488Srsb 7211488Srsb /* 7221488Srsb * Now that we have the path, walk through the mnt_list and 7231488Srsb * look for the first (best) match. 7241488Srsb */ 7251488Srsb for (mntp = mnt_list; mntp; mntp = mntp->m_next) { 7261488Srsb if (strncmp(path, mntp->m_mntpt, strlen(mntp->m_mntpt)) == 0) { 7271488Srsb if (mntp->m_fsid == 0) { 7282396Srsb if (statvfs64(mntp->m_mntpt, &statvfsbuf)) { 7291488Srsb /* Can't statvfs so no match */ 7301488Srsb continue; 7311488Srsb } else { 7321488Srsb mntp->m_fsid = statvfsbuf.f_fsid; 7331488Srsb } 7341488Srsb } 7351488Srsb 7361488Srsb if (ep->e_fsid != mntp->m_fsid) { 7371488Srsb /* No match - Move on */ 7381488Srsb continue; 7391488Srsb } 7401488Srsb 7411488Srsb break; 7421488Srsb } 7431488Srsb } 7441488Srsb 7451488Srsb if (mntp == NULL) { 7461488Srsb (void) fprintf(stderr, gettext( 7471488Srsb "Can't find mount point for %s\n"), path); 7481488Srsb return (1); 7491488Srsb } 7501488Srsb 7511488Srsb ep->e_name = strdup(mntp->m_mntpt); 7521488Srsb free(original_name); 7531488Srsb return (0); 7541488Srsb } 7551488Srsb 7561488Srsb /* 7571488Srsb * We have an array of entities that are potentially stat-able. Using 7581488Srsb * the name (e_name) of the entity, attempt to construct a ksname suitable 7591488Srsb * for use by kstat_lookup(3kstat) and fill it into the e_ksname member. 7601488Srsb * 7611488Srsb * We check the e_name against the list of file system types. If there is 7621488Srsb * no match then test to see if the path is valid. If the path is valid, 7631488Srsb * then determine the mountpoint. 7641488Srsb */ 7651488Srsb void 7661488Srsb set_ksnames(entity_t *entities, int nentities, char **fstypes, int nfstypes) 7671488Srsb { 7681488Srsb int i, j; 7692396Srsb struct statvfs64 statvfsbuf; 7701488Srsb 7711488Srsb for (i = 0; i < nentities; i++) { 7721488Srsb entity_t *ep = &entities[i]; 7731488Srsb 7741488Srsb /* Check the name against the list of fstypes */ 7751488Srsb for (j = 1; j < nfstypes; j++) { 7761488Srsb if (fstypes[j] && ep->e_name && 7771488Srsb strcmp(ep->e_name, fstypes[j]) == 0) { 7781488Srsb /* It's a file system type */ 7791488Srsb ep->e_type = ENTYPE_FSTYPE; 7801488Srsb (void) snprintf(ep->e_ksname, 7811488Srsb KSTAT_STRLEN, "%s%s", 7821488Srsb VOPSTATS_STR, ep->e_name); 7831488Srsb /* Now allocate the vopstats array */ 7841488Srsb ep->e_vs = calloc(VS_SIZE, sizeof (vopstats_t)); 7851488Srsb if (entities[i].e_vs == NULL) { 7862054Srsb perror("calloc() fstype vopstats"); 7871488Srsb exit(1); 7881488Srsb } 7891488Srsb break; 7901488Srsb } 7911488Srsb } 7921488Srsb if (j < nfstypes) /* Found it! */ 7931488Srsb continue; 7941488Srsb 7951488Srsb /* 7961488Srsb * If the entity in the exception list of fstypes, then 7971488Srsb * null out the entry so it isn't displayed and move along. 7981488Srsb */ 7991488Srsb if (is_exception(ep->e_name)) { 8001488Srsb ep->e_ksname[0] = 0; 8011488Srsb continue; 8021488Srsb } 8031488Srsb 8041488Srsb /* If we didn't find it, see if it's a path */ 8052396Srsb if (ep->e_name == NULL || statvfs64(ep->e_name, &statvfsbuf)) { 8061488Srsb /* Error - Make sure the entry is nulled out */ 8071488Srsb ep->e_ksname[0] = 0; 8081488Srsb continue; 8091488Srsb } 8101488Srsb (void) snprintf(ep->e_ksname, KSTAT_STRLEN, "%s%lx", 8111488Srsb VOPSTATS_STR, statvfsbuf.f_fsid); 8121488Srsb ep->e_fsid = statvfsbuf.f_fsid; 8131488Srsb if (set_mntpt(ep)) { 8141488Srsb (void) fprintf(stderr, 8151488Srsb gettext("Can't determine type of \"%s\"\n"), 8161488Srsb ep->e_name ? ep->e_name : gettext("<NULL>")); 8171488Srsb } else { 8181488Srsb ep->e_type = ENTYPE_MNTPT; 8191488Srsb } 8201488Srsb 8211488Srsb /* Now allocate the vopstats array */ 8221488Srsb ep->e_vs = calloc(VS_SIZE, sizeof (vopstats_t)); 8231488Srsb if (entities[i].e_vs == NULL) { 8242054Srsb perror("calloc() vopstats array"); 8251488Srsb exit(1); 8261488Srsb } 8271488Srsb } 8281488Srsb } 8291488Srsb 8301488Srsb void 8311488Srsb print_time(int type) 8321488Srsb { 8331488Srsb time_t t; 8341967Srsb static char *fmt = NULL; /* Time format */ 8351967Srsb 8361967Srsb /* We only need to retrieve this once per invocation */ 8371967Srsb if (fmt == NULL) { 8381967Srsb fmt = nl_langinfo(_DATE_FMT); 8391967Srsb } 8401488Srsb 8411488Srsb if (time(&t) != -1) { 8421488Srsb if (type == UDATE) { 8431488Srsb (void) printf("%ld\n", t); 8441488Srsb } else if (type == DDATE) { 8451967Srsb char dstr[64]; 8461967Srsb int len; 8471488Srsb 8481967Srsb len = strftime(dstr, sizeof (dstr), fmt, localtime(&t)); 8491967Srsb if (len > 0) { 8501967Srsb (void) printf("%s\n", dstr); 8511488Srsb } 8521488Srsb } 8531488Srsb } 8541488Srsb } 8551488Srsb 8561488Srsb /* 8571488Srsb * The idea is that 'dspfunc' should only be modified from the default 8581488Srsb * once since the display options are mutually exclusive. If 'dspfunc' 8591488Srsb * only contains the default display function, then all is good and we 8601488Srsb * can set it to the new display function. Otherwise, bail. 8611488Srsb */ 8621488Srsb void 8631488Srsb set_dispfunc( 8641488Srsb void (**dspfunc)(char *, vopstats_t *, vopstats_t *, int), 8651488Srsb void (*newfunc)(char *, vopstats_t *, vopstats_t *, int)) 8661488Srsb { 8671488Srsb if (*dspfunc != dflt_display) { 8681488Srsb (void) fprintf(stderr, gettext( 8691488Srsb "%s: Display options -{a|f|i|n|v} are mutually exclusive\n"), 8701488Srsb cmdname); 8711488Srsb usage(); 8721488Srsb } 8731488Srsb *dspfunc = newfunc; 8741488Srsb } 8751488Srsb 8761488Srsb int 8771488Srsb main(int argc, char *argv[]) 8781488Srsb { 8791488Srsb int c; 8801488Srsb int i, j; /* Generic counters */ 8811488Srsb int nentities_found; 8821488Srsb int linesout; /* Keeps track of lines printed */ 8831488Srsb int printhdr = 0; /* Print a header? 0 = no, 1 = yes */ 8841488Srsb int nfstypes; /* Number of fstypes */ 8851488Srsb int dispflag = 0; /* Flags for display control */ 8861488Srsb int timestamp = NODATE; /* Default: no time stamp */ 8871488Srsb long count = 0; /* Number of iterations for display */ 888*5461Stc35445 int forever; /* Run forever */ 8891488Srsb long interval = 0; 8901488Srsb boolean_t fstypes_only = B_FALSE; /* Display fstypes only */ 8911488Srsb char **fstypes; /* Array of names of all fstypes */ 8921488Srsb int nentities; /* Number of stat-able entities */ 8931488Srsb entity_t *entities; /* Array of stat-able entities */ 8941488Srsb kstat_ctl_t *kc; 8951488Srsb void (*dfunc)(char *, vopstats_t *, vopstats_t *, int) = dflt_display; 896*5461Stc35445 hrtime_t start_n; /* Start time */ 897*5461Stc35445 hrtime_t period_n; /* Interval in nanoseconds */ 8981488Srsb 8991488Srsb extern int optind; 9001488Srsb 9011967Srsb (void) setlocale(LC_ALL, ""); 9021967Srsb #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 9031967Srsb #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 9041967Srsb #endif 9051967Srsb (void) textdomain(TEXT_DOMAIN); 9061967Srsb 9071488Srsb cmdname = argv[0]; 9081488Srsb while ((c = getopt(argc, argv, OPTIONS)) != EOF) { 9091488Srsb switch (c) { 9101488Srsb 9111488Srsb default: 9121488Srsb usage(); 9131488Srsb break; 9141488Srsb 9151680Srsb case 'F': /* Only display available FStypes */ 9161680Srsb fstypes_only = B_TRUE; 9171680Srsb break; 9181680Srsb 9191680Srsb #if PARSABLE_OUTPUT 9201488Srsb case 'P': /* Parsable output */ 9211488Srsb dispflag |= DISP_RAW; 9221488Srsb break; 9231680Srsb #endif /* PARSABLE_OUTPUT */ 9241488Srsb 9251488Srsb case 'T': /* Timestamp */ 9261488Srsb if (optarg) { 9271488Srsb if (strcmp(optarg, "u") == 0) { 9281488Srsb timestamp = UDATE; 9291488Srsb } else if (strcmp(optarg, "d") == 0) { 9301488Srsb timestamp = DDATE; 9311488Srsb } 9321488Srsb } 9331488Srsb 9341488Srsb /* If it was never set properly... */ 9351488Srsb if (timestamp == NODATE) { 9361488Srsb (void) fprintf(stderr, gettext( 9371488Srsb "%s: -T option requires either 'u' or 'd'\n"), 9381488Srsb cmdname); 9391488Srsb usage(); 9401488Srsb } 9411488Srsb break; 9421488Srsb 9431488Srsb case 'a': 9441488Srsb set_dispfunc(&dfunc, attr_display); 9451488Srsb break; 9461488Srsb 9471488Srsb case 'f': 9481488Srsb set_dispfunc(&dfunc, vop_display); 9491488Srsb break; 9501488Srsb 9511488Srsb case 'i': 9521488Srsb set_dispfunc(&dfunc, io_display); 9531488Srsb break; 9541488Srsb 9551488Srsb case 'n': 9561488Srsb set_dispfunc(&dfunc, naming_display); 9571488Srsb break; 9581488Srsb 9591488Srsb case 'v': 9601488Srsb set_dispfunc(&dfunc, vm_display); 9611488Srsb break; 9621488Srsb } 9631488Srsb } 9641488Srsb 9651680Srsb #if PARSABLE_OUTPUT 9661488Srsb if ((dispflag & DISP_RAW) && (timestamp != NODATE)) { 9671488Srsb (void) fprintf(stderr, gettext( 9681488Srsb "-P and -T options are mutually exclusive\n")); 9691488Srsb usage(); 9701488Srsb } 9711680Srsb #endif /* PARSABLE_OUTPUT */ 9721488Srsb 9731488Srsb /* Gather the list of filesystem types */ 9741488Srsb if ((nfstypes = build_fstype_list(&fstypes)) == 0) { 9751488Srsb (void) fprintf(stderr, 9761488Srsb gettext("Can't build list of fstypes\n")); 9771488Srsb exit(1); 9781488Srsb } 9791488Srsb 9801488Srsb nentities = parse_operands( 9811488Srsb argc, argv, optind, &interval, &count, &entities); 982*5461Stc35445 forever = count == MAXLONG; 983*5461Stc35445 period_n = (hrtime_t)interval * NANOSEC; 9841488Srsb 9851488Srsb if (nentities == -1) /* Set of operands didn't parse properly */ 9861488Srsb usage(); 9871488Srsb 9881680Srsb if ((nentities == 0) && (fstypes_only == B_FALSE)) { 9891680Srsb (void) fprintf(stderr, gettext( 9901680Srsb "Must specify -F or at least one fstype or mount point\n")); 9911680Srsb usage(); 9921680Srsb } 9931680Srsb 9941680Srsb if ((nentities > 0) && (fstypes_only == B_TRUE)) { 9951680Srsb (void) fprintf(stderr, gettext( 9961680Srsb "Cannot use -F with fstypes or mount points\n")); 9971680Srsb usage(); 9981680Srsb } 9991680Srsb 10001488Srsb /* 10011680Srsb * If we had no operands (except for interval/count) and we 10021680Srsb * requested FStypes only (-F), then fill in the entities[] 10031680Srsb * array with all available fstypes. 10041488Srsb */ 10051680Srsb if ((nentities == 0) && (fstypes_only == B_TRUE)) { 10061488Srsb if ((entities = calloc(nfstypes, sizeof (entity_t))) == NULL) { 10072054Srsb perror("calloc() fstype stats"); 10081488Srsb exit(1); 10091488Srsb } 10101488Srsb 10111488Srsb for (i = 1; i < nfstypes; i++) { 10121488Srsb if (fstypes[i]) { 10131488Srsb entities[nentities].e_name = strdup(fstypes[i]); 10141488Srsb nentities++; 10151488Srsb } 10161488Srsb } 10171488Srsb } 10181488Srsb 10191488Srsb set_ksnames(entities, nentities, fstypes, nfstypes); 10201488Srsb 10211488Srsb if ((kc = kstat_open()) == NULL) { 10222054Srsb perror("kstat_open"); 10231488Srsb exit(1); 10241488Srsb } 10251488Srsb 1026*5461Stc35445 /* Set start time */ 1027*5461Stc35445 start_n = gethrtime(); 1028*5461Stc35445 10291488Srsb /* 10301488Srsb * The following loop walks through the entities[] list to "prime 10311488Srsb * the pump" 10321488Srsb */ 10331488Srsb for (j = 0, linesout = 0; j < nentities; j++) { 10341488Srsb entity_t *ent = &entities[j]; 10351488Srsb vopstats_t *vsp = &ent->e_vs[CUR_INDEX]; 10361488Srsb kstat_t *ksp = NULL; 10371488Srsb 10381488Srsb if (get_vopstats(kc, ent->e_ksname, vsp, &ksp) == 0) { 10391488Srsb (*dfunc)(ent->e_name, NULL, vsp, 10401488Srsb dispflag_policy(linesout == 0, dispflag)); 10411488Srsb linesout++; 10421488Srsb } else { 10431488Srsb /* 10441488Srsb * If we can't find it the first time through, then 10451488Srsb * get rid of it. 10461488Srsb */ 10471488Srsb entities[j].e_ksname[0] = 0; 10481488Srsb 10491488Srsb /* 10501680Srsb * If we're only displaying FStypes (-F) then don't 10511488Srsb * complain about any file systems that might not 10521488Srsb * be loaded. Otherwise, let the user know that 10531488Srsb * he chose poorly. 10541488Srsb */ 10551488Srsb if (fstypes_only == B_FALSE) { 10561488Srsb (void) fprintf(stderr, gettext( 10571488Srsb "No statistics available for %s\n"), 10581488Srsb entities[j].e_name); 10591488Srsb } 10601488Srsb } 10611488Srsb } 10621488Srsb 1063*5461Stc35445 if (count > 1) 1064*5461Stc35445 /* Set up signal handler for SIGCONT */ 1065*5461Stc35445 if (signal(SIGCONT, cont_handler) == SIG_ERR) 1066*5461Stc35445 fail(1, "signal failed"); 1067*5461Stc35445 1068*5461Stc35445 10691488Srsb BUMP_INDEX(); /* Swap the previous/current indices */ 1070*5461Stc35445 i = 1; 1071*5461Stc35445 while (forever || i++ <= count) { 10721488Srsb /* 10731488Srsb * No telling how many lines will be printed in any interval. 10741488Srsb * There should be a minimum of HEADERLINES between any 10751488Srsb * header. If we exceed that, no big deal. 10761488Srsb */ 10771488Srsb if (linesout > HEADERLINES) { 10781488Srsb linesout = 0; 10791488Srsb printhdr = 1; 10801488Srsb } 1081*5461Stc35445 /* Have a kip */ 1082*5461Stc35445 sleep_until(&start_n, period_n, forever, &caught_cont); 10831488Srsb 10841488Srsb if (timestamp) { 10851488Srsb print_time(timestamp); 10861488Srsb linesout++; 10871488Srsb } 10881488Srsb 10891488Srsb for (j = 0, nentities_found = 0; j < nentities; j++) { 10901488Srsb entity_t *ent = &entities[j]; 10911488Srsb 10921488Srsb /* 10931488Srsb * If this entry has been cleared, don't attempt 10941488Srsb * to process it. 10951488Srsb */ 10961488Srsb if (ent->e_ksname[0] == 0) { 10971488Srsb continue; 10981488Srsb } 10991488Srsb 11001488Srsb if (get_vopstats(kc, ent->e_ksname, 11011488Srsb &ent->e_vs[CUR_INDEX], NULL) == 0) { 11021488Srsb (*dfunc)(ent->e_name, &ent->e_vs[PREV_INDEX], 11031488Srsb &ent->e_vs[CUR_INDEX], 11041488Srsb dispflag_policy(printhdr, dispflag)); 11051488Srsb linesout++; 11061488Srsb nentities_found++; 11071488Srsb } else { 11081488Srsb if (ent->e_type == ENTYPE_MNTPT) { 11091488Srsb (void) printf(gettext( 11101488Srsb "<<mount point no longer " 11111488Srsb "available: %s>>\n"), ent->e_name); 11121488Srsb } else if (ent->e_type == ENTYPE_FSTYPE) { 11131488Srsb (void) printf(gettext( 11141488Srsb "<<file system module no longer " 11151488Srsb "loaded: %s>>\n"), ent->e_name); 11161488Srsb } else { 11171488Srsb (void) printf(gettext( 11181488Srsb "<<%s no longer available>>\n"), 11191488Srsb ent->e_name); 11201488Srsb } 11211488Srsb /* Disable this so it doesn't print again */ 11221488Srsb ent->e_ksname[0] = 0; 11231488Srsb } 11241488Srsb printhdr = 0; /* Always shut this off */ 11251488Srsb } 11261488Srsb BUMP_INDEX(); /* Bump the previous/current indices */ 11271488Srsb 11281488Srsb /* 11291488Srsb * If the entities we were observing are no longer there 11301488Srsb * (file system modules unloaded, file systems unmounted) 11311488Srsb * then we're done. 11321488Srsb */ 11331488Srsb if (nentities_found == 0) 11341488Srsb break; 11351488Srsb } 11361488Srsb 11371488Srsb return (0); 11381488Srsb } 1139