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*11539SChunli.Zhang@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
231488Srsb * Use is subject to license terms.
241488Srsb */
251488Srsb
261488Srsb #include <stdio.h>
271488Srsb #include <kstat.h>
281488Srsb #include <stdlib.h>
291488Srsb #include <string.h>
301488Srsb #include <strings.h>
311488Srsb #include <errno.h>
321488Srsb #include <limits.h>
331488Srsb #include <sys/types.h>
341488Srsb #include <time.h>
351488Srsb #include <sys/time.h>
361488Srsb #include <sys/uio.h>
371488Srsb #include <sys/vnode.h>
381488Srsb #include <sys/vfs.h>
391488Srsb #include <sys/statvfs.h>
401488Srsb #include <sys/fstyp.h>
411488Srsb #include <sys/fsid.h>
421488Srsb #include <sys/mnttab.h>
431488Srsb #include <values.h>
441488Srsb #include <poll.h>
451488Srsb #include <ctype.h>
461488Srsb #include <libintl.h>
471967Srsb #include <locale.h>
485461Stc35445 #include <signal.h>
495461Stc35445
505461Stc35445 #include "statcommon.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
1175461Stc35445 char *cmdname; /* name of this command */
1185461Stc35445 int caught_cont = 0; /* have caught a SIGCONT */
1191488Srsb
12010265SKrishnendu.Sadhukhan@Sun.COM static uint_t timestamp_fmt = NODATE; /* print timestamp with stats */
1219123Sjohn.levon@sun.com
1221488Srsb static int vs_i = 0; /* Index of current vs[] slot */
1231488Srsb
1241488Srsb static void
usage()1251488Srsb usage()
1261488Srsb {
1271680Srsb (void) fprintf(stderr, gettext(
1281680Srsb "Usage: %s [-a|f|i|n|v] [-T d|u] {-F | {fstype | fspath}...} "
1291488Srsb "[interval [count]]\n"), cmdname);
1301488Srsb exit(2);
1311488Srsb }
1321488Srsb
1331488Srsb /*
1341488Srsb * Given a 64-bit number and a starting unit (e.g., n - nanoseconds),
1351488Srsb * convert the number to a 5-character representation including any
1361488Srsb * decimal point and single-character unit. Put that representation
1371488Srsb * into the array "buf" (which had better be big enough).
1381488Srsb */
1391488Srsb char *
nicenum(uint64_t num,char unit,char * buf)1401488Srsb nicenum(uint64_t num, char unit, char *buf)
1411488Srsb {
1421488Srsb uint64_t n = num;
1431488Srsb int unit_index;
1441488Srsb int index;
1451488Srsb char u;
1461488Srsb
1471488Srsb /* If the user passed in a NUL/zero unit, use the blank value for 1 */
1481488Srsb if (unit == '\0')
1491488Srsb unit = ' ';
1501488Srsb
1511488Srsb unit_index = 0;
1521488Srsb while (units[unit_index] != unit) {
1531488Srsb unit_index++;
1541488Srsb if (unit_index > sizeof (units) - 1) {
1551488Srsb (void) sprintf(buf, "??");
1561488Srsb return (buf);
1571488Srsb }
1581488Srsb }
1591488Srsb
1601488Srsb index = 0;
1611488Srsb while (n >= KILO_VAL) {
1621488Srsb n = (n + (KILO_VAL / 2)) / KILO_VAL; /* Round up or down */
1631488Srsb index++;
1641488Srsb unit_index++;
1651488Srsb }
1661488Srsb
1671488Srsb if (unit_index >= sizeof (units) - 1) {
1681488Srsb (void) sprintf(buf, "??");
1691488Srsb return (buf);
1701488Srsb }
1711488Srsb
1721488Srsb u = units[unit_index];
1731488Srsb
1741488Srsb if (unit_index == ONE_INDEX) {
1751488Srsb (void) sprintf(buf, "%llu", (u_longlong_t)n);
1761488Srsb } else if (n < 10 && (num & (num - 1)) != 0) {
1771488Srsb (void) sprintf(buf, "%.2f%c",
1781488Srsb (double)num / (1ULL << 10 * index), u);
1791488Srsb } else if (n < 100 && (num & (num - 1)) != 0) {
1801488Srsb (void) sprintf(buf, "%.1f%c",
1811488Srsb (double)num / (1ULL << 10 * index), u);
1821488Srsb } else {
1831488Srsb (void) sprintf(buf, "%llu%c", (u_longlong_t)n, u);
1841488Srsb }
1851488Srsb
1861488Srsb return (buf);
1871488Srsb }
1881488Srsb
1891488Srsb
1901488Srsb #define RAWVAL(ptr, member) ((ptr)->member.value.ui64)
1911488Srsb #define DELTA(member) \
1921488Srsb (newvsp->member.value.ui64 - (oldvsp ? oldvsp->member.value.ui64 : 0))
1931680Srsb
1941488Srsb #define PRINTSTAT(isnice, nicestring, rawstring, rawval, unit, buf) \
1951488Srsb (isnice) ? \
1961488Srsb (void) printf((nicestring), nicenum(rawval, unit, buf)) \
1971488Srsb : \
1981488Srsb (void) printf((rawstring), (rawval))
1991488Srsb
2001488Srsb /* Values for display flag */
2011488Srsb #define DISP_HEADER 0x1
2021488Srsb #define DISP_RAW 0x2
2031488Srsb
2041488Srsb /*
2051488Srsb * The policy for dealing with multiple flags is dealt with here.
2061488Srsb * Currently, if we are displaying raw output, then don't allow
2071488Srsb * headers to be printed.
2081488Srsb */
2091488Srsb int
dispflag_policy(int printhdr,int dispflag)2101488Srsb dispflag_policy(int printhdr, int dispflag)
2111488Srsb {
2121488Srsb /* If we're not displaying raw output, then allow headers to print */
2131488Srsb if ((dispflag & DISP_RAW) == 0) {
2141488Srsb if (printhdr) {
2151488Srsb dispflag |= DISP_HEADER;
2161488Srsb }
2171488Srsb }
2181488Srsb
2191488Srsb return (dispflag);
2201488Srsb }
2211488Srsb
2221488Srsb static void
dflt_display(char * name,vopstats_t * oldvsp,vopstats_t * newvsp,int dispflag)2231488Srsb dflt_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
2241488Srsb {
2251488Srsb int niceflag = ((dispflag & DISP_RAW) == 0);
2261488Srsb longlong_t nnewfile;
2271488Srsb longlong_t nnamerm;
2281488Srsb longlong_t nnamechg;
2291488Srsb longlong_t nattrret;
2301488Srsb longlong_t nattrchg;
2311488Srsb longlong_t nlookup;
2321488Srsb longlong_t nreaddir;
2331488Srsb longlong_t ndataread;
2341488Srsb longlong_t ndatawrite;
2351488Srsb longlong_t readthruput;
2361488Srsb longlong_t writethruput;
2371488Srsb char buf[LBUFSZ];
2381488Srsb
2391488Srsb nnewfile = DELTA(ncreate) + DELTA(nmkdir) + DELTA(nsymlink);
2401488Srsb nnamerm = DELTA(nremove) + DELTA(nrmdir);
2411488Srsb nnamechg = DELTA(nrename) + DELTA(nlink) + DELTA(nsymlink);
2421488Srsb nattrret = DELTA(ngetattr) + DELTA(naccess) +
2438261SRobert.Harris@Sun.COM DELTA(ngetsecattr) + DELTA(nfid);
2441488Srsb nattrchg = DELTA(nsetattr) + DELTA(nsetsecattr) + DELTA(nspace);
2451488Srsb nlookup = DELTA(nlookup);
2461488Srsb nreaddir = DELTA(nreaddir);
2471488Srsb ndataread = DELTA(nread);
2481488Srsb ndatawrite = DELTA(nwrite);
2491488Srsb readthruput = DELTA(read_bytes);
2501488Srsb writethruput = DELTA(write_bytes);
2511488Srsb
2521488Srsb if (dispflag & DISP_HEADER) {
2532054Srsb (void) printf(
2541488Srsb " new name name attr attr lookup rddir read read write write\n"
2552054Srsb " file remov chng get set ops ops ops bytes ops bytes\n");
2561488Srsb }
2571488Srsb
2581488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnewfile, ' ', buf);
2591488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnamerm, ' ', buf);
2601488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnamechg, ' ', buf);
2611488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nattrret, ' ', buf);
2621488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nattrchg, ' ', buf);
2631488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", nlookup, ' ', buf);
2641488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nreaddir, ' ', buf);
2651488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", ndataread, ' ', buf);
2661488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", readthruput, ' ', buf);
2671488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", ndatawrite, ' ', buf);
2681488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", writethruput, ' ', buf);
2691488Srsb (void) printf("%s\n", name);
2701488Srsb }
2711488Srsb
2721488Srsb static void
io_display(char * name,vopstats_t * oldvsp,vopstats_t * newvsp,int dispflag)2731488Srsb io_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
2741488Srsb {
2751488Srsb int niceflag = ((dispflag & DISP_RAW) == 0);
2761488Srsb char buf[LBUFSZ];
2771488Srsb
2781488Srsb if (dispflag & DISP_HEADER) {
2792054Srsb (void) printf(
2801488Srsb " read read write write rddir rddir rwlock rwulock\n"
2812054Srsb " ops bytes ops bytes ops bytes ops ops\n");
2821488Srsb }
2831488Srsb
2841488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nread), ' ', buf);
2851488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(read_bytes), ' ', buf);
2861488Srsb
2871488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nwrite), ' ', buf);
2881488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(write_bytes), ' ', buf);
2891488Srsb
2901488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf);
2911488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(readdir_bytes), ' ', buf);
2921488Srsb
2931488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nrwlock), ' ', buf);
2941488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrwunlock), ' ', buf);
2951488Srsb
2961488Srsb (void) printf("%s\n", name);
2971488Srsb }
2981488Srsb
2991488Srsb static void
vm_display(char * name,vopstats_t * oldvsp,vopstats_t * newvsp,int dispflag)3001488Srsb vm_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
3011488Srsb {
3021488Srsb int niceflag = ((dispflag & DISP_RAW) == 0);
3031488Srsb char buf[LBUFSZ];
3041488Srsb
3051488Srsb if (dispflag & DISP_HEADER) {
3062054Srsb (void) printf(" map addmap delmap getpag putpag pagio\n");
3071488Srsb }
3081488Srsb
3091488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmap), ' ', buf);
3101488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(naddmap), ' ', buf);
3111488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ndelmap), ' ', buf);
3121488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetpage), ' ', buf);
3131488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nputpage), ' ', buf);
3141488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(npageio), ' ', buf);
3151488Srsb (void) printf("%s\n", name);
3161488Srsb }
3171488Srsb
3181488Srsb static void
attr_display(char * name,vopstats_t * oldvsp,vopstats_t * newvsp,int dispflag)3191488Srsb attr_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
3201488Srsb {
3211488Srsb int niceflag = ((dispflag & DISP_RAW) == 0);
3221488Srsb char buf[LBUFSZ];
3231488Srsb
3241488Srsb if (dispflag & DISP_HEADER) {
3252054Srsb (void) printf("getattr setattr getsec setsec\n");
3261488Srsb }
3271488Srsb
3281488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetattr), ' ', buf);
3291488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetattr), ' ', buf);
3301488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetsecattr), ' ', buf);
3311488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetsecattr), ' ', buf);
3321488Srsb
3331488Srsb (void) printf("%s\n", name);
3341488Srsb }
3351488Srsb
3361488Srsb static void
naming_display(char * name,vopstats_t * oldvsp,vopstats_t * newvsp,int dispflag)3371488Srsb naming_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
3381488Srsb {
3391488Srsb int niceflag = ((dispflag & DISP_RAW) == 0);
3401488Srsb char buf[LBUFSZ];
3411488Srsb
3421488Srsb if (dispflag & DISP_HEADER) {
3432054Srsb (void) printf(
3442054Srsb "lookup creat remov link renam mkdir rmdir rddir symlnk rdlnk\n");
3451488Srsb }
3461488Srsb
3471488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlookup), ' ', buf);
3481488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(ncreate), ' ', buf);
3491488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nremove), ' ', buf);
3501488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlink), ' ', buf);
3511488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrename), ' ', buf);
3521488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmkdir), ' ', buf);
3531488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrmdir), ' ', buf);
3541488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf);
3551488Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsymlink), ' ', buf);
3561488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreadlink), ' ', buf);
3571488Srsb (void) printf("%s\n", name);
3581488Srsb }
3591488Srsb
3601488Srsb
3611488Srsb #define PRINT_VOPSTAT_CMN(niceflag, vop) \
3621488Srsb if (niceflag) \
3631488Srsb (void) printf("%10s ", #vop); \
3641488Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(n##vop), ' ', buf);
3651488Srsb
3661488Srsb #define PRINT_VOPSTAT(niceflag, vop) \
3671488Srsb PRINT_VOPSTAT_CMN(niceflag, vop); \
3681488Srsb if (niceflag) \
3691488Srsb (void) printf("\n");
3701488Srsb
3711488Srsb #define PRINT_VOPSTAT_IO(niceflag, vop) \
3721488Srsb PRINT_VOPSTAT_CMN(niceflag, vop); \
3731488Srsb PRINTSTAT(niceflag, " %5s\n", "%lld:", \
3741488Srsb DELTA(vop##_bytes), ' ', buf);
3751488Srsb
3761488Srsb static void
vop_display(char * name,vopstats_t * oldvsp,vopstats_t * newvsp,int dispflag)3771488Srsb vop_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
3781488Srsb {
3791488Srsb int niceflag = ((dispflag & DISP_RAW) == 0);
3801488Srsb char buf[LBUFSZ];
3811488Srsb
3821488Srsb if (niceflag) {
3831488Srsb (void) printf("%s\n", name);
3842054Srsb (void) printf(" operation #ops bytes\n");
3851488Srsb }
3861488Srsb
3871488Srsb PRINT_VOPSTAT(niceflag, open);
3881488Srsb PRINT_VOPSTAT(niceflag, close);
3891488Srsb PRINT_VOPSTAT_IO(niceflag, read);
3901488Srsb PRINT_VOPSTAT_IO(niceflag, write);
3911488Srsb PRINT_VOPSTAT(niceflag, ioctl);
3921488Srsb PRINT_VOPSTAT(niceflag, setfl);
3931488Srsb PRINT_VOPSTAT(niceflag, getattr);
3941488Srsb PRINT_VOPSTAT(niceflag, setattr);
3951488Srsb PRINT_VOPSTAT(niceflag, access);
3961488Srsb PRINT_VOPSTAT(niceflag, lookup);
3971488Srsb PRINT_VOPSTAT(niceflag, create);
3981488Srsb PRINT_VOPSTAT(niceflag, remove);
3991488Srsb PRINT_VOPSTAT(niceflag, link);
4001488Srsb PRINT_VOPSTAT(niceflag, rename);
4011488Srsb PRINT_VOPSTAT(niceflag, mkdir);
4021488Srsb PRINT_VOPSTAT(niceflag, rmdir);
4031488Srsb PRINT_VOPSTAT_IO(niceflag, readdir);
4041488Srsb PRINT_VOPSTAT(niceflag, symlink);
4051488Srsb PRINT_VOPSTAT(niceflag, readlink);
4061488Srsb PRINT_VOPSTAT(niceflag, fsync);
4071488Srsb PRINT_VOPSTAT(niceflag, inactive);
4081488Srsb PRINT_VOPSTAT(niceflag, fid);
4091488Srsb PRINT_VOPSTAT(niceflag, rwlock);
4101488Srsb PRINT_VOPSTAT(niceflag, rwunlock);
4111488Srsb PRINT_VOPSTAT(niceflag, seek);
4121488Srsb PRINT_VOPSTAT(niceflag, cmp);
4131488Srsb PRINT_VOPSTAT(niceflag, frlock);
4141488Srsb PRINT_VOPSTAT(niceflag, space);
4151488Srsb PRINT_VOPSTAT(niceflag, realvp);
4161488Srsb PRINT_VOPSTAT(niceflag, getpage);
4171488Srsb PRINT_VOPSTAT(niceflag, putpage);
4181488Srsb PRINT_VOPSTAT(niceflag, map);
4191488Srsb PRINT_VOPSTAT(niceflag, addmap);
4201488Srsb PRINT_VOPSTAT(niceflag, delmap);
4211488Srsb PRINT_VOPSTAT(niceflag, poll);
4221488Srsb PRINT_VOPSTAT(niceflag, dump);
4231488Srsb PRINT_VOPSTAT(niceflag, pathconf);
4241488Srsb PRINT_VOPSTAT(niceflag, pageio);
4251488Srsb PRINT_VOPSTAT(niceflag, dumpctl);
4261488Srsb PRINT_VOPSTAT(niceflag, dispose);
4271488Srsb PRINT_VOPSTAT(niceflag, getsecattr);
4281488Srsb PRINT_VOPSTAT(niceflag, setsecattr);
4291488Srsb PRINT_VOPSTAT(niceflag, shrlock);
4301488Srsb PRINT_VOPSTAT(niceflag, vnevent);
431*11539SChunli.Zhang@Sun.COM PRINT_VOPSTAT(niceflag, reqzcbuf);
432*11539SChunli.Zhang@Sun.COM PRINT_VOPSTAT(niceflag, retzcbuf);
4331488Srsb
4341488Srsb if (niceflag) {
4351488Srsb /* Make it easier on the eyes */
4361488Srsb (void) printf("\n");
4371488Srsb } else {
4381488Srsb (void) printf("%s\n", name);
4391488Srsb }
4401488Srsb }
4411488Srsb
4421488Srsb
4431488Srsb /*
4441488Srsb * Retrieve the vopstats. If kspp (pointer to kstat_t pointer) is non-NULL,
4451488Srsb * then pass it back to the caller.
4461488Srsb *
4471488Srsb * Returns 0 on success, non-zero on failure.
4481488Srsb */
4491488Srsb int
get_vopstats(kstat_ctl_t * kc,char * ksname,vopstats_t * vsp,kstat_t ** kspp)4501488Srsb get_vopstats(kstat_ctl_t *kc, char *ksname, vopstats_t *vsp, kstat_t **kspp)
4511488Srsb {
4521488Srsb kstat_t *ksp;
4531488Srsb
4541488Srsb if (ksname == NULL || *ksname == 0)
4551488Srsb return (1);
4561488Srsb
4571488Srsb errno = 0;
4581488Srsb /* wait for a possibly up-to-date chain */
4591488Srsb while (kstat_chain_update(kc) == -1) {
4601488Srsb if (errno == EAGAIN) {
4611488Srsb errno = 0;
4621488Srsb (void) poll(NULL, 0, RETRY_DELAY);
4631488Srsb continue;
4641488Srsb }
4652054Srsb perror("kstat_chain_update");
4661488Srsb exit(1);
4671488Srsb }
4681488Srsb
4691488Srsb if ((ksp = kstat_lookup(kc, NULL, -1, ksname)) == NULL) {
4701488Srsb return (1);
4711488Srsb }
4721488Srsb
4731488Srsb if (kstat_read(kc, ksp, vsp) == -1) {
4741488Srsb return (1);
4751488Srsb }
4761488Srsb
4771488Srsb if (kspp)
4781488Srsb *kspp = ksp;
4791488Srsb
4801488Srsb return (0);
4811488Srsb }
4821488Srsb
4831488Srsb /*
4841488Srsb * Given a file system type name, determine if it's part of the
4851488Srsb * exception list of file systems that are not to be displayed.
4861488Srsb */
4871488Srsb int
is_exception(char * fsname)4881488Srsb is_exception(char *fsname)
4891488Srsb {
4901488Srsb char **xlp; /* Pointer into the exception list */
4911488Srsb
4921488Srsb static char *exception_list[] = {
4931488Srsb "specfs",
4941488Srsb "fifofs",
4951488Srsb "fd",
4961488Srsb "swapfs",
4971488Srsb "ctfs",
4981488Srsb "objfs",
4991488Srsb "nfsdyn",
5001488Srsb NULL
5011488Srsb };
5021488Srsb
5031488Srsb for (xlp = &exception_list[0]; *xlp != NULL; xlp++) {
5041488Srsb if (strcmp(fsname, *xlp) == 0)
5051488Srsb return (1);
5061488Srsb }
5071488Srsb
5081488Srsb return (0);
5091488Srsb }
5101488Srsb
5111488Srsb /*
5121488Srsb * Plain and simple, build an array of names for fstypes
5131488Srsb * Returns 0, if it encounters a problem.
5141488Srsb */
5151488Srsb int
build_fstype_list(char *** fstypep)5161488Srsb build_fstype_list(char ***fstypep)
5171488Srsb {
5181488Srsb int i;
5191488Srsb int nfstype;
5201488Srsb char buf[FSTYPSZ + 1];
5211488Srsb
5221488Srsb if ((nfstype = sysfs(GETNFSTYP)) < 0) {
5232054Srsb perror("sysfs(GETNFSTYP)");
5241488Srsb return (0);
5251488Srsb }
5261488Srsb
5271488Srsb if ((*fstypep = calloc(nfstype, sizeof (char *))) == NULL) {
5282054Srsb perror("calloc() fstypes");
5291488Srsb return (0);
5301488Srsb }
5311488Srsb
5321488Srsb for (i = 1; i < nfstype; i++) {
5331488Srsb if (sysfs(GETFSTYP, i, buf) < 0) {
5342054Srsb perror("sysfs(GETFSTYP)");
5351488Srsb return (0);
5361488Srsb }
5371488Srsb
5381488Srsb if (buf[0] == 0)
5391488Srsb continue;
5401488Srsb
5411488Srsb /* If this is part of the exception list, move on */
5421488Srsb if (is_exception(buf))
5431488Srsb continue;
5441488Srsb
5451488Srsb if (((*fstypep)[i] = strdup(buf)) == NULL) {
5462054Srsb perror("strdup() fstype name");
5471488Srsb return (0);
5481488Srsb }
5491488Srsb }
5501488Srsb
5511488Srsb return (i);
5521488Srsb }
5531488Srsb
5541488Srsb /*
5551488Srsb * After we're done with getopts(), process the rest of the
5561488Srsb * operands. We have three cases and this is the priority:
5571488Srsb *
5581488Srsb * 1) [ operand... ] interval count
5591488Srsb * 2) [ operand... ] interval
5601488Srsb * 3) [ operand... ]
5611488Srsb *
5621488Srsb * The trick is that any of the operands might start with a number or even
5631488Srsb * be made up exclusively of numbers (and we have to handle negative numbers
5641488Srsb * in case a user/script gets out of line). If we find two operands at the
5651488Srsb * end of the list then we claim case 1. If we find only one operand at the
5661488Srsb * end made up only of number, then we claim case 2. Otherwise, case 3.
5671488Srsb * BTW, argc, argv don't change.
5681488Srsb */
5691488Srsb int
parse_operands(int argc,char ** argv,int optind,long * interval,long * count,entity_t ** entityp)5701488Srsb parse_operands(
5711488Srsb int argc,
5721488Srsb char **argv,
5731488Srsb int optind,
5741488Srsb long *interval,
5751488Srsb long *count,
5761488Srsb entity_t **entityp) /* Array of stat-able entities */
5771488Srsb {
5781488Srsb int nentities = 0; /* Number of entities found */
5791488Srsb int out_of_range; /* Set if 2nd-to-last operand out-of-range */
5801488Srsb
5811488Srsb if (argc == optind)
5821488Srsb return (nentities); /* None found, returns 0 */
5831488Srsb /*
5841488Srsb * We know exactly what the maximum number of entities is going
5851488Srsb * to be: argc - optind
5861488Srsb */
5871488Srsb if ((*entityp = calloc((argc - optind), sizeof (entity_t))) == NULL) {
5882054Srsb perror("calloc() entities");
5891488Srsb return (-1);
5901488Srsb }
5911488Srsb
5921488Srsb for (/* void */; argc > optind; optind++) {
5931488Srsb char *endptr;
5941488Srsb
5951488Srsb /* If we have more than two operands left to process */
5961488Srsb if ((argc - optind) > 2) {
5971488Srsb (*entityp)[nentities++].e_name = strdup(argv[optind]);
5981488Srsb continue;
5991488Srsb }
6001488Srsb
6011488Srsb /* If we're here, then we only have one or two operands left */
6021488Srsb errno = 0;
6031488Srsb out_of_range = 0;
6041488Srsb *interval = strtol(argv[optind], &endptr, 10);
6051488Srsb if (*endptr && !isdigit((int)*endptr)) {
6061488Srsb /* Operand was not a number */
6071488Srsb (*entityp)[nentities++].e_name = strdup(argv[optind]);
6081488Srsb continue;
6091488Srsb } else if (errno == ERANGE || *interval <= 0 ||
6108261SRobert.Harris@Sun.COM *interval > MAXLONG) {
6111488Srsb /* Operand was a number, just out of range */
6121488Srsb out_of_range++;
6131488Srsb }
6141488Srsb
6151488Srsb /*
6161488Srsb * The last operand we saw was a number. If it happened to
6171488Srsb * be the last operand, then it is the interval...
6181488Srsb */
6191488Srsb if ((argc - optind) == 1) {
6201488Srsb /* ...but we need to check the range. */
6211488Srsb if (out_of_range) {
6221488Srsb (void) fprintf(stderr, gettext(
6231488Srsb "interval must be between 1 and "
6241488Srsb "%ld (inclusive)\n"), MAXLONG);
6251488Srsb return (-1);
6261488Srsb } else {
6271488Srsb /*
6281488Srsb * The value of the interval is valid. Set
6291488Srsb * count to something really big so it goes
6301488Srsb * virtually forever.
6311488Srsb */
6321488Srsb *count = MAXLONG;
6331488Srsb break;
6341488Srsb }
6351488Srsb }
6361488Srsb
6371488Srsb /*
6381488Srsb * At this point, we *might* have the interval, but if the
6391488Srsb * next operand isn't a number, then we don't have either
6401488Srsb * the interval nor the count. Both must be set to the
6411488Srsb * defaults. In that case, both the current and the previous
6421488Srsb * operands are stat-able entities.
6431488Srsb */
6441488Srsb errno = 0;
6451488Srsb *count = strtol(argv[optind + 1], &endptr, 10);
6461488Srsb if (*endptr && !isdigit((int)*endptr)) {
6471488Srsb /*
6481488Srsb * Faked out! The last operand wasn't a number so
6491488Srsb * the current and previous operands should be
6501488Srsb * stat-able entities. We also need to reset interval.
6511488Srsb */
6521488Srsb *interval = 0;
6531488Srsb (*entityp)[nentities++].e_name = strdup(argv[optind++]);
6541488Srsb (*entityp)[nentities++].e_name = strdup(argv[optind++]);
6551488Srsb } else if (out_of_range || errno == ERANGE || *count <= 0) {
6561488Srsb (void) fprintf(stderr, gettext(
6571488Srsb "Both interval and count must be between 1 "
6581488Srsb "and %ld (inclusive)\n"), MAXLONG);
6591488Srsb return (-1);
6601488Srsb }
6611488Srsb break; /* Done! */
6621488Srsb }
6631488Srsb return (nentities);
6641488Srsb }
6651488Srsb
6661488Srsb /*
6671488Srsb * set_mntpt() looks at the entity's name (e_name) and finds its
6681488Srsb * mountpoint. To do this, we need to build a list of mountpoints
6691488Srsb * from /etc/mnttab. We only need to do this once and we don't do it
6701488Srsb * if we don't need to look at any mountpoints.
6711488Srsb * Returns 0 on success, non-zero if it couldn't find a mount-point.
6721488Srsb */
6731488Srsb int
set_mntpt(entity_t * ep)6741488Srsb set_mntpt(entity_t *ep)
6751488Srsb {
6761488Srsb static struct mnt {
6771488Srsb struct mnt *m_next;
6781488Srsb char *m_mntpt;
6791488Srsb ulong_t m_fsid; /* From statvfs(), set only as needed */
6801488Srsb } *mnt_list = NULL; /* Linked list of mount-points */
6811488Srsb struct mnt *mntp;
6822396Srsb struct statvfs64 statvfsbuf;
6831488Srsb char *original_name = ep->e_name;
6841488Srsb char path[PATH_MAX];
6851488Srsb
6861488Srsb if (original_name == NULL) /* Shouldn't happen */
6871488Srsb return (1);
6881488Srsb
6891488Srsb /* We only set up mnt_list the first time this is called */
6901488Srsb if (mnt_list == NULL) {
6911488Srsb FILE *fp;
6921488Srsb struct mnttab mnttab;
6931488Srsb
6941488Srsb if ((fp = fopen(MNTTAB, "r")) == NULL) {
6951488Srsb perror(MNTTAB);
6961488Srsb return (1);
6971488Srsb }
6981488Srsb resetmnttab(fp);
6991488Srsb /*
7001488Srsb * We insert at the front of the list so that when we
7011488Srsb * search entries we'll have the last mounted entries
7021488Srsb * first in the list so that we can match the longest
7031488Srsb * mountpoint.
7041488Srsb */
7051488Srsb while (getmntent(fp, &mnttab) == 0) {
7061488Srsb if ((mntp = malloc(sizeof (*mntp))) == NULL) {
7072054Srsb perror("malloc() mount list");
7081488Srsb return (1);
7091488Srsb }
7101488Srsb mntp->m_mntpt = strdup(mnttab.mnt_mountp);
7111488Srsb mntp->m_next = mnt_list;
7121488Srsb mnt_list = mntp;
7131488Srsb }
7141488Srsb (void) fclose(fp);
7151488Srsb }
7161488Srsb
7171488Srsb if (realpath(original_name, path) == NULL) {
7181488Srsb perror(original_name);
7191488Srsb return (1);
7201488Srsb }
7211488Srsb
7221488Srsb /*
7231488Srsb * Now that we have the path, walk through the mnt_list and
7241488Srsb * look for the first (best) match.
7251488Srsb */
7261488Srsb for (mntp = mnt_list; mntp; mntp = mntp->m_next) {
7271488Srsb if (strncmp(path, mntp->m_mntpt, strlen(mntp->m_mntpt)) == 0) {
7281488Srsb if (mntp->m_fsid == 0) {
7292396Srsb if (statvfs64(mntp->m_mntpt, &statvfsbuf)) {
7301488Srsb /* Can't statvfs so no match */
7311488Srsb continue;
7321488Srsb } else {
7331488Srsb mntp->m_fsid = statvfsbuf.f_fsid;
7341488Srsb }
7351488Srsb }
7361488Srsb
7371488Srsb if (ep->e_fsid != mntp->m_fsid) {
7381488Srsb /* No match - Move on */
7391488Srsb continue;
7401488Srsb }
7411488Srsb
7421488Srsb break;
7431488Srsb }
7441488Srsb }
7451488Srsb
7461488Srsb if (mntp == NULL) {
7471488Srsb (void) fprintf(stderr, gettext(
7481488Srsb "Can't find mount point for %s\n"), path);
7491488Srsb return (1);
7501488Srsb }
7511488Srsb
7521488Srsb ep->e_name = strdup(mntp->m_mntpt);
7531488Srsb free(original_name);
7541488Srsb return (0);
7551488Srsb }
7561488Srsb
7571488Srsb /*
7581488Srsb * We have an array of entities that are potentially stat-able. Using
7591488Srsb * the name (e_name) of the entity, attempt to construct a ksname suitable
7601488Srsb * for use by kstat_lookup(3kstat) and fill it into the e_ksname member.
7611488Srsb *
7621488Srsb * We check the e_name against the list of file system types. If there is
7631488Srsb * no match then test to see if the path is valid. If the path is valid,
7641488Srsb * then determine the mountpoint.
7651488Srsb */
7661488Srsb void
set_ksnames(entity_t * entities,int nentities,char ** fstypes,int nfstypes)7671488Srsb set_ksnames(entity_t *entities, int nentities, char **fstypes, int nfstypes)
7681488Srsb {
7691488Srsb int i, j;
7702396Srsb struct statvfs64 statvfsbuf;
7711488Srsb
7721488Srsb for (i = 0; i < nentities; i++) {
7731488Srsb entity_t *ep = &entities[i];
7741488Srsb
7751488Srsb /* Check the name against the list of fstypes */
7761488Srsb for (j = 1; j < nfstypes; j++) {
7771488Srsb if (fstypes[j] && ep->e_name &&
7781488Srsb strcmp(ep->e_name, fstypes[j]) == 0) {
7791488Srsb /* It's a file system type */
7801488Srsb ep->e_type = ENTYPE_FSTYPE;
7818261SRobert.Harris@Sun.COM (void) snprintf(ep->e_ksname, KSTAT_STRLEN,
7828261SRobert.Harris@Sun.COM "%s%s", 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 /*
8311488Srsb * The idea is that 'dspfunc' should only be modified from the default
8321488Srsb * once since the display options are mutually exclusive. If 'dspfunc'
8331488Srsb * only contains the default display function, then all is good and we
8341488Srsb * can set it to the new display function. Otherwise, bail.
8351488Srsb */
8361488Srsb void
set_dispfunc(void (** dspfunc)(char *,vopstats_t *,vopstats_t *,int),void (* newfunc)(char *,vopstats_t *,vopstats_t *,int))8371488Srsb set_dispfunc(
8381488Srsb void (**dspfunc)(char *, vopstats_t *, vopstats_t *, int),
8391488Srsb void (*newfunc)(char *, vopstats_t *, vopstats_t *, int))
8401488Srsb {
8411488Srsb if (*dspfunc != dflt_display) {
8421488Srsb (void) fprintf(stderr, gettext(
8431488Srsb "%s: Display options -{a|f|i|n|v} are mutually exclusive\n"),
8441488Srsb cmdname);
8451488Srsb usage();
8461488Srsb }
8471488Srsb *dspfunc = newfunc;
8481488Srsb }
8491488Srsb
8501488Srsb int
main(int argc,char * argv[])8511488Srsb main(int argc, char *argv[])
8521488Srsb {
8531488Srsb int c;
8541488Srsb int i, j; /* Generic counters */
8551488Srsb int nentities_found;
8569123Sjohn.levon@sun.com int linesout = 0; /* Keeps track of lines printed */
8571488Srsb int printhdr = 0; /* Print a header? 0 = no, 1 = yes */
8581488Srsb int nfstypes; /* Number of fstypes */
8591488Srsb int dispflag = 0; /* Flags for display control */
8601488Srsb long count = 0; /* Number of iterations for display */
8615461Stc35445 int forever; /* Run forever */
8621488Srsb long interval = 0;
8631488Srsb boolean_t fstypes_only = B_FALSE; /* Display fstypes only */
8641488Srsb char **fstypes; /* Array of names of all fstypes */
8651488Srsb int nentities; /* Number of stat-able entities */
8661488Srsb entity_t *entities; /* Array of stat-able entities */
8671488Srsb kstat_ctl_t *kc;
8681488Srsb void (*dfunc)(char *, vopstats_t *, vopstats_t *, int) = dflt_display;
8695461Stc35445 hrtime_t start_n; /* Start time */
8705461Stc35445 hrtime_t period_n; /* Interval in nanoseconds */
8711488Srsb
8721488Srsb extern int optind;
8731488Srsb
8741967Srsb (void) setlocale(LC_ALL, "");
8751967Srsb #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
8761967Srsb #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
8771967Srsb #endif
8781967Srsb (void) textdomain(TEXT_DOMAIN);
8791967Srsb
8808261SRobert.Harris@Sun.COM /* Don't let buffering interfere with piped output. */
8818261SRobert.Harris@Sun.COM (void) setvbuf(stdout, NULL, _IOLBF, 0);
8828261SRobert.Harris@Sun.COM
8831488Srsb cmdname = argv[0];
8841488Srsb while ((c = getopt(argc, argv, OPTIONS)) != EOF) {
8851488Srsb switch (c) {
8861488Srsb
8871488Srsb default:
8881488Srsb usage();
8891488Srsb break;
8901488Srsb
8911680Srsb case 'F': /* Only display available FStypes */
8921680Srsb fstypes_only = B_TRUE;
8931680Srsb break;
8941680Srsb
8951680Srsb #if PARSABLE_OUTPUT
8961488Srsb case 'P': /* Parsable output */
8971488Srsb dispflag |= DISP_RAW;
8981488Srsb break;
8991680Srsb #endif /* PARSABLE_OUTPUT */
9001488Srsb
9011488Srsb case 'T': /* Timestamp */
9021488Srsb if (optarg) {
9031488Srsb if (strcmp(optarg, "u") == 0) {
9049123Sjohn.levon@sun.com timestamp_fmt = UDATE;
9051488Srsb } else if (strcmp(optarg, "d") == 0) {
9069123Sjohn.levon@sun.com timestamp_fmt = DDATE;
9071488Srsb }
9081488Srsb }
9091488Srsb
9101488Srsb /* If it was never set properly... */
9119123Sjohn.levon@sun.com if (timestamp_fmt == NODATE) {
9128261SRobert.Harris@Sun.COM (void) fprintf(stderr, gettext("%s: -T option "
9138261SRobert.Harris@Sun.COM "requires either 'u' or 'd'\n"), cmdname);
9141488Srsb usage();
9151488Srsb }
9161488Srsb break;
9171488Srsb
9181488Srsb case 'a':
9191488Srsb set_dispfunc(&dfunc, attr_display);
9201488Srsb break;
9211488Srsb
9221488Srsb case 'f':
9231488Srsb set_dispfunc(&dfunc, vop_display);
9241488Srsb break;
9251488Srsb
9261488Srsb case 'i':
9271488Srsb set_dispfunc(&dfunc, io_display);
9281488Srsb break;
9291488Srsb
9301488Srsb case 'n':
9311488Srsb set_dispfunc(&dfunc, naming_display);
9321488Srsb break;
9331488Srsb
9341488Srsb case 'v':
9351488Srsb set_dispfunc(&dfunc, vm_display);
9361488Srsb break;
9371488Srsb }
9381488Srsb }
9391488Srsb
9401680Srsb #if PARSABLE_OUTPUT
9419123Sjohn.levon@sun.com if ((dispflag & DISP_RAW) && (timestamp_fmt != NODATE)) {
9421488Srsb (void) fprintf(stderr, gettext(
9438261SRobert.Harris@Sun.COM "-P and -T options are mutually exclusive\n"));
9441488Srsb usage();
9451488Srsb }
9461680Srsb #endif /* PARSABLE_OUTPUT */
9471488Srsb
9481488Srsb /* Gather the list of filesystem types */
9491488Srsb if ((nfstypes = build_fstype_list(&fstypes)) == 0) {
9501488Srsb (void) fprintf(stderr,
9511488Srsb gettext("Can't build list of fstypes\n"));
9521488Srsb exit(1);
9531488Srsb }
9541488Srsb
9551488Srsb nentities = parse_operands(
9561488Srsb argc, argv, optind, &interval, &count, &entities);
9575461Stc35445 forever = count == MAXLONG;
9585461Stc35445 period_n = (hrtime_t)interval * NANOSEC;
9591488Srsb
9601488Srsb if (nentities == -1) /* Set of operands didn't parse properly */
9611488Srsb usage();
9621488Srsb
9631680Srsb if ((nentities == 0) && (fstypes_only == B_FALSE)) {
9641680Srsb (void) fprintf(stderr, gettext(
9651680Srsb "Must specify -F or at least one fstype or mount point\n"));
9661680Srsb usage();
9671680Srsb }
9681680Srsb
9691680Srsb if ((nentities > 0) && (fstypes_only == B_TRUE)) {
9701680Srsb (void) fprintf(stderr, gettext(
9711680Srsb "Cannot use -F with fstypes or mount points\n"));
9721680Srsb usage();
9731680Srsb }
9741680Srsb
9751488Srsb /*
9761680Srsb * If we had no operands (except for interval/count) and we
9771680Srsb * requested FStypes only (-F), then fill in the entities[]
9781680Srsb * array with all available fstypes.
9791488Srsb */
9801680Srsb if ((nentities == 0) && (fstypes_only == B_TRUE)) {
9811488Srsb if ((entities = calloc(nfstypes, sizeof (entity_t))) == NULL) {
9822054Srsb perror("calloc() fstype stats");
9831488Srsb exit(1);
9841488Srsb }
9851488Srsb
9861488Srsb for (i = 1; i < nfstypes; i++) {
9871488Srsb if (fstypes[i]) {
9881488Srsb entities[nentities].e_name = strdup(fstypes[i]);
9891488Srsb nentities++;
9901488Srsb }
9911488Srsb }
9921488Srsb }
9931488Srsb
9941488Srsb set_ksnames(entities, nentities, fstypes, nfstypes);
9951488Srsb
9961488Srsb if ((kc = kstat_open()) == NULL) {
9972054Srsb perror("kstat_open");
9981488Srsb exit(1);
9991488Srsb }
10001488Srsb
10015461Stc35445 /* Set start time */
10025461Stc35445 start_n = gethrtime();
10035461Stc35445
10049123Sjohn.levon@sun.com /* Initial timestamp */
10059123Sjohn.levon@sun.com if (timestamp_fmt != NODATE) {
100610265SKrishnendu.Sadhukhan@Sun.COM print_timestamp(timestamp_fmt);
10079123Sjohn.levon@sun.com linesout++;
10089123Sjohn.levon@sun.com }
10099123Sjohn.levon@sun.com
10101488Srsb /*
10111488Srsb * The following loop walks through the entities[] list to "prime
10121488Srsb * the pump"
10131488Srsb */
10149123Sjohn.levon@sun.com for (j = 0, printhdr = 1; j < nentities; j++) {
10151488Srsb entity_t *ent = &entities[j];
10161488Srsb vopstats_t *vsp = &ent->e_vs[CUR_INDEX];
10171488Srsb kstat_t *ksp = NULL;
10181488Srsb
10191488Srsb if (get_vopstats(kc, ent->e_ksname, vsp, &ksp) == 0) {
10201488Srsb (*dfunc)(ent->e_name, NULL, vsp,
10219123Sjohn.levon@sun.com dispflag_policy(printhdr, dispflag));
10221488Srsb linesout++;
10231488Srsb } else {
10241488Srsb /*
10251488Srsb * If we can't find it the first time through, then
10261488Srsb * get rid of it.
10271488Srsb */
10281488Srsb entities[j].e_ksname[0] = 0;
10291488Srsb
10301488Srsb /*
10311680Srsb * If we're only displaying FStypes (-F) then don't
10321488Srsb * complain about any file systems that might not
10331488Srsb * be loaded. Otherwise, let the user know that
10341488Srsb * he chose poorly.
10351488Srsb */
10361488Srsb if (fstypes_only == B_FALSE) {
10371488Srsb (void) fprintf(stderr, gettext(
10381488Srsb "No statistics available for %s\n"),
10391488Srsb entities[j].e_name);
10401488Srsb }
10411488Srsb }
10429123Sjohn.levon@sun.com printhdr = 0;
10431488Srsb }
10441488Srsb
10455461Stc35445 if (count > 1)
10465461Stc35445 /* Set up signal handler for SIGCONT */
10475461Stc35445 if (signal(SIGCONT, cont_handler) == SIG_ERR)
10485461Stc35445 fail(1, "signal failed");
10495461Stc35445
10505461Stc35445
10511488Srsb BUMP_INDEX(); /* Swap the previous/current indices */
10525461Stc35445 i = 1;
10535461Stc35445 while (forever || i++ <= count) {
10541488Srsb /*
10551488Srsb * No telling how many lines will be printed in any interval.
10561488Srsb * There should be a minimum of HEADERLINES between any
10571488Srsb * header. If we exceed that, no big deal.
10581488Srsb */
10591488Srsb if (linesout > HEADERLINES) {
10601488Srsb linesout = 0;
10611488Srsb printhdr = 1;
10621488Srsb }
10635461Stc35445 /* Have a kip */
10645461Stc35445 sleep_until(&start_n, period_n, forever, &caught_cont);
10651488Srsb
10669123Sjohn.levon@sun.com if (timestamp_fmt != NODATE) {
106710265SKrishnendu.Sadhukhan@Sun.COM print_timestamp(timestamp_fmt);
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