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