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