xref: /onnv-gate/usr/src/cmd/stat/fsstat/fsstat.c (revision 11539:10d35fc3d7fd)
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