xref: /titanic_53/usr/src/cmd/stat/fsstat/fsstat.c (revision 5a59a8b3d86e67dbe75588879c46e3629f40efec)
1*5a59a8b3Srsb /*
2*5a59a8b3Srsb  * CDDL HEADER START
3*5a59a8b3Srsb  *
4*5a59a8b3Srsb  * The contents of this file are subject to the terms of the
5*5a59a8b3Srsb  * Common Development and Distribution License (the "License").
6*5a59a8b3Srsb  * You may not use this file except in compliance with the License.
7*5a59a8b3Srsb  *
8*5a59a8b3Srsb  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5a59a8b3Srsb  * or http://www.opensolaris.org/os/licensing.
10*5a59a8b3Srsb  * See the License for the specific language governing permissions
11*5a59a8b3Srsb  * and limitations under the License.
12*5a59a8b3Srsb  *
13*5a59a8b3Srsb  * When distributing Covered Code, include this CDDL HEADER in each
14*5a59a8b3Srsb  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5a59a8b3Srsb  * If applicable, add the following below this CDDL HEADER, with the
16*5a59a8b3Srsb  * fields enclosed by brackets "[]" replaced with your own identifying
17*5a59a8b3Srsb  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5a59a8b3Srsb  *
19*5a59a8b3Srsb  * CDDL HEADER END
20*5a59a8b3Srsb  */
21*5a59a8b3Srsb /*
22*5a59a8b3Srsb  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*5a59a8b3Srsb  * Use is subject to license terms.
24*5a59a8b3Srsb  */
25*5a59a8b3Srsb 
26*5a59a8b3Srsb #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*5a59a8b3Srsb 
28*5a59a8b3Srsb #include <stdio.h>
29*5a59a8b3Srsb #include <kstat.h>
30*5a59a8b3Srsb #include <stdlib.h>
31*5a59a8b3Srsb #include <string.h>
32*5a59a8b3Srsb #include <strings.h>
33*5a59a8b3Srsb #include <errno.h>
34*5a59a8b3Srsb #include <limits.h>
35*5a59a8b3Srsb #include <sys/types.h>
36*5a59a8b3Srsb #include <time.h>
37*5a59a8b3Srsb #include <sys/time.h>
38*5a59a8b3Srsb #include <sys/uio.h>
39*5a59a8b3Srsb #include <sys/vnode.h>
40*5a59a8b3Srsb #include <sys/vfs.h>
41*5a59a8b3Srsb #include <sys/statvfs.h>
42*5a59a8b3Srsb #include <sys/fstyp.h>
43*5a59a8b3Srsb #include <sys/fsid.h>
44*5a59a8b3Srsb #include <sys/mnttab.h>
45*5a59a8b3Srsb #include <values.h>
46*5a59a8b3Srsb #include <poll.h>
47*5a59a8b3Srsb #include <ctype.h>
48*5a59a8b3Srsb #include <libintl.h>
49*5a59a8b3Srsb 
50*5a59a8b3Srsb #define	OPTIONS	"PT:afginv"
51*5a59a8b3Srsb 
52*5a59a8b3Srsb /* Time stamp values */
53*5a59a8b3Srsb #define	NODATE	0	/* Default:  No time stamp */
54*5a59a8b3Srsb #define	DDATE	1	/* Standard date format */
55*5a59a8b3Srsb #define	UDATE	2	/* Internal representation of Unix time */
56*5a59a8b3Srsb 
57*5a59a8b3Srsb #define	RETRY_DELAY	250	/* Timeout for poll() */
58*5a59a8b3Srsb #define	HEADERLINES	22	/* Number of lines between display headers */
59*5a59a8b3Srsb 
60*5a59a8b3Srsb #define	LBUFSZ		64	/* Generic size for local buffer */
61*5a59a8b3Srsb 
62*5a59a8b3Srsb /*
63*5a59a8b3Srsb  * The following are used for the nicenum() function
64*5a59a8b3Srsb  */
65*5a59a8b3Srsb #define	KILO_VAL	1024
66*5a59a8b3Srsb #define	ONE_INDEX	3
67*5a59a8b3Srsb 
68*5a59a8b3Srsb #define	NENTITY_INIT	1	/* Initial number of entities to allocate */
69*5a59a8b3Srsb 
70*5a59a8b3Srsb /*
71*5a59a8b3Srsb  * We need to have a mechanism for an old/previous and new/current vopstat
72*5a59a8b3Srsb  * structure.  We only need two per entity and we can swap between them.
73*5a59a8b3Srsb  */
74*5a59a8b3Srsb #define	VS_SIZE	2	/* Size of vopstat array */
75*5a59a8b3Srsb #define	CUR_INDEX	(vs_i)
76*5a59a8b3Srsb #define	PREV_INDEX	((vs_i == 0) ? 1 : 0)	/* Opposite of CUR_INDEX */
77*5a59a8b3Srsb #define	BUMP_INDEX()	vs_i = ((vs_i == 0) ? 1 : 0)
78*5a59a8b3Srsb 
79*5a59a8b3Srsb /*
80*5a59a8b3Srsb  * An "entity" is anything we're collecting statistics on, it could
81*5a59a8b3Srsb  * be a mountpoint or an FS-type.
82*5a59a8b3Srsb  * e_name is the name of the entity (e.g. mount point or FS-type)
83*5a59a8b3Srsb  * e_ksname is the name of the associated kstat
84*5a59a8b3Srsb  * e_vs is an array of vopstats.  This is used to keep track of "previous"
85*5a59a8b3Srsb  * and "current" vopstats.
86*5a59a8b3Srsb  */
87*5a59a8b3Srsb typedef struct entity {
88*5a59a8b3Srsb 	char		*e_name;		/* name of entity */
89*5a59a8b3Srsb 	vopstats_t	*e_vs;			/* Array of vopstats */
90*5a59a8b3Srsb 	ulong_t		e_fsid;			/* fsid for ENTYPE_MNTPT only */
91*5a59a8b3Srsb 	int		e_type;			/* type of entity */
92*5a59a8b3Srsb 	char		e_ksname[KSTAT_STRLEN];	/* kstat name */
93*5a59a8b3Srsb } entity_t;
94*5a59a8b3Srsb 
95*5a59a8b3Srsb /* Types of entities (e_type) */
96*5a59a8b3Srsb #define	ENTYPE_UNKNOWN	0	/* UNKNOWN must be zero since we calloc() */
97*5a59a8b3Srsb #define	ENTYPE_FSTYPE	1
98*5a59a8b3Srsb #define	ENTYPE_MNTPT	2
99*5a59a8b3Srsb 
100*5a59a8b3Srsb /* If more sub-one units are added, make sure to adjust ONE_INDEX above */
101*5a59a8b3Srsb static char units[] = "num KMGTPE";
102*5a59a8b3Srsb 
103*5a59a8b3Srsb static char	*cmdname;	/* name of this command */
104*5a59a8b3Srsb 
105*5a59a8b3Srsb static int	vs_i = 0;	/* Index of current vs[] slot */
106*5a59a8b3Srsb 
107*5a59a8b3Srsb static void
108*5a59a8b3Srsb usage()
109*5a59a8b3Srsb {
110*5a59a8b3Srsb 	(void) fprintf(stderr,
111*5a59a8b3Srsb 	    gettext("Usage: %s [-a|f|i|n|v] [-P] [ fstype | fspath ]... "
112*5a59a8b3Srsb 	    "[interval [count]]\n"), cmdname);
113*5a59a8b3Srsb 	exit(2);
114*5a59a8b3Srsb }
115*5a59a8b3Srsb 
116*5a59a8b3Srsb /*
117*5a59a8b3Srsb  * Given a 64-bit number and a starting unit (e.g., n - nanoseconds),
118*5a59a8b3Srsb  * convert the number to a 5-character representation including any
119*5a59a8b3Srsb  * decimal point and single-character unit.  Put that representation
120*5a59a8b3Srsb  * into the array "buf" (which had better be big enough).
121*5a59a8b3Srsb  */
122*5a59a8b3Srsb char *
123*5a59a8b3Srsb nicenum(uint64_t num, char unit, char *buf)
124*5a59a8b3Srsb {
125*5a59a8b3Srsb 	uint64_t n = num;
126*5a59a8b3Srsb 	int unit_index;
127*5a59a8b3Srsb 	int index;
128*5a59a8b3Srsb 	char u;
129*5a59a8b3Srsb 
130*5a59a8b3Srsb 	/* If the user passed in a NUL/zero unit, use the blank value for 1 */
131*5a59a8b3Srsb 	if (unit == '\0')
132*5a59a8b3Srsb 		unit = ' ';
133*5a59a8b3Srsb 
134*5a59a8b3Srsb 	unit_index = 0;
135*5a59a8b3Srsb 	while (units[unit_index] != unit) {
136*5a59a8b3Srsb 		unit_index++;
137*5a59a8b3Srsb 		if (unit_index > sizeof (units) - 1) {
138*5a59a8b3Srsb 			(void) sprintf(buf, "??");
139*5a59a8b3Srsb 			return (buf);
140*5a59a8b3Srsb 		}
141*5a59a8b3Srsb 	}
142*5a59a8b3Srsb 
143*5a59a8b3Srsb 	index = 0;
144*5a59a8b3Srsb 	while (n >= KILO_VAL) {
145*5a59a8b3Srsb 		n = (n + (KILO_VAL / 2)) / KILO_VAL; /* Round up or down */
146*5a59a8b3Srsb 		index++;
147*5a59a8b3Srsb 		unit_index++;
148*5a59a8b3Srsb 	}
149*5a59a8b3Srsb 
150*5a59a8b3Srsb 	if (unit_index >= sizeof (units) - 1) {
151*5a59a8b3Srsb 		(void) sprintf(buf, "??");
152*5a59a8b3Srsb 		return (buf);
153*5a59a8b3Srsb 	}
154*5a59a8b3Srsb 
155*5a59a8b3Srsb 	u = units[unit_index];
156*5a59a8b3Srsb 
157*5a59a8b3Srsb 	if (unit_index == ONE_INDEX) {
158*5a59a8b3Srsb 		(void) sprintf(buf, "%llu", (u_longlong_t)n);
159*5a59a8b3Srsb 	} else if (n < 10 && (num & (num - 1)) != 0) {
160*5a59a8b3Srsb 		(void) sprintf(buf, "%.2f%c",
161*5a59a8b3Srsb 		    (double)num / (1ULL << 10 * index), u);
162*5a59a8b3Srsb 	} else if (n < 100 && (num & (num - 1)) != 0) {
163*5a59a8b3Srsb 		(void) sprintf(buf, "%.1f%c",
164*5a59a8b3Srsb 		    (double)num / (1ULL << 10 * index), u);
165*5a59a8b3Srsb 	} else {
166*5a59a8b3Srsb 		(void) sprintf(buf, "%llu%c", (u_longlong_t)n, u);
167*5a59a8b3Srsb 	}
168*5a59a8b3Srsb 
169*5a59a8b3Srsb 	return (buf);
170*5a59a8b3Srsb }
171*5a59a8b3Srsb 
172*5a59a8b3Srsb 
173*5a59a8b3Srsb #define	RAWVAL(ptr, member) ((ptr)->member.value.ui64)
174*5a59a8b3Srsb #define	DELTA(member)	\
175*5a59a8b3Srsb 	(newvsp->member.value.ui64 - (oldvsp ? oldvsp->member.value.ui64 : 0))
176*5a59a8b3Srsb #define	OLDPRINTSTAT(isnice, nicestring, niceval, rawstring, rawval)	\
177*5a59a8b3Srsb 		(isnice) ? 						\
178*5a59a8b3Srsb 			(void) printf((nicestring), (niceval))		\
179*5a59a8b3Srsb 		:							\
180*5a59a8b3Srsb 			(void) printf((rawstring), (rawval))
181*5a59a8b3Srsb #define	PRINTSTAT(isnice, nicestring, rawstring, rawval, unit, buf)	\
182*5a59a8b3Srsb 	(isnice) ?	 						\
183*5a59a8b3Srsb 		(void) printf((nicestring), nicenum(rawval, unit, buf))	\
184*5a59a8b3Srsb 	:								\
185*5a59a8b3Srsb 		(void) printf((rawstring), (rawval))
186*5a59a8b3Srsb 
187*5a59a8b3Srsb /* Values for display flag */
188*5a59a8b3Srsb #define	DISP_HEADER	0x1
189*5a59a8b3Srsb #define	DISP_RAW	0x2
190*5a59a8b3Srsb 
191*5a59a8b3Srsb /*
192*5a59a8b3Srsb  * The policy for dealing with multiple flags is dealt with here.
193*5a59a8b3Srsb  * Currently, if we are displaying raw output, then don't allow
194*5a59a8b3Srsb  * headers to be printed.
195*5a59a8b3Srsb  */
196*5a59a8b3Srsb int
197*5a59a8b3Srsb dispflag_policy(int printhdr, int dispflag)
198*5a59a8b3Srsb {
199*5a59a8b3Srsb 	/* If we're not displaying raw output, then allow headers to print */
200*5a59a8b3Srsb 	if ((dispflag & DISP_RAW) == 0) {
201*5a59a8b3Srsb 		if (printhdr) {
202*5a59a8b3Srsb 			dispflag |= DISP_HEADER;
203*5a59a8b3Srsb 		}
204*5a59a8b3Srsb 	}
205*5a59a8b3Srsb 
206*5a59a8b3Srsb 	return (dispflag);
207*5a59a8b3Srsb }
208*5a59a8b3Srsb 
209*5a59a8b3Srsb static void
210*5a59a8b3Srsb dflt_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
211*5a59a8b3Srsb {
212*5a59a8b3Srsb 	int		niceflag = ((dispflag & DISP_RAW) == 0);
213*5a59a8b3Srsb 	longlong_t	nnewfile;
214*5a59a8b3Srsb 	longlong_t	nnamerm;
215*5a59a8b3Srsb 	longlong_t	nnamechg;
216*5a59a8b3Srsb 	longlong_t	nattrret;
217*5a59a8b3Srsb 	longlong_t	nattrchg;
218*5a59a8b3Srsb 	longlong_t	nlookup;
219*5a59a8b3Srsb 	longlong_t	nreaddir;
220*5a59a8b3Srsb 	longlong_t	ndataread;
221*5a59a8b3Srsb 	longlong_t	ndatawrite;
222*5a59a8b3Srsb 	longlong_t	readthruput;
223*5a59a8b3Srsb 	longlong_t	writethruput;
224*5a59a8b3Srsb 	char		buf[LBUFSZ];
225*5a59a8b3Srsb 
226*5a59a8b3Srsb 	nnewfile = DELTA(ncreate) + DELTA(nmkdir) + DELTA(nsymlink);
227*5a59a8b3Srsb 	nnamerm = DELTA(nremove) + DELTA(nrmdir);
228*5a59a8b3Srsb 	nnamechg = DELTA(nrename) + DELTA(nlink) + DELTA(nsymlink);
229*5a59a8b3Srsb 	nattrret = DELTA(ngetattr) + DELTA(naccess) +
230*5a59a8b3Srsb 				DELTA(ngetsecattr) + DELTA(nfid);
231*5a59a8b3Srsb 	nattrchg = DELTA(nsetattr) + DELTA(nsetsecattr) + DELTA(nspace);
232*5a59a8b3Srsb 	nlookup = DELTA(nlookup);
233*5a59a8b3Srsb 	nreaddir = DELTA(nreaddir);
234*5a59a8b3Srsb 	ndataread = DELTA(nread);
235*5a59a8b3Srsb 	ndatawrite = DELTA(nwrite);
236*5a59a8b3Srsb 	readthruput = DELTA(read_bytes);
237*5a59a8b3Srsb 	writethruput = DELTA(write_bytes);
238*5a59a8b3Srsb 
239*5a59a8b3Srsb 	if (dispflag & DISP_HEADER) {
240*5a59a8b3Srsb 		(void) printf(gettext(
241*5a59a8b3Srsb " new  name   name  attr  attr lookup rddir  read read  write write\n"
242*5a59a8b3Srsb " file remov  chng   get   set    ops   ops   ops bytes   ops bytes\n"));
243*5a59a8b3Srsb 	}
244*5a59a8b3Srsb 
245*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", nnewfile, ' ', buf);
246*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", nnamerm, ' ', buf);
247*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", nnamechg, ' ', buf);
248*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", nattrret, ' ', buf);
249*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", nattrchg, ' ', buf);
250*5a59a8b3Srsb 	PRINTSTAT(niceflag, " %5s ", "%lld:", nlookup, ' ', buf);
251*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", nreaddir, ' ', buf);
252*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", ndataread, ' ', buf);
253*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", readthruput, ' ', buf);
254*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", ndatawrite, ' ', buf);
255*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", writethruput, ' ', buf);
256*5a59a8b3Srsb 	(void) printf("%s\n", name);
257*5a59a8b3Srsb }
258*5a59a8b3Srsb 
259*5a59a8b3Srsb static void
260*5a59a8b3Srsb io_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
261*5a59a8b3Srsb {
262*5a59a8b3Srsb 	int		niceflag = ((dispflag & DISP_RAW) == 0);
263*5a59a8b3Srsb 	char		buf[LBUFSZ];
264*5a59a8b3Srsb 
265*5a59a8b3Srsb 	if (dispflag & DISP_HEADER) {
266*5a59a8b3Srsb 		(void) printf(gettext(
267*5a59a8b3Srsb " read read  write write rddir rddir rwlock rwulock\n"
268*5a59a8b3Srsb "  ops bytes   ops bytes   ops bytes    ops     ops\n"));
269*5a59a8b3Srsb 	}
270*5a59a8b3Srsb 
271*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nread), ' ', buf);
272*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(read_bytes), ' ', buf);
273*5a59a8b3Srsb 
274*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nwrite), ' ', buf);
275*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(write_bytes), ' ', buf);
276*5a59a8b3Srsb 
277*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf);
278*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(readdir_bytes), ' ', buf);
279*5a59a8b3Srsb 
280*5a59a8b3Srsb 	PRINTSTAT(niceflag, " %5s   ", "%lld:", DELTA(nrwlock), ' ', buf);
281*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrwunlock), ' ', buf);
282*5a59a8b3Srsb 
283*5a59a8b3Srsb 	(void) printf("%s\n", name);
284*5a59a8b3Srsb }
285*5a59a8b3Srsb 
286*5a59a8b3Srsb static void
287*5a59a8b3Srsb vm_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
288*5a59a8b3Srsb {
289*5a59a8b3Srsb 	int		niceflag = ((dispflag & DISP_RAW) == 0);
290*5a59a8b3Srsb 	char		buf[LBUFSZ];
291*5a59a8b3Srsb 
292*5a59a8b3Srsb 	if (dispflag & DISP_HEADER) {
293*5a59a8b3Srsb 		(void) printf(
294*5a59a8b3Srsb 		    gettext("  map addmap delmap getpag putpag pagio\n"));
295*5a59a8b3Srsb 	}
296*5a59a8b3Srsb 
297*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmap), ' ', buf);
298*5a59a8b3Srsb 	PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(naddmap), ' ', buf);
299*5a59a8b3Srsb 	PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ndelmap), ' ', buf);
300*5a59a8b3Srsb 	PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetpage), ' ', buf);
301*5a59a8b3Srsb 	PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nputpage), ' ', buf);
302*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(npageio), ' ', buf);
303*5a59a8b3Srsb 	(void) printf("%s\n", name);
304*5a59a8b3Srsb }
305*5a59a8b3Srsb 
306*5a59a8b3Srsb static void
307*5a59a8b3Srsb attr_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
308*5a59a8b3Srsb {
309*5a59a8b3Srsb 	int		niceflag = ((dispflag & DISP_RAW) == 0);
310*5a59a8b3Srsb 	char		buf[LBUFSZ];
311*5a59a8b3Srsb 
312*5a59a8b3Srsb 	if (dispflag & DISP_HEADER) {
313*5a59a8b3Srsb 		(void) printf(gettext("getattr setattr getsec  setsec\n"));
314*5a59a8b3Srsb 	}
315*5a59a8b3Srsb 
316*5a59a8b3Srsb 	PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetattr), ' ', buf);
317*5a59a8b3Srsb 	PRINTSTAT(niceflag, "  %5s ", "%lld:", DELTA(nsetattr), ' ', buf);
318*5a59a8b3Srsb 	PRINTSTAT(niceflag, "  %5s ", "%lld:", DELTA(ngetsecattr), ' ', buf);
319*5a59a8b3Srsb 	PRINTSTAT(niceflag, "  %5s ", "%lld:", DELTA(nsetsecattr), ' ', buf);
320*5a59a8b3Srsb 
321*5a59a8b3Srsb 	(void) printf("%s\n", name);
322*5a59a8b3Srsb }
323*5a59a8b3Srsb 
324*5a59a8b3Srsb static void
325*5a59a8b3Srsb naming_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
326*5a59a8b3Srsb {
327*5a59a8b3Srsb 	int		niceflag = ((dispflag & DISP_RAW) == 0);
328*5a59a8b3Srsb 	char		buf[LBUFSZ];
329*5a59a8b3Srsb 
330*5a59a8b3Srsb 	if (dispflag & DISP_HEADER) {
331*5a59a8b3Srsb 		(void) printf(gettext(
332*5a59a8b3Srsb 	"lookup creat remov  link renam mkdir rmdir rddir symlnk rdlnk\n"));
333*5a59a8b3Srsb 	}
334*5a59a8b3Srsb 
335*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s  ", "%lld:", DELTA(nlookup), ' ', buf);
336*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(ncreate), ' ', buf);
337*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nremove), ' ', buf);
338*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlink), ' ', buf);
339*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrename), ' ', buf);
340*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmkdir), ' ', buf);
341*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrmdir), ' ', buf);
342*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf);
343*5a59a8b3Srsb 	PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsymlink), ' ', buf);
344*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreadlink), ' ', buf);
345*5a59a8b3Srsb 	(void) printf("%s\n", name);
346*5a59a8b3Srsb }
347*5a59a8b3Srsb 
348*5a59a8b3Srsb 
349*5a59a8b3Srsb #define	PRINT_VOPSTAT_CMN(niceflag, vop)				\
350*5a59a8b3Srsb 	if (niceflag)							\
351*5a59a8b3Srsb 		(void) printf("%10s ", #vop);				\
352*5a59a8b3Srsb 	PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(n##vop), ' ', buf);
353*5a59a8b3Srsb 
354*5a59a8b3Srsb #define	PRINT_VOPSTAT(niceflag, vop) 					\
355*5a59a8b3Srsb 	PRINT_VOPSTAT_CMN(niceflag, vop);				\
356*5a59a8b3Srsb 	if (niceflag)							\
357*5a59a8b3Srsb 		(void) printf("\n");
358*5a59a8b3Srsb 
359*5a59a8b3Srsb #define	PRINT_VOPSTAT_IO(niceflag, vop)					\
360*5a59a8b3Srsb 	PRINT_VOPSTAT_CMN(niceflag, vop);				\
361*5a59a8b3Srsb 	PRINTSTAT(niceflag, " %5s\n", "%lld:",				\
362*5a59a8b3Srsb 		DELTA(vop##_bytes), ' ', buf);
363*5a59a8b3Srsb 
364*5a59a8b3Srsb static void
365*5a59a8b3Srsb vop_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
366*5a59a8b3Srsb {
367*5a59a8b3Srsb 	int		niceflag = ((dispflag & DISP_RAW) == 0);
368*5a59a8b3Srsb 	char		buf[LBUFSZ];
369*5a59a8b3Srsb 
370*5a59a8b3Srsb 	if (niceflag) {
371*5a59a8b3Srsb 		(void) printf("%s\n", name);
372*5a59a8b3Srsb 		(void) printf(gettext(" operation  #ops  bytes\n"));
373*5a59a8b3Srsb 	}
374*5a59a8b3Srsb 
375*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, open);
376*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, close);
377*5a59a8b3Srsb 	PRINT_VOPSTAT_IO(niceflag, read);
378*5a59a8b3Srsb 	PRINT_VOPSTAT_IO(niceflag, write);
379*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, ioctl);
380*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, setfl);
381*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, getattr);
382*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, setattr);
383*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, access);
384*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, lookup);
385*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, create);
386*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, remove);
387*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, link);
388*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, rename);
389*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, mkdir);
390*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, rmdir);
391*5a59a8b3Srsb 	PRINT_VOPSTAT_IO(niceflag, readdir);
392*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, symlink);
393*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, readlink);
394*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, fsync);
395*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, inactive);
396*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, fid);
397*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, rwlock);
398*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, rwunlock);
399*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, seek);
400*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, cmp);
401*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, frlock);
402*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, space);
403*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, realvp);
404*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, getpage);
405*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, putpage);
406*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, map);
407*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, addmap);
408*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, delmap);
409*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, poll);
410*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, dump);
411*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, pathconf);
412*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, pageio);
413*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, dumpctl);
414*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, dispose);
415*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, getsecattr);
416*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, setsecattr);
417*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, shrlock);
418*5a59a8b3Srsb 	PRINT_VOPSTAT(niceflag, vnevent);
419*5a59a8b3Srsb 
420*5a59a8b3Srsb 	if (niceflag) {
421*5a59a8b3Srsb 		/* Make it easier on the eyes */
422*5a59a8b3Srsb 		(void) printf("\n");
423*5a59a8b3Srsb 	} else {
424*5a59a8b3Srsb 		(void) printf("%s\n", name);
425*5a59a8b3Srsb 	}
426*5a59a8b3Srsb }
427*5a59a8b3Srsb 
428*5a59a8b3Srsb 
429*5a59a8b3Srsb /*
430*5a59a8b3Srsb  * Retrieve the vopstats.  If kspp (pointer to kstat_t pointer) is non-NULL,
431*5a59a8b3Srsb  * then pass it back to the caller.
432*5a59a8b3Srsb  *
433*5a59a8b3Srsb  * Returns 0 on success, non-zero on failure.
434*5a59a8b3Srsb  */
435*5a59a8b3Srsb int
436*5a59a8b3Srsb get_vopstats(kstat_ctl_t *kc, char *ksname, vopstats_t *vsp, kstat_t **kspp)
437*5a59a8b3Srsb {
438*5a59a8b3Srsb 	kstat_t		*ksp;
439*5a59a8b3Srsb 
440*5a59a8b3Srsb 	if (ksname == NULL || *ksname == 0)
441*5a59a8b3Srsb 		return (1);
442*5a59a8b3Srsb 
443*5a59a8b3Srsb 	errno = 0;
444*5a59a8b3Srsb 	/* wait for a possibly up-to-date chain */
445*5a59a8b3Srsb 	while (kstat_chain_update(kc) == -1) {
446*5a59a8b3Srsb 		if (errno == EAGAIN) {
447*5a59a8b3Srsb 			errno = 0;
448*5a59a8b3Srsb 			(void) poll(NULL, 0, RETRY_DELAY);
449*5a59a8b3Srsb 			continue;
450*5a59a8b3Srsb 		}
451*5a59a8b3Srsb 		perror(gettext("kstat_chain_update"));
452*5a59a8b3Srsb 		exit(1);
453*5a59a8b3Srsb 	}
454*5a59a8b3Srsb 
455*5a59a8b3Srsb 	if ((ksp = kstat_lookup(kc, NULL, -1, ksname)) == NULL) {
456*5a59a8b3Srsb 		return (1);
457*5a59a8b3Srsb 	}
458*5a59a8b3Srsb 
459*5a59a8b3Srsb 	if (kstat_read(kc, ksp, vsp) == -1) {
460*5a59a8b3Srsb 		return (1);
461*5a59a8b3Srsb 	}
462*5a59a8b3Srsb 
463*5a59a8b3Srsb 	if (kspp)
464*5a59a8b3Srsb 		*kspp = ksp;
465*5a59a8b3Srsb 
466*5a59a8b3Srsb 	return (0);
467*5a59a8b3Srsb }
468*5a59a8b3Srsb 
469*5a59a8b3Srsb /*
470*5a59a8b3Srsb  * Given a file system type name, determine if it's part of the
471*5a59a8b3Srsb  * exception list of file systems that are not to be displayed.
472*5a59a8b3Srsb  */
473*5a59a8b3Srsb int
474*5a59a8b3Srsb is_exception(char *fsname)
475*5a59a8b3Srsb {
476*5a59a8b3Srsb 	char **xlp;	/* Pointer into the exception list */
477*5a59a8b3Srsb 
478*5a59a8b3Srsb 	static char *exception_list[] = {
479*5a59a8b3Srsb 		"specfs",
480*5a59a8b3Srsb 		"fifofs",
481*5a59a8b3Srsb 		"fd",
482*5a59a8b3Srsb 		"swapfs",
483*5a59a8b3Srsb 		"ctfs",
484*5a59a8b3Srsb 		"objfs",
485*5a59a8b3Srsb 		"nfsdyn",
486*5a59a8b3Srsb 		NULL
487*5a59a8b3Srsb 	};
488*5a59a8b3Srsb 
489*5a59a8b3Srsb 	for (xlp = &exception_list[0]; *xlp != NULL; xlp++) {
490*5a59a8b3Srsb 		if (strcmp(fsname, *xlp) == 0)
491*5a59a8b3Srsb 			return (1);
492*5a59a8b3Srsb 	}
493*5a59a8b3Srsb 
494*5a59a8b3Srsb 	return (0);
495*5a59a8b3Srsb }
496*5a59a8b3Srsb 
497*5a59a8b3Srsb /*
498*5a59a8b3Srsb  * Plain and simple, build an array of names for fstypes
499*5a59a8b3Srsb  * Returns 0, if it encounters a problem.
500*5a59a8b3Srsb  */
501*5a59a8b3Srsb int
502*5a59a8b3Srsb build_fstype_list(char ***fstypep)
503*5a59a8b3Srsb {
504*5a59a8b3Srsb 	int	i;
505*5a59a8b3Srsb 	int	nfstype;
506*5a59a8b3Srsb 	char	buf[FSTYPSZ + 1];
507*5a59a8b3Srsb 
508*5a59a8b3Srsb 	if ((nfstype = sysfs(GETNFSTYP)) < 0) {
509*5a59a8b3Srsb 		perror(gettext("sysfs(GETNFSTYP)"));
510*5a59a8b3Srsb 		return (0);
511*5a59a8b3Srsb 	}
512*5a59a8b3Srsb 
513*5a59a8b3Srsb 	if ((*fstypep = calloc(nfstype, sizeof (char *))) == NULL) {
514*5a59a8b3Srsb 		perror(gettext("calloc on fstypes"));
515*5a59a8b3Srsb 		return (0);
516*5a59a8b3Srsb 	}
517*5a59a8b3Srsb 
518*5a59a8b3Srsb 	for (i = 1; i < nfstype; i++) {
519*5a59a8b3Srsb 		if (sysfs(GETFSTYP, i, buf) < 0) {
520*5a59a8b3Srsb 			perror(gettext("sysfs(GETFSTYP)"));
521*5a59a8b3Srsb 			return (0);
522*5a59a8b3Srsb 		}
523*5a59a8b3Srsb 
524*5a59a8b3Srsb 		if (buf[0] == 0)
525*5a59a8b3Srsb 			continue;
526*5a59a8b3Srsb 
527*5a59a8b3Srsb 		/* If this is part of the exception list, move on */
528*5a59a8b3Srsb 		if (is_exception(buf))
529*5a59a8b3Srsb 			continue;
530*5a59a8b3Srsb 
531*5a59a8b3Srsb 		if (((*fstypep)[i] = strdup(buf)) == NULL) {
532*5a59a8b3Srsb 			perror(gettext("strdup() of fstype name"));
533*5a59a8b3Srsb 			return (0);
534*5a59a8b3Srsb 		}
535*5a59a8b3Srsb 	}
536*5a59a8b3Srsb 
537*5a59a8b3Srsb 	return (i);
538*5a59a8b3Srsb }
539*5a59a8b3Srsb 
540*5a59a8b3Srsb /*
541*5a59a8b3Srsb  * After we're done with getopts(), process the rest of the
542*5a59a8b3Srsb  * operands.  We have three cases and this is the priority:
543*5a59a8b3Srsb  *
544*5a59a8b3Srsb  * 1) [ operand... ] interval count
545*5a59a8b3Srsb  * 2) [ operand... ] interval
546*5a59a8b3Srsb  * 3) [ operand... ]
547*5a59a8b3Srsb  *
548*5a59a8b3Srsb  * The trick is that any of the operands might start with a number or even
549*5a59a8b3Srsb  * be made up exclusively of numbers (and we have to handle negative numbers
550*5a59a8b3Srsb  * in case a user/script gets out of line).  If we find two operands at the
551*5a59a8b3Srsb  * end of the list then we claim case 1.  If we find only one operand at the
552*5a59a8b3Srsb  * end made up only of number, then we claim case 2.  Otherwise, case 3.
553*5a59a8b3Srsb  * BTW, argc, argv don't change.
554*5a59a8b3Srsb  */
555*5a59a8b3Srsb int
556*5a59a8b3Srsb parse_operands(
557*5a59a8b3Srsb 	int		argc,
558*5a59a8b3Srsb 	char		**argv,
559*5a59a8b3Srsb 	int		optind,
560*5a59a8b3Srsb 	long		*interval,
561*5a59a8b3Srsb 	long		*count,
562*5a59a8b3Srsb 	entity_t	**entityp)	/* Array of stat-able entities */
563*5a59a8b3Srsb {
564*5a59a8b3Srsb 	int	nentities = 0;	/* Number of entities found */
565*5a59a8b3Srsb 	int	out_of_range;	/* Set if 2nd-to-last operand out-of-range */
566*5a59a8b3Srsb 
567*5a59a8b3Srsb 	if (argc == optind)
568*5a59a8b3Srsb 		return (nentities);	/* None found, returns 0 */
569*5a59a8b3Srsb 	/*
570*5a59a8b3Srsb 	 * We know exactly what the maximum number of entities is going
571*5a59a8b3Srsb 	 * to be:  argc - optind
572*5a59a8b3Srsb 	 */
573*5a59a8b3Srsb 	if ((*entityp = calloc((argc - optind), sizeof (entity_t))) == NULL) {
574*5a59a8b3Srsb 		perror(gettext("calloc"));
575*5a59a8b3Srsb 		return (-1);
576*5a59a8b3Srsb 	}
577*5a59a8b3Srsb 
578*5a59a8b3Srsb 	for (/* void */; argc > optind; optind++) {
579*5a59a8b3Srsb 		char	*endptr;
580*5a59a8b3Srsb 
581*5a59a8b3Srsb 		/* If we have more than two operands left to process */
582*5a59a8b3Srsb 		if ((argc - optind) > 2) {
583*5a59a8b3Srsb 			(*entityp)[nentities++].e_name = strdup(argv[optind]);
584*5a59a8b3Srsb 			continue;
585*5a59a8b3Srsb 		}
586*5a59a8b3Srsb 
587*5a59a8b3Srsb 		/* If we're here, then we only have one or two operands left */
588*5a59a8b3Srsb 		errno = 0;
589*5a59a8b3Srsb 		out_of_range = 0;
590*5a59a8b3Srsb 		*interval = strtol(argv[optind], &endptr, 10);
591*5a59a8b3Srsb 		if (*endptr && !isdigit((int)*endptr)) {
592*5a59a8b3Srsb 			/* Operand was not a number */
593*5a59a8b3Srsb 			(*entityp)[nentities++].e_name = strdup(argv[optind]);
594*5a59a8b3Srsb 			continue;
595*5a59a8b3Srsb 		} else if (errno == ERANGE || *interval <= 0 ||
596*5a59a8b3Srsb 							*interval > MAXLONG) {
597*5a59a8b3Srsb 			/* Operand was a number, just out of range */
598*5a59a8b3Srsb 			out_of_range++;
599*5a59a8b3Srsb 		}
600*5a59a8b3Srsb 
601*5a59a8b3Srsb 		/*
602*5a59a8b3Srsb 		 * The last operand we saw was a number.  If it happened to
603*5a59a8b3Srsb 		 * be the last operand, then it is the interval...
604*5a59a8b3Srsb 		 */
605*5a59a8b3Srsb 		if ((argc - optind) == 1) {
606*5a59a8b3Srsb 			/* ...but we need to check the range. */
607*5a59a8b3Srsb 			if (out_of_range) {
608*5a59a8b3Srsb 				(void) fprintf(stderr, gettext(
609*5a59a8b3Srsb 				    "interval must be between 1 and "
610*5a59a8b3Srsb 				    "%ld (inclusive)\n"), MAXLONG);
611*5a59a8b3Srsb 				return (-1);
612*5a59a8b3Srsb 			} else {
613*5a59a8b3Srsb 				/*
614*5a59a8b3Srsb 				 * The value of the interval is valid. Set
615*5a59a8b3Srsb 				 * count to something really big so it goes
616*5a59a8b3Srsb 				 * virtually forever.
617*5a59a8b3Srsb 				 */
618*5a59a8b3Srsb 				*count = MAXLONG;
619*5a59a8b3Srsb 				break;
620*5a59a8b3Srsb 			}
621*5a59a8b3Srsb 		}
622*5a59a8b3Srsb 
623*5a59a8b3Srsb 		/*
624*5a59a8b3Srsb 		 * At this point, we *might* have the interval, but if the
625*5a59a8b3Srsb 		 * next operand isn't a number, then we don't have either
626*5a59a8b3Srsb 		 * the interval nor the count.  Both must be set to the
627*5a59a8b3Srsb 		 * defaults.  In that case, both the current and the previous
628*5a59a8b3Srsb 		 * operands are stat-able entities.
629*5a59a8b3Srsb 		 */
630*5a59a8b3Srsb 		errno = 0;
631*5a59a8b3Srsb 		*count = strtol(argv[optind + 1], &endptr, 10);
632*5a59a8b3Srsb 		if (*endptr && !isdigit((int)*endptr)) {
633*5a59a8b3Srsb 			/*
634*5a59a8b3Srsb 			 * Faked out!  The last operand wasn't a number so
635*5a59a8b3Srsb 			 * the current and previous operands should be
636*5a59a8b3Srsb 			 * stat-able entities. We also need to reset interval.
637*5a59a8b3Srsb 			 */
638*5a59a8b3Srsb 			*interval = 0;
639*5a59a8b3Srsb 			(*entityp)[nentities++].e_name = strdup(argv[optind++]);
640*5a59a8b3Srsb 			(*entityp)[nentities++].e_name = strdup(argv[optind++]);
641*5a59a8b3Srsb 		} else if (out_of_range || errno == ERANGE || *count <= 0) {
642*5a59a8b3Srsb 			(void) fprintf(stderr, gettext(
643*5a59a8b3Srsb 			    "Both interval and count must be between 1 "
644*5a59a8b3Srsb 			    "and %ld (inclusive)\n"), MAXLONG);
645*5a59a8b3Srsb 			return (-1);
646*5a59a8b3Srsb 		}
647*5a59a8b3Srsb 		break;	/* Done! */
648*5a59a8b3Srsb 	}
649*5a59a8b3Srsb 	return (nentities);
650*5a59a8b3Srsb }
651*5a59a8b3Srsb 
652*5a59a8b3Srsb /*
653*5a59a8b3Srsb  * set_mntpt() looks at the entity's name (e_name) and finds its
654*5a59a8b3Srsb  * mountpoint.  To do this, we need to build a list of mountpoints
655*5a59a8b3Srsb  * from /etc/mnttab.  We only need to do this once and we don't do it
656*5a59a8b3Srsb  * if we don't need to look at any mountpoints.
657*5a59a8b3Srsb  * Returns 0 on success, non-zero if it couldn't find a mount-point.
658*5a59a8b3Srsb  */
659*5a59a8b3Srsb int
660*5a59a8b3Srsb set_mntpt(entity_t *ep)
661*5a59a8b3Srsb {
662*5a59a8b3Srsb 	static struct mnt {
663*5a59a8b3Srsb 		struct mnt	*m_next;
664*5a59a8b3Srsb 		char		*m_mntpt;
665*5a59a8b3Srsb 		ulong_t		m_fsid;	/* From statvfs(), set only as needed */
666*5a59a8b3Srsb 	} *mnt_list = NULL;	/* Linked list of mount-points */
667*5a59a8b3Srsb 	struct mnt *mntp;
668*5a59a8b3Srsb 	struct statvfs statvfsbuf;
669*5a59a8b3Srsb 	char *original_name = ep->e_name;
670*5a59a8b3Srsb 	char path[PATH_MAX];
671*5a59a8b3Srsb 
672*5a59a8b3Srsb 	if (original_name == NULL)		/* Shouldn't happen */
673*5a59a8b3Srsb 		return (1);
674*5a59a8b3Srsb 
675*5a59a8b3Srsb 	/* We only set up mnt_list the first time this is called */
676*5a59a8b3Srsb 	if (mnt_list == NULL) {
677*5a59a8b3Srsb 		FILE *fp;
678*5a59a8b3Srsb 		struct mnttab mnttab;
679*5a59a8b3Srsb 
680*5a59a8b3Srsb 		if ((fp = fopen(MNTTAB, "r")) == NULL) {
681*5a59a8b3Srsb 			perror(MNTTAB);
682*5a59a8b3Srsb 			return (1);
683*5a59a8b3Srsb 		}
684*5a59a8b3Srsb 		resetmnttab(fp);
685*5a59a8b3Srsb 		/*
686*5a59a8b3Srsb 		 * We insert at the front of the list so that when we
687*5a59a8b3Srsb 		 * search entries we'll have the last mounted entries
688*5a59a8b3Srsb 		 * first in the list so that we can match the longest
689*5a59a8b3Srsb 		 * mountpoint.
690*5a59a8b3Srsb 		 */
691*5a59a8b3Srsb 		while (getmntent(fp, &mnttab) == 0) {
692*5a59a8b3Srsb 			if ((mntp = malloc(sizeof (*mntp))) == NULL) {
693*5a59a8b3Srsb 				perror(gettext("Can't create mount list"));
694*5a59a8b3Srsb 				return (1);
695*5a59a8b3Srsb 			}
696*5a59a8b3Srsb 			mntp->m_mntpt = strdup(mnttab.mnt_mountp);
697*5a59a8b3Srsb 			mntp->m_next = mnt_list;
698*5a59a8b3Srsb 			mnt_list = mntp;
699*5a59a8b3Srsb 		}
700*5a59a8b3Srsb 		(void) fclose(fp);
701*5a59a8b3Srsb 	}
702*5a59a8b3Srsb 
703*5a59a8b3Srsb 	if (realpath(original_name, path) == NULL) {
704*5a59a8b3Srsb 		perror(original_name);
705*5a59a8b3Srsb 		return (1);
706*5a59a8b3Srsb 	}
707*5a59a8b3Srsb 
708*5a59a8b3Srsb 	/*
709*5a59a8b3Srsb 	 * Now that we have the path, walk through the mnt_list and
710*5a59a8b3Srsb 	 * look for the first (best) match.
711*5a59a8b3Srsb 	 */
712*5a59a8b3Srsb 	for (mntp = mnt_list; mntp; mntp = mntp->m_next) {
713*5a59a8b3Srsb 		if (strncmp(path, mntp->m_mntpt, strlen(mntp->m_mntpt)) == 0) {
714*5a59a8b3Srsb 			if (mntp->m_fsid == 0) {
715*5a59a8b3Srsb 				if (statvfs(mntp->m_mntpt, &statvfsbuf)) {
716*5a59a8b3Srsb 					/* Can't statvfs so no match */
717*5a59a8b3Srsb 					continue;
718*5a59a8b3Srsb 				} else {
719*5a59a8b3Srsb 					mntp->m_fsid = statvfsbuf.f_fsid;
720*5a59a8b3Srsb 				}
721*5a59a8b3Srsb 			}
722*5a59a8b3Srsb 
723*5a59a8b3Srsb 			if (ep->e_fsid != mntp->m_fsid) {
724*5a59a8b3Srsb 				/* No match - Move on */
725*5a59a8b3Srsb 				continue;
726*5a59a8b3Srsb 			}
727*5a59a8b3Srsb 
728*5a59a8b3Srsb 			break;
729*5a59a8b3Srsb 		}
730*5a59a8b3Srsb 	}
731*5a59a8b3Srsb 
732*5a59a8b3Srsb 	if (mntp == NULL) {
733*5a59a8b3Srsb 		(void) fprintf(stderr, gettext(
734*5a59a8b3Srsb 		    "Can't find mount point for %s\n"), path);
735*5a59a8b3Srsb 		return (1);
736*5a59a8b3Srsb 	}
737*5a59a8b3Srsb 
738*5a59a8b3Srsb 	ep->e_name = strdup(mntp->m_mntpt);
739*5a59a8b3Srsb 	free(original_name);
740*5a59a8b3Srsb 	return (0);
741*5a59a8b3Srsb }
742*5a59a8b3Srsb 
743*5a59a8b3Srsb /*
744*5a59a8b3Srsb  * We have an array of entities that are potentially stat-able.  Using
745*5a59a8b3Srsb  * the name (e_name) of the entity, attempt to construct a ksname suitable
746*5a59a8b3Srsb  * for use by kstat_lookup(3kstat) and fill it into the e_ksname member.
747*5a59a8b3Srsb  *
748*5a59a8b3Srsb  * We check the e_name against the list of file system types.  If there is
749*5a59a8b3Srsb  * no match then test to see if the path is valid.  If the path is valid,
750*5a59a8b3Srsb  * then determine the mountpoint.
751*5a59a8b3Srsb  */
752*5a59a8b3Srsb void
753*5a59a8b3Srsb set_ksnames(entity_t *entities, int nentities, char **fstypes, int nfstypes)
754*5a59a8b3Srsb {
755*5a59a8b3Srsb 	int		i, j;
756*5a59a8b3Srsb 	struct statvfs	statvfsbuf;
757*5a59a8b3Srsb 
758*5a59a8b3Srsb 	for (i = 0; i < nentities; i++) {
759*5a59a8b3Srsb 		entity_t	*ep = &entities[i];
760*5a59a8b3Srsb 
761*5a59a8b3Srsb 		/* Check the name against the list of fstypes */
762*5a59a8b3Srsb 		for (j = 1; j < nfstypes; j++) {
763*5a59a8b3Srsb 			if (fstypes[j] && ep->e_name &&
764*5a59a8b3Srsb 			    strcmp(ep->e_name, fstypes[j]) == 0) {
765*5a59a8b3Srsb 				/* It's a file system type */
766*5a59a8b3Srsb 				ep->e_type = ENTYPE_FSTYPE;
767*5a59a8b3Srsb 				(void) snprintf(ep->e_ksname,
768*5a59a8b3Srsb 						KSTAT_STRLEN, "%s%s",
769*5a59a8b3Srsb 						VOPSTATS_STR, ep->e_name);
770*5a59a8b3Srsb 				/* Now allocate the vopstats array */
771*5a59a8b3Srsb 				ep->e_vs = calloc(VS_SIZE, sizeof (vopstats_t));
772*5a59a8b3Srsb 				if (entities[i].e_vs == NULL) {
773*5a59a8b3Srsb 					perror(gettext("calloc() vopstats"));
774*5a59a8b3Srsb 					exit(1);
775*5a59a8b3Srsb 				}
776*5a59a8b3Srsb 				break;
777*5a59a8b3Srsb 			}
778*5a59a8b3Srsb 		}
779*5a59a8b3Srsb 		if (j < nfstypes)	/* Found it! */
780*5a59a8b3Srsb 			continue;
781*5a59a8b3Srsb 
782*5a59a8b3Srsb 		/*
783*5a59a8b3Srsb 		 * If the entity in the exception list of fstypes, then
784*5a59a8b3Srsb 		 * null out the entry so it isn't displayed and move along.
785*5a59a8b3Srsb 		 */
786*5a59a8b3Srsb 		if (is_exception(ep->e_name)) {
787*5a59a8b3Srsb 			ep->e_ksname[0] = 0;
788*5a59a8b3Srsb 			continue;
789*5a59a8b3Srsb 		}
790*5a59a8b3Srsb 
791*5a59a8b3Srsb 		/* If we didn't find it, see if it's a path */
792*5a59a8b3Srsb 		if (ep->e_name == NULL || statvfs(ep->e_name, &statvfsbuf)) {
793*5a59a8b3Srsb 			/* Error - Make sure the entry is nulled out */
794*5a59a8b3Srsb 			ep->e_ksname[0] = 0;
795*5a59a8b3Srsb 			continue;
796*5a59a8b3Srsb 		}
797*5a59a8b3Srsb 		(void) snprintf(ep->e_ksname, KSTAT_STRLEN, "%s%lx",
798*5a59a8b3Srsb 		    VOPSTATS_STR, statvfsbuf.f_fsid);
799*5a59a8b3Srsb 		ep->e_fsid = statvfsbuf.f_fsid;
800*5a59a8b3Srsb 		if (set_mntpt(ep)) {
801*5a59a8b3Srsb 			(void) fprintf(stderr,
802*5a59a8b3Srsb 			    gettext("Can't determine type of \"%s\"\n"),
803*5a59a8b3Srsb 			    ep->e_name ? ep->e_name : gettext("<NULL>"));
804*5a59a8b3Srsb 		} else {
805*5a59a8b3Srsb 			ep->e_type = ENTYPE_MNTPT;
806*5a59a8b3Srsb 		}
807*5a59a8b3Srsb 
808*5a59a8b3Srsb 		/* Now allocate the vopstats array */
809*5a59a8b3Srsb 		ep->e_vs = calloc(VS_SIZE, sizeof (vopstats_t));
810*5a59a8b3Srsb 		if (entities[i].e_vs == NULL) {
811*5a59a8b3Srsb 			perror(gettext("Can't calloc vopstats"));
812*5a59a8b3Srsb 			exit(1);
813*5a59a8b3Srsb 		}
814*5a59a8b3Srsb 	}
815*5a59a8b3Srsb }
816*5a59a8b3Srsb 
817*5a59a8b3Srsb void
818*5a59a8b3Srsb print_time(int type)
819*5a59a8b3Srsb {
820*5a59a8b3Srsb 	time_t	t;
821*5a59a8b3Srsb 
822*5a59a8b3Srsb 	if (time(&t) != -1) {
823*5a59a8b3Srsb 		if (type == UDATE) {
824*5a59a8b3Srsb 			(void) printf("%ld\n", t);
825*5a59a8b3Srsb 		} else if (type == DDATE) {
826*5a59a8b3Srsb 			char	*dstr;
827*5a59a8b3Srsb 
828*5a59a8b3Srsb 			dstr = ctime(&t);
829*5a59a8b3Srsb 			if (dstr) {
830*5a59a8b3Srsb 				(void) printf("%s", dstr);
831*5a59a8b3Srsb 			}
832*5a59a8b3Srsb 		}
833*5a59a8b3Srsb 	}
834*5a59a8b3Srsb }
835*5a59a8b3Srsb 
836*5a59a8b3Srsb /*
837*5a59a8b3Srsb  * The idea is that 'dspfunc' should only be modified from the default
838*5a59a8b3Srsb  * once since the display options are mutually exclusive.  If 'dspfunc'
839*5a59a8b3Srsb  * only contains the default display function, then all is good and we
840*5a59a8b3Srsb  * can set it to the new display function.  Otherwise, bail.
841*5a59a8b3Srsb  */
842*5a59a8b3Srsb void
843*5a59a8b3Srsb set_dispfunc(
844*5a59a8b3Srsb 	void (**dspfunc)(char *, vopstats_t *, vopstats_t *, int),
845*5a59a8b3Srsb 	void (*newfunc)(char *, vopstats_t *, vopstats_t *, int))
846*5a59a8b3Srsb {
847*5a59a8b3Srsb 	if (*dspfunc != dflt_display) {
848*5a59a8b3Srsb 		(void) fprintf(stderr, gettext(
849*5a59a8b3Srsb 		"%s: Display options -{a|f|i|n|v} are mutually exclusive\n"),
850*5a59a8b3Srsb 		    cmdname);
851*5a59a8b3Srsb 		usage();
852*5a59a8b3Srsb 	}
853*5a59a8b3Srsb 	*dspfunc = newfunc;
854*5a59a8b3Srsb }
855*5a59a8b3Srsb 
856*5a59a8b3Srsb int
857*5a59a8b3Srsb main(int argc, char *argv[])
858*5a59a8b3Srsb {
859*5a59a8b3Srsb 	int		c;
860*5a59a8b3Srsb 	int		i, j;		/* Generic counters */
861*5a59a8b3Srsb 	int		nentities_found;
862*5a59a8b3Srsb 	int		linesout;	/* Keeps track of lines printed */
863*5a59a8b3Srsb 	int		printhdr = 0;	/* Print a header?  0 = no, 1 = yes */
864*5a59a8b3Srsb 	int		nfstypes;	/* Number of fstypes */
865*5a59a8b3Srsb 	int		dispflag = 0;	/* Flags for display control */
866*5a59a8b3Srsb 	int		timestamp = NODATE;	/* Default: no time stamp */
867*5a59a8b3Srsb 	long		count = 0;	/* Number of iterations for display */
868*5a59a8b3Srsb 	long		interval = 0;
869*5a59a8b3Srsb 	boolean_t	fstypes_only = B_FALSE;	/* Display fstypes only */
870*5a59a8b3Srsb 	char		**fstypes;	/* Array of names of all fstypes */
871*5a59a8b3Srsb 	int		nentities;	/* Number of stat-able entities */
872*5a59a8b3Srsb 	entity_t	*entities;	/* Array of stat-able entities */
873*5a59a8b3Srsb 	kstat_ctl_t	*kc;
874*5a59a8b3Srsb 	void (*dfunc)(char *, vopstats_t *, vopstats_t *, int) = dflt_display;
875*5a59a8b3Srsb 
876*5a59a8b3Srsb 	extern int	optind;
877*5a59a8b3Srsb 
878*5a59a8b3Srsb 	cmdname = argv[0];
879*5a59a8b3Srsb 	while ((c = getopt(argc, argv, OPTIONS)) != EOF) {
880*5a59a8b3Srsb 		switch (c) {
881*5a59a8b3Srsb 
882*5a59a8b3Srsb 		default:
883*5a59a8b3Srsb 			usage();
884*5a59a8b3Srsb 			break;
885*5a59a8b3Srsb 
886*5a59a8b3Srsb 		case 'P':	/* Parsable output */
887*5a59a8b3Srsb 			dispflag |= DISP_RAW;
888*5a59a8b3Srsb 			break;
889*5a59a8b3Srsb 
890*5a59a8b3Srsb 		case 'T':	/* Timestamp */
891*5a59a8b3Srsb 			if (optarg) {
892*5a59a8b3Srsb 				if (strcmp(optarg, "u") == 0) {
893*5a59a8b3Srsb 					timestamp = UDATE;
894*5a59a8b3Srsb 				} else if (strcmp(optarg, "d") == 0) {
895*5a59a8b3Srsb 					timestamp = DDATE;
896*5a59a8b3Srsb 				}
897*5a59a8b3Srsb 			}
898*5a59a8b3Srsb 
899*5a59a8b3Srsb 			/* If it was never set properly... */
900*5a59a8b3Srsb 			if (timestamp == NODATE) {
901*5a59a8b3Srsb 				(void) fprintf(stderr, gettext(
902*5a59a8b3Srsb 				"%s: -T option requires either 'u' or 'd'\n"),
903*5a59a8b3Srsb 					cmdname);
904*5a59a8b3Srsb 				usage();
905*5a59a8b3Srsb 			}
906*5a59a8b3Srsb 			break;
907*5a59a8b3Srsb 
908*5a59a8b3Srsb 		case 'a':
909*5a59a8b3Srsb 			set_dispfunc(&dfunc, attr_display);
910*5a59a8b3Srsb 			break;
911*5a59a8b3Srsb 
912*5a59a8b3Srsb 		case 'f':
913*5a59a8b3Srsb 			set_dispfunc(&dfunc, vop_display);
914*5a59a8b3Srsb 			break;
915*5a59a8b3Srsb 
916*5a59a8b3Srsb 		case 'i':
917*5a59a8b3Srsb 			set_dispfunc(&dfunc, io_display);
918*5a59a8b3Srsb 			break;
919*5a59a8b3Srsb 
920*5a59a8b3Srsb 		case 'n':
921*5a59a8b3Srsb 			set_dispfunc(&dfunc, naming_display);
922*5a59a8b3Srsb 			break;
923*5a59a8b3Srsb 
924*5a59a8b3Srsb 		case 'v':
925*5a59a8b3Srsb 			set_dispfunc(&dfunc, vm_display);
926*5a59a8b3Srsb 			break;
927*5a59a8b3Srsb 		}
928*5a59a8b3Srsb 	}
929*5a59a8b3Srsb 
930*5a59a8b3Srsb 	if ((dispflag & DISP_RAW) && (timestamp != NODATE)) {
931*5a59a8b3Srsb 		(void) fprintf(stderr, gettext(
932*5a59a8b3Srsb 			"-P and -T options are mutually exclusive\n"));
933*5a59a8b3Srsb 		usage();
934*5a59a8b3Srsb 	}
935*5a59a8b3Srsb 
936*5a59a8b3Srsb 	/* Gather the list of filesystem types */
937*5a59a8b3Srsb 	if ((nfstypes = build_fstype_list(&fstypes)) == 0) {
938*5a59a8b3Srsb 		(void) fprintf(stderr,
939*5a59a8b3Srsb 		    gettext("Can't build list of fstypes\n"));
940*5a59a8b3Srsb 		exit(1);
941*5a59a8b3Srsb 	}
942*5a59a8b3Srsb 
943*5a59a8b3Srsb 	nentities = parse_operands(
944*5a59a8b3Srsb 	    argc, argv, optind, &interval, &count, &entities);
945*5a59a8b3Srsb 
946*5a59a8b3Srsb 	if (nentities == -1)	/* Set of operands didn't parse properly  */
947*5a59a8b3Srsb 		usage();
948*5a59a8b3Srsb 
949*5a59a8b3Srsb 	/*
950*5a59a8b3Srsb 	 * If we had no operands (except for interval/count) then we
951*5a59a8b3Srsb 	 * fill in the entities[] array with all the fstypes.
952*5a59a8b3Srsb 	 */
953*5a59a8b3Srsb 	if (nentities == 0) {
954*5a59a8b3Srsb 		fstypes_only = B_TRUE;
955*5a59a8b3Srsb 
956*5a59a8b3Srsb 		if ((entities = calloc(nfstypes, sizeof (entity_t))) == NULL) {
957*5a59a8b3Srsb 			(void) fprintf(stderr,
958*5a59a8b3Srsb 			    gettext("Can't calloc fstype stats\n"));
959*5a59a8b3Srsb 			exit(1);
960*5a59a8b3Srsb 		}
961*5a59a8b3Srsb 
962*5a59a8b3Srsb 		for (i = 1; i < nfstypes; i++) {
963*5a59a8b3Srsb 			if (fstypes[i]) {
964*5a59a8b3Srsb 				entities[nentities].e_name = strdup(fstypes[i]);
965*5a59a8b3Srsb 				nentities++;
966*5a59a8b3Srsb 			}
967*5a59a8b3Srsb 		}
968*5a59a8b3Srsb 	}
969*5a59a8b3Srsb 
970*5a59a8b3Srsb 	set_ksnames(entities, nentities, fstypes, nfstypes);
971*5a59a8b3Srsb 
972*5a59a8b3Srsb 	if ((kc = kstat_open()) == NULL) {
973*5a59a8b3Srsb 		perror(gettext("kstat_open"));
974*5a59a8b3Srsb 		exit(1);
975*5a59a8b3Srsb 	}
976*5a59a8b3Srsb 
977*5a59a8b3Srsb 	/*
978*5a59a8b3Srsb 	 * The following loop walks through the entities[] list to "prime
979*5a59a8b3Srsb 	 * the pump"
980*5a59a8b3Srsb 	 */
981*5a59a8b3Srsb 	for (j = 0, linesout = 0; j < nentities; j++) {
982*5a59a8b3Srsb 		entity_t *ent = &entities[j];
983*5a59a8b3Srsb 		vopstats_t *vsp = &ent->e_vs[CUR_INDEX];
984*5a59a8b3Srsb 		kstat_t *ksp = NULL;
985*5a59a8b3Srsb 
986*5a59a8b3Srsb 		if (get_vopstats(kc, ent->e_ksname, vsp, &ksp) == 0) {
987*5a59a8b3Srsb 			(*dfunc)(ent->e_name, NULL, vsp,
988*5a59a8b3Srsb 			    dispflag_policy(linesout == 0, dispflag));
989*5a59a8b3Srsb 			linesout++;
990*5a59a8b3Srsb 		} else {
991*5a59a8b3Srsb 			/*
992*5a59a8b3Srsb 			 * If we can't find it the first time through, then
993*5a59a8b3Srsb 			 * get rid of it.
994*5a59a8b3Srsb 			 */
995*5a59a8b3Srsb 			entities[j].e_ksname[0] = 0;
996*5a59a8b3Srsb 
997*5a59a8b3Srsb 			/*
998*5a59a8b3Srsb 			 * If we're only displaying the fstypes (default
999*5a59a8b3Srsb 			 * with no other entities requested) then don't
1000*5a59a8b3Srsb 			 * complain about any file systems that might not
1001*5a59a8b3Srsb 			 * be loaded.  Otherwise, let the user know that
1002*5a59a8b3Srsb 			 * he chose poorly.
1003*5a59a8b3Srsb 			 */
1004*5a59a8b3Srsb 			if (fstypes_only == B_FALSE) {
1005*5a59a8b3Srsb 				(void) fprintf(stderr, gettext(
1006*5a59a8b3Srsb 				    "No statistics available for %s\n"),
1007*5a59a8b3Srsb 				    entities[j].e_name);
1008*5a59a8b3Srsb 			}
1009*5a59a8b3Srsb 		}
1010*5a59a8b3Srsb 	}
1011*5a59a8b3Srsb 
1012*5a59a8b3Srsb 	BUMP_INDEX();	/* Swap the previous/current indices */
1013*5a59a8b3Srsb 	for (i = 1; i <= count; i++) {
1014*5a59a8b3Srsb 		/*
1015*5a59a8b3Srsb 		 * No telling how many lines will be printed in any interval.
1016*5a59a8b3Srsb 		 * There should be a minimum of HEADERLINES between any
1017*5a59a8b3Srsb 		 * header.  If we exceed that, no big deal.
1018*5a59a8b3Srsb 		 */
1019*5a59a8b3Srsb 		if (linesout > HEADERLINES) {
1020*5a59a8b3Srsb 			linesout = 0;
1021*5a59a8b3Srsb 			printhdr = 1;
1022*5a59a8b3Srsb 		}
1023*5a59a8b3Srsb 		(void) poll(NULL, 0, interval*1000);
1024*5a59a8b3Srsb 
1025*5a59a8b3Srsb 		if (timestamp) {
1026*5a59a8b3Srsb 			print_time(timestamp);
1027*5a59a8b3Srsb 			linesout++;
1028*5a59a8b3Srsb 		}
1029*5a59a8b3Srsb 
1030*5a59a8b3Srsb 		for (j = 0, nentities_found = 0; j < nentities; j++) {
1031*5a59a8b3Srsb 			entity_t *ent = &entities[j];
1032*5a59a8b3Srsb 
1033*5a59a8b3Srsb 			/*
1034*5a59a8b3Srsb 			 * If this entry has been cleared, don't attempt
1035*5a59a8b3Srsb 			 * to process it.
1036*5a59a8b3Srsb 			 */
1037*5a59a8b3Srsb 			if (ent->e_ksname[0] == 0) {
1038*5a59a8b3Srsb 				continue;
1039*5a59a8b3Srsb 			}
1040*5a59a8b3Srsb 
1041*5a59a8b3Srsb 			if (get_vopstats(kc, ent->e_ksname,
1042*5a59a8b3Srsb 			    &ent->e_vs[CUR_INDEX], NULL) == 0) {
1043*5a59a8b3Srsb 				(*dfunc)(ent->e_name, &ent->e_vs[PREV_INDEX],
1044*5a59a8b3Srsb 				    &ent->e_vs[CUR_INDEX],
1045*5a59a8b3Srsb 				    dispflag_policy(printhdr, dispflag));
1046*5a59a8b3Srsb 				linesout++;
1047*5a59a8b3Srsb 				nentities_found++;
1048*5a59a8b3Srsb 			} else {
1049*5a59a8b3Srsb 				if (ent->e_type == ENTYPE_MNTPT) {
1050*5a59a8b3Srsb 					(void) printf(gettext(
1051*5a59a8b3Srsb 					    "<<mount point no longer "
1052*5a59a8b3Srsb 					    "available: %s>>\n"), ent->e_name);
1053*5a59a8b3Srsb 				} else if (ent->e_type == ENTYPE_FSTYPE) {
1054*5a59a8b3Srsb 					(void) printf(gettext(
1055*5a59a8b3Srsb 					    "<<file system module no longer "
1056*5a59a8b3Srsb 					    "loaded: %s>>\n"), ent->e_name);
1057*5a59a8b3Srsb 				} else {
1058*5a59a8b3Srsb 					(void) printf(gettext(
1059*5a59a8b3Srsb 					    "<<%s no longer available>>\n"),
1060*5a59a8b3Srsb 					    ent->e_name);
1061*5a59a8b3Srsb 				}
1062*5a59a8b3Srsb 				/* Disable this so it doesn't print again */
1063*5a59a8b3Srsb 				ent->e_ksname[0] = 0;
1064*5a59a8b3Srsb 			}
1065*5a59a8b3Srsb 			printhdr = 0;	/* Always shut this off */
1066*5a59a8b3Srsb 		}
1067*5a59a8b3Srsb 		BUMP_INDEX();	/* Bump the previous/current indices */
1068*5a59a8b3Srsb 
1069*5a59a8b3Srsb 		/*
1070*5a59a8b3Srsb 		 * If the entities we were observing are no longer there
1071*5a59a8b3Srsb 		 * (file system modules unloaded, file systems unmounted)
1072*5a59a8b3Srsb 		 * then we're done.
1073*5a59a8b3Srsb 		 */
1074*5a59a8b3Srsb 		if (nentities_found == 0)
1075*5a59a8b3Srsb 			break;
1076*5a59a8b3Srsb 	}
1077*5a59a8b3Srsb 
1078*5a59a8b3Srsb 	return (0);
1079*5a59a8b3Srsb }
1080