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 /* 225a59a8b3Srsb * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 235a59a8b3Srsb * Use is subject to license terms. 245a59a8b3Srsb */ 255a59a8b3Srsb 265a59a8b3Srsb #pragma ident "%Z%%M% %I% %E% SMI" 275a59a8b3Srsb 285a59a8b3Srsb #include <stdio.h> 295a59a8b3Srsb #include <kstat.h> 305a59a8b3Srsb #include <stdlib.h> 315a59a8b3Srsb #include <string.h> 325a59a8b3Srsb #include <strings.h> 335a59a8b3Srsb #include <errno.h> 345a59a8b3Srsb #include <limits.h> 355a59a8b3Srsb #include <sys/types.h> 365a59a8b3Srsb #include <time.h> 37*13c7b6acSrsb #include <langinfo.h> 385a59a8b3Srsb #include <sys/time.h> 395a59a8b3Srsb #include <sys/uio.h> 405a59a8b3Srsb #include <sys/vnode.h> 415a59a8b3Srsb #include <sys/vfs.h> 425a59a8b3Srsb #include <sys/statvfs.h> 435a59a8b3Srsb #include <sys/fstyp.h> 445a59a8b3Srsb #include <sys/fsid.h> 455a59a8b3Srsb #include <sys/mnttab.h> 465a59a8b3Srsb #include <values.h> 475a59a8b3Srsb #include <poll.h> 485a59a8b3Srsb #include <ctype.h> 495a59a8b3Srsb #include <libintl.h> 50*13c7b6acSrsb #include <locale.h> 515a59a8b3Srsb 523a401a6bSrsb /* 533a401a6bSrsb * For now, parsable output is turned off. Once we gather feedback and 543a401a6bSrsb * stablize the output format, we'll turn it back on. This prevents 553a401a6bSrsb * the situation where users build tools which depend on a specific 563a401a6bSrsb * format before we declare the output stable. 573a401a6bSrsb */ 583a401a6bSrsb #define PARSABLE_OUTPUT 0 593a401a6bSrsb 603a401a6bSrsb #if PARSABLE_OUTPUT 613a401a6bSrsb #define OPTIONS "FPT:afginv" 623a401a6bSrsb #else 633a401a6bSrsb #define OPTIONS "FT:afginv" 643a401a6bSrsb #endif 655a59a8b3Srsb 665a59a8b3Srsb /* Time stamp values */ 675a59a8b3Srsb #define NODATE 0 /* Default: No time stamp */ 685a59a8b3Srsb #define DDATE 1 /* Standard date format */ 695a59a8b3Srsb #define UDATE 2 /* Internal representation of Unix time */ 705a59a8b3Srsb 715a59a8b3Srsb #define RETRY_DELAY 250 /* Timeout for poll() */ 723a401a6bSrsb #define HEADERLINES 12 /* Number of lines between display headers */ 735a59a8b3Srsb 745a59a8b3Srsb #define LBUFSZ 64 /* Generic size for local buffer */ 755a59a8b3Srsb 765a59a8b3Srsb /* 775a59a8b3Srsb * The following are used for the nicenum() function 785a59a8b3Srsb */ 795a59a8b3Srsb #define KILO_VAL 1024 805a59a8b3Srsb #define ONE_INDEX 3 815a59a8b3Srsb 825a59a8b3Srsb #define NENTITY_INIT 1 /* Initial number of entities to allocate */ 835a59a8b3Srsb 845a59a8b3Srsb /* 855a59a8b3Srsb * We need to have a mechanism for an old/previous and new/current vopstat 865a59a8b3Srsb * structure. We only need two per entity and we can swap between them. 875a59a8b3Srsb */ 885a59a8b3Srsb #define VS_SIZE 2 /* Size of vopstat array */ 895a59a8b3Srsb #define CUR_INDEX (vs_i) 905a59a8b3Srsb #define PREV_INDEX ((vs_i == 0) ? 1 : 0) /* Opposite of CUR_INDEX */ 915a59a8b3Srsb #define BUMP_INDEX() vs_i = ((vs_i == 0) ? 1 : 0) 925a59a8b3Srsb 935a59a8b3Srsb /* 945a59a8b3Srsb * An "entity" is anything we're collecting statistics on, it could 955a59a8b3Srsb * be a mountpoint or an FS-type. 965a59a8b3Srsb * e_name is the name of the entity (e.g. mount point or FS-type) 975a59a8b3Srsb * e_ksname is the name of the associated kstat 985a59a8b3Srsb * e_vs is an array of vopstats. This is used to keep track of "previous" 995a59a8b3Srsb * and "current" vopstats. 1005a59a8b3Srsb */ 1015a59a8b3Srsb typedef struct entity { 1025a59a8b3Srsb char *e_name; /* name of entity */ 1035a59a8b3Srsb vopstats_t *e_vs; /* Array of vopstats */ 1045a59a8b3Srsb ulong_t e_fsid; /* fsid for ENTYPE_MNTPT only */ 1055a59a8b3Srsb int e_type; /* type of entity */ 1065a59a8b3Srsb char e_ksname[KSTAT_STRLEN]; /* kstat name */ 1075a59a8b3Srsb } entity_t; 1085a59a8b3Srsb 1095a59a8b3Srsb /* Types of entities (e_type) */ 1105a59a8b3Srsb #define ENTYPE_UNKNOWN 0 /* UNKNOWN must be zero since we calloc() */ 1115a59a8b3Srsb #define ENTYPE_FSTYPE 1 1125a59a8b3Srsb #define ENTYPE_MNTPT 2 1135a59a8b3Srsb 1145a59a8b3Srsb /* If more sub-one units are added, make sure to adjust ONE_INDEX above */ 1155a59a8b3Srsb static char units[] = "num KMGTPE"; 1165a59a8b3Srsb 1175a59a8b3Srsb static char *cmdname; /* name of this command */ 1185a59a8b3Srsb 1195a59a8b3Srsb static int vs_i = 0; /* Index of current vs[] slot */ 1205a59a8b3Srsb 1215a59a8b3Srsb static void 1225a59a8b3Srsb usage() 1235a59a8b3Srsb { 1243a401a6bSrsb (void) fprintf(stderr, gettext( 1253a401a6bSrsb "Usage: %s [-a|f|i|n|v] [-T d|u] {-F | {fstype | fspath}...} " 1265a59a8b3Srsb "[interval [count]]\n"), cmdname); 1275a59a8b3Srsb exit(2); 1285a59a8b3Srsb } 1295a59a8b3Srsb 1305a59a8b3Srsb /* 1315a59a8b3Srsb * Given a 64-bit number and a starting unit (e.g., n - nanoseconds), 1325a59a8b3Srsb * convert the number to a 5-character representation including any 1335a59a8b3Srsb * decimal point and single-character unit. Put that representation 1345a59a8b3Srsb * into the array "buf" (which had better be big enough). 1355a59a8b3Srsb */ 1365a59a8b3Srsb char * 1375a59a8b3Srsb nicenum(uint64_t num, char unit, char *buf) 1385a59a8b3Srsb { 1395a59a8b3Srsb uint64_t n = num; 1405a59a8b3Srsb int unit_index; 1415a59a8b3Srsb int index; 1425a59a8b3Srsb char u; 1435a59a8b3Srsb 1445a59a8b3Srsb /* If the user passed in a NUL/zero unit, use the blank value for 1 */ 1455a59a8b3Srsb if (unit == '\0') 1465a59a8b3Srsb unit = ' '; 1475a59a8b3Srsb 1485a59a8b3Srsb unit_index = 0; 1495a59a8b3Srsb while (units[unit_index] != unit) { 1505a59a8b3Srsb unit_index++; 1515a59a8b3Srsb if (unit_index > sizeof (units) - 1) { 1525a59a8b3Srsb (void) sprintf(buf, "??"); 1535a59a8b3Srsb return (buf); 1545a59a8b3Srsb } 1555a59a8b3Srsb } 1565a59a8b3Srsb 1575a59a8b3Srsb index = 0; 1585a59a8b3Srsb while (n >= KILO_VAL) { 1595a59a8b3Srsb n = (n + (KILO_VAL / 2)) / KILO_VAL; /* Round up or down */ 1605a59a8b3Srsb index++; 1615a59a8b3Srsb unit_index++; 1625a59a8b3Srsb } 1635a59a8b3Srsb 1645a59a8b3Srsb if (unit_index >= sizeof (units) - 1) { 1655a59a8b3Srsb (void) sprintf(buf, "??"); 1665a59a8b3Srsb return (buf); 1675a59a8b3Srsb } 1685a59a8b3Srsb 1695a59a8b3Srsb u = units[unit_index]; 1705a59a8b3Srsb 1715a59a8b3Srsb if (unit_index == ONE_INDEX) { 1725a59a8b3Srsb (void) sprintf(buf, "%llu", (u_longlong_t)n); 1735a59a8b3Srsb } else if (n < 10 && (num & (num - 1)) != 0) { 1745a59a8b3Srsb (void) sprintf(buf, "%.2f%c", 1755a59a8b3Srsb (double)num / (1ULL << 10 * index), u); 1765a59a8b3Srsb } else if (n < 100 && (num & (num - 1)) != 0) { 1775a59a8b3Srsb (void) sprintf(buf, "%.1f%c", 1785a59a8b3Srsb (double)num / (1ULL << 10 * index), u); 1795a59a8b3Srsb } else { 1805a59a8b3Srsb (void) sprintf(buf, "%llu%c", (u_longlong_t)n, u); 1815a59a8b3Srsb } 1825a59a8b3Srsb 1835a59a8b3Srsb return (buf); 1845a59a8b3Srsb } 1855a59a8b3Srsb 1865a59a8b3Srsb 1875a59a8b3Srsb #define RAWVAL(ptr, member) ((ptr)->member.value.ui64) 1885a59a8b3Srsb #define DELTA(member) \ 1895a59a8b3Srsb (newvsp->member.value.ui64 - (oldvsp ? oldvsp->member.value.ui64 : 0)) 1903a401a6bSrsb 1915a59a8b3Srsb #define PRINTSTAT(isnice, nicestring, rawstring, rawval, unit, buf) \ 1925a59a8b3Srsb (isnice) ? \ 1935a59a8b3Srsb (void) printf((nicestring), nicenum(rawval, unit, buf)) \ 1945a59a8b3Srsb : \ 1955a59a8b3Srsb (void) printf((rawstring), (rawval)) 1965a59a8b3Srsb 1975a59a8b3Srsb /* Values for display flag */ 1985a59a8b3Srsb #define DISP_HEADER 0x1 1995a59a8b3Srsb #define DISP_RAW 0x2 2005a59a8b3Srsb 2015a59a8b3Srsb /* 2025a59a8b3Srsb * The policy for dealing with multiple flags is dealt with here. 2035a59a8b3Srsb * Currently, if we are displaying raw output, then don't allow 2045a59a8b3Srsb * headers to be printed. 2055a59a8b3Srsb */ 2065a59a8b3Srsb int 2075a59a8b3Srsb dispflag_policy(int printhdr, int dispflag) 2085a59a8b3Srsb { 2095a59a8b3Srsb /* If we're not displaying raw output, then allow headers to print */ 2105a59a8b3Srsb if ((dispflag & DISP_RAW) == 0) { 2115a59a8b3Srsb if (printhdr) { 2125a59a8b3Srsb dispflag |= DISP_HEADER; 2135a59a8b3Srsb } 2145a59a8b3Srsb } 2155a59a8b3Srsb 2165a59a8b3Srsb return (dispflag); 2175a59a8b3Srsb } 2185a59a8b3Srsb 2195a59a8b3Srsb static void 2205a59a8b3Srsb dflt_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 2215a59a8b3Srsb { 2225a59a8b3Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 2235a59a8b3Srsb longlong_t nnewfile; 2245a59a8b3Srsb longlong_t nnamerm; 2255a59a8b3Srsb longlong_t nnamechg; 2265a59a8b3Srsb longlong_t nattrret; 2275a59a8b3Srsb longlong_t nattrchg; 2285a59a8b3Srsb longlong_t nlookup; 2295a59a8b3Srsb longlong_t nreaddir; 2305a59a8b3Srsb longlong_t ndataread; 2315a59a8b3Srsb longlong_t ndatawrite; 2325a59a8b3Srsb longlong_t readthruput; 2335a59a8b3Srsb longlong_t writethruput; 2345a59a8b3Srsb char buf[LBUFSZ]; 2355a59a8b3Srsb 2365a59a8b3Srsb nnewfile = DELTA(ncreate) + DELTA(nmkdir) + DELTA(nsymlink); 2375a59a8b3Srsb nnamerm = DELTA(nremove) + DELTA(nrmdir); 2385a59a8b3Srsb nnamechg = DELTA(nrename) + DELTA(nlink) + DELTA(nsymlink); 2395a59a8b3Srsb nattrret = DELTA(ngetattr) + DELTA(naccess) + 2405a59a8b3Srsb DELTA(ngetsecattr) + DELTA(nfid); 2415a59a8b3Srsb nattrchg = DELTA(nsetattr) + DELTA(nsetsecattr) + DELTA(nspace); 2425a59a8b3Srsb nlookup = DELTA(nlookup); 2435a59a8b3Srsb nreaddir = DELTA(nreaddir); 2445a59a8b3Srsb ndataread = DELTA(nread); 2455a59a8b3Srsb ndatawrite = DELTA(nwrite); 2465a59a8b3Srsb readthruput = DELTA(read_bytes); 2475a59a8b3Srsb writethruput = DELTA(write_bytes); 2485a59a8b3Srsb 2495a59a8b3Srsb if (dispflag & DISP_HEADER) { 2505a59a8b3Srsb (void) printf(gettext( 2515a59a8b3Srsb " new name name attr attr lookup rddir read read write write\n" 2525a59a8b3Srsb " file remov chng get set ops ops ops bytes ops bytes\n")); 2535a59a8b3Srsb } 2545a59a8b3Srsb 2555a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnewfile, ' ', buf); 2565a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnamerm, ' ', buf); 2575a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nnamechg, ' ', buf); 2585a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nattrret, ' ', buf); 2595a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nattrchg, ' ', buf); 2605a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", nlookup, ' ', buf); 2615a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", nreaddir, ' ', buf); 2625a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", ndataread, ' ', buf); 2635a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", readthruput, ' ', buf); 2645a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", ndatawrite, ' ', buf); 2655a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", writethruput, ' ', buf); 2665a59a8b3Srsb (void) printf("%s\n", name); 2675a59a8b3Srsb } 2685a59a8b3Srsb 2695a59a8b3Srsb static void 2705a59a8b3Srsb io_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 2715a59a8b3Srsb { 2725a59a8b3Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 2735a59a8b3Srsb char buf[LBUFSZ]; 2745a59a8b3Srsb 2755a59a8b3Srsb if (dispflag & DISP_HEADER) { 2765a59a8b3Srsb (void) printf(gettext( 2775a59a8b3Srsb " read read write write rddir rddir rwlock rwulock\n" 2785a59a8b3Srsb " ops bytes ops bytes ops bytes ops ops\n")); 2795a59a8b3Srsb } 2805a59a8b3Srsb 2815a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nread), ' ', buf); 2825a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(read_bytes), ' ', buf); 2835a59a8b3Srsb 2845a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nwrite), ' ', buf); 2855a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(write_bytes), ' ', buf); 2865a59a8b3Srsb 2875a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf); 2885a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(readdir_bytes), ' ', buf); 2895a59a8b3Srsb 2905a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nrwlock), ' ', buf); 2915a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrwunlock), ' ', buf); 2925a59a8b3Srsb 2935a59a8b3Srsb (void) printf("%s\n", name); 2945a59a8b3Srsb } 2955a59a8b3Srsb 2965a59a8b3Srsb static void 2975a59a8b3Srsb vm_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 2985a59a8b3Srsb { 2995a59a8b3Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 3005a59a8b3Srsb char buf[LBUFSZ]; 3015a59a8b3Srsb 3025a59a8b3Srsb if (dispflag & DISP_HEADER) { 3035a59a8b3Srsb (void) printf( 3045a59a8b3Srsb gettext(" map addmap delmap getpag putpag pagio\n")); 3055a59a8b3Srsb } 3065a59a8b3Srsb 3075a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmap), ' ', buf); 3085a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(naddmap), ' ', buf); 3095a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ndelmap), ' ', buf); 3105a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetpage), ' ', buf); 3115a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nputpage), ' ', buf); 3125a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(npageio), ' ', buf); 3135a59a8b3Srsb (void) printf("%s\n", name); 3145a59a8b3Srsb } 3155a59a8b3Srsb 3165a59a8b3Srsb static void 3175a59a8b3Srsb attr_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 3185a59a8b3Srsb { 3195a59a8b3Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 3205a59a8b3Srsb char buf[LBUFSZ]; 3215a59a8b3Srsb 3225a59a8b3Srsb if (dispflag & DISP_HEADER) { 3235a59a8b3Srsb (void) printf(gettext("getattr setattr getsec setsec\n")); 3245a59a8b3Srsb } 3255a59a8b3Srsb 3265a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetattr), ' ', buf); 3275a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetattr), ' ', buf); 3285a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetsecattr), ' ', buf); 3295a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetsecattr), ' ', buf); 3305a59a8b3Srsb 3315a59a8b3Srsb (void) printf("%s\n", name); 3325a59a8b3Srsb } 3335a59a8b3Srsb 3345a59a8b3Srsb static void 3355a59a8b3Srsb naming_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 3365a59a8b3Srsb { 3375a59a8b3Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 3385a59a8b3Srsb char buf[LBUFSZ]; 3395a59a8b3Srsb 3405a59a8b3Srsb if (dispflag & DISP_HEADER) { 3415a59a8b3Srsb (void) printf(gettext( 3425a59a8b3Srsb "lookup creat remov link renam mkdir rmdir rddir symlnk rdlnk\n")); 3435a59a8b3Srsb } 3445a59a8b3Srsb 3455a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlookup), ' ', buf); 3465a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(ncreate), ' ', buf); 3475a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nremove), ' ', buf); 3485a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlink), ' ', buf); 3495a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrename), ' ', buf); 3505a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmkdir), ' ', buf); 3515a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrmdir), ' ', buf); 3525a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf); 3535a59a8b3Srsb PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsymlink), ' ', buf); 3545a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreadlink), ' ', buf); 3555a59a8b3Srsb (void) printf("%s\n", name); 3565a59a8b3Srsb } 3575a59a8b3Srsb 3585a59a8b3Srsb 3595a59a8b3Srsb #define PRINT_VOPSTAT_CMN(niceflag, vop) \ 3605a59a8b3Srsb if (niceflag) \ 3615a59a8b3Srsb (void) printf("%10s ", #vop); \ 3625a59a8b3Srsb PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(n##vop), ' ', buf); 3635a59a8b3Srsb 3645a59a8b3Srsb #define PRINT_VOPSTAT(niceflag, vop) \ 3655a59a8b3Srsb PRINT_VOPSTAT_CMN(niceflag, vop); \ 3665a59a8b3Srsb if (niceflag) \ 3675a59a8b3Srsb (void) printf("\n"); 3685a59a8b3Srsb 3695a59a8b3Srsb #define PRINT_VOPSTAT_IO(niceflag, vop) \ 3705a59a8b3Srsb PRINT_VOPSTAT_CMN(niceflag, vop); \ 3715a59a8b3Srsb PRINTSTAT(niceflag, " %5s\n", "%lld:", \ 3725a59a8b3Srsb DELTA(vop##_bytes), ' ', buf); 3735a59a8b3Srsb 3745a59a8b3Srsb static void 3755a59a8b3Srsb vop_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) 3765a59a8b3Srsb { 3775a59a8b3Srsb int niceflag = ((dispflag & DISP_RAW) == 0); 3785a59a8b3Srsb char buf[LBUFSZ]; 3795a59a8b3Srsb 3805a59a8b3Srsb if (niceflag) { 3815a59a8b3Srsb (void) printf("%s\n", name); 3825a59a8b3Srsb (void) printf(gettext(" operation #ops bytes\n")); 3835a59a8b3Srsb } 3845a59a8b3Srsb 3855a59a8b3Srsb PRINT_VOPSTAT(niceflag, open); 3865a59a8b3Srsb PRINT_VOPSTAT(niceflag, close); 3875a59a8b3Srsb PRINT_VOPSTAT_IO(niceflag, read); 3885a59a8b3Srsb PRINT_VOPSTAT_IO(niceflag, write); 3895a59a8b3Srsb PRINT_VOPSTAT(niceflag, ioctl); 3905a59a8b3Srsb PRINT_VOPSTAT(niceflag, setfl); 3915a59a8b3Srsb PRINT_VOPSTAT(niceflag, getattr); 3925a59a8b3Srsb PRINT_VOPSTAT(niceflag, setattr); 3935a59a8b3Srsb PRINT_VOPSTAT(niceflag, access); 3945a59a8b3Srsb PRINT_VOPSTAT(niceflag, lookup); 3955a59a8b3Srsb PRINT_VOPSTAT(niceflag, create); 3965a59a8b3Srsb PRINT_VOPSTAT(niceflag, remove); 3975a59a8b3Srsb PRINT_VOPSTAT(niceflag, link); 3985a59a8b3Srsb PRINT_VOPSTAT(niceflag, rename); 3995a59a8b3Srsb PRINT_VOPSTAT(niceflag, mkdir); 4005a59a8b3Srsb PRINT_VOPSTAT(niceflag, rmdir); 4015a59a8b3Srsb PRINT_VOPSTAT_IO(niceflag, readdir); 4025a59a8b3Srsb PRINT_VOPSTAT(niceflag, symlink); 4035a59a8b3Srsb PRINT_VOPSTAT(niceflag, readlink); 4045a59a8b3Srsb PRINT_VOPSTAT(niceflag, fsync); 4055a59a8b3Srsb PRINT_VOPSTAT(niceflag, inactive); 4065a59a8b3Srsb PRINT_VOPSTAT(niceflag, fid); 4075a59a8b3Srsb PRINT_VOPSTAT(niceflag, rwlock); 4085a59a8b3Srsb PRINT_VOPSTAT(niceflag, rwunlock); 4095a59a8b3Srsb PRINT_VOPSTAT(niceflag, seek); 4105a59a8b3Srsb PRINT_VOPSTAT(niceflag, cmp); 4115a59a8b3Srsb PRINT_VOPSTAT(niceflag, frlock); 4125a59a8b3Srsb PRINT_VOPSTAT(niceflag, space); 4135a59a8b3Srsb PRINT_VOPSTAT(niceflag, realvp); 4145a59a8b3Srsb PRINT_VOPSTAT(niceflag, getpage); 4155a59a8b3Srsb PRINT_VOPSTAT(niceflag, putpage); 4165a59a8b3Srsb PRINT_VOPSTAT(niceflag, map); 4175a59a8b3Srsb PRINT_VOPSTAT(niceflag, addmap); 4185a59a8b3Srsb PRINT_VOPSTAT(niceflag, delmap); 4195a59a8b3Srsb PRINT_VOPSTAT(niceflag, poll); 4205a59a8b3Srsb PRINT_VOPSTAT(niceflag, dump); 4215a59a8b3Srsb PRINT_VOPSTAT(niceflag, pathconf); 4225a59a8b3Srsb PRINT_VOPSTAT(niceflag, pageio); 4235a59a8b3Srsb PRINT_VOPSTAT(niceflag, dumpctl); 4245a59a8b3Srsb PRINT_VOPSTAT(niceflag, dispose); 4255a59a8b3Srsb PRINT_VOPSTAT(niceflag, getsecattr); 4265a59a8b3Srsb PRINT_VOPSTAT(niceflag, setsecattr); 4275a59a8b3Srsb PRINT_VOPSTAT(niceflag, shrlock); 4285a59a8b3Srsb PRINT_VOPSTAT(niceflag, vnevent); 4295a59a8b3Srsb 4305a59a8b3Srsb if (niceflag) { 4315a59a8b3Srsb /* Make it easier on the eyes */ 4325a59a8b3Srsb (void) printf("\n"); 4335a59a8b3Srsb } else { 4345a59a8b3Srsb (void) printf("%s\n", name); 4355a59a8b3Srsb } 4365a59a8b3Srsb } 4375a59a8b3Srsb 4385a59a8b3Srsb 4395a59a8b3Srsb /* 4405a59a8b3Srsb * Retrieve the vopstats. If kspp (pointer to kstat_t pointer) is non-NULL, 4415a59a8b3Srsb * then pass it back to the caller. 4425a59a8b3Srsb * 4435a59a8b3Srsb * Returns 0 on success, non-zero on failure. 4445a59a8b3Srsb */ 4455a59a8b3Srsb int 4465a59a8b3Srsb get_vopstats(kstat_ctl_t *kc, char *ksname, vopstats_t *vsp, kstat_t **kspp) 4475a59a8b3Srsb { 4485a59a8b3Srsb kstat_t *ksp; 4495a59a8b3Srsb 4505a59a8b3Srsb if (ksname == NULL || *ksname == 0) 4515a59a8b3Srsb return (1); 4525a59a8b3Srsb 4535a59a8b3Srsb errno = 0; 4545a59a8b3Srsb /* wait for a possibly up-to-date chain */ 4555a59a8b3Srsb while (kstat_chain_update(kc) == -1) { 4565a59a8b3Srsb if (errno == EAGAIN) { 4575a59a8b3Srsb errno = 0; 4585a59a8b3Srsb (void) poll(NULL, 0, RETRY_DELAY); 4595a59a8b3Srsb continue; 4605a59a8b3Srsb } 4615a59a8b3Srsb perror(gettext("kstat_chain_update")); 4625a59a8b3Srsb exit(1); 4635a59a8b3Srsb } 4645a59a8b3Srsb 4655a59a8b3Srsb if ((ksp = kstat_lookup(kc, NULL, -1, ksname)) == NULL) { 4665a59a8b3Srsb return (1); 4675a59a8b3Srsb } 4685a59a8b3Srsb 4695a59a8b3Srsb if (kstat_read(kc, ksp, vsp) == -1) { 4705a59a8b3Srsb return (1); 4715a59a8b3Srsb } 4725a59a8b3Srsb 4735a59a8b3Srsb if (kspp) 4745a59a8b3Srsb *kspp = ksp; 4755a59a8b3Srsb 4765a59a8b3Srsb return (0); 4775a59a8b3Srsb } 4785a59a8b3Srsb 4795a59a8b3Srsb /* 4805a59a8b3Srsb * Given a file system type name, determine if it's part of the 4815a59a8b3Srsb * exception list of file systems that are not to be displayed. 4825a59a8b3Srsb */ 4835a59a8b3Srsb int 4845a59a8b3Srsb is_exception(char *fsname) 4855a59a8b3Srsb { 4865a59a8b3Srsb char **xlp; /* Pointer into the exception list */ 4875a59a8b3Srsb 4885a59a8b3Srsb static char *exception_list[] = { 4895a59a8b3Srsb "specfs", 4905a59a8b3Srsb "fifofs", 4915a59a8b3Srsb "fd", 4925a59a8b3Srsb "swapfs", 4935a59a8b3Srsb "ctfs", 4945a59a8b3Srsb "objfs", 4955a59a8b3Srsb "nfsdyn", 4965a59a8b3Srsb NULL 4975a59a8b3Srsb }; 4985a59a8b3Srsb 4995a59a8b3Srsb for (xlp = &exception_list[0]; *xlp != NULL; xlp++) { 5005a59a8b3Srsb if (strcmp(fsname, *xlp) == 0) 5015a59a8b3Srsb return (1); 5025a59a8b3Srsb } 5035a59a8b3Srsb 5045a59a8b3Srsb return (0); 5055a59a8b3Srsb } 5065a59a8b3Srsb 5075a59a8b3Srsb /* 5085a59a8b3Srsb * Plain and simple, build an array of names for fstypes 5095a59a8b3Srsb * Returns 0, if it encounters a problem. 5105a59a8b3Srsb */ 5115a59a8b3Srsb int 5125a59a8b3Srsb build_fstype_list(char ***fstypep) 5135a59a8b3Srsb { 5145a59a8b3Srsb int i; 5155a59a8b3Srsb int nfstype; 5165a59a8b3Srsb char buf[FSTYPSZ + 1]; 5175a59a8b3Srsb 5185a59a8b3Srsb if ((nfstype = sysfs(GETNFSTYP)) < 0) { 5195a59a8b3Srsb perror(gettext("sysfs(GETNFSTYP)")); 5205a59a8b3Srsb return (0); 5215a59a8b3Srsb } 5225a59a8b3Srsb 5235a59a8b3Srsb if ((*fstypep = calloc(nfstype, sizeof (char *))) == NULL) { 5245a59a8b3Srsb perror(gettext("calloc on fstypes")); 5255a59a8b3Srsb return (0); 5265a59a8b3Srsb } 5275a59a8b3Srsb 5285a59a8b3Srsb for (i = 1; i < nfstype; i++) { 5295a59a8b3Srsb if (sysfs(GETFSTYP, i, buf) < 0) { 5305a59a8b3Srsb perror(gettext("sysfs(GETFSTYP)")); 5315a59a8b3Srsb return (0); 5325a59a8b3Srsb } 5335a59a8b3Srsb 5345a59a8b3Srsb if (buf[0] == 0) 5355a59a8b3Srsb continue; 5365a59a8b3Srsb 5375a59a8b3Srsb /* If this is part of the exception list, move on */ 5385a59a8b3Srsb if (is_exception(buf)) 5395a59a8b3Srsb continue; 5405a59a8b3Srsb 5415a59a8b3Srsb if (((*fstypep)[i] = strdup(buf)) == NULL) { 5425a59a8b3Srsb perror(gettext("strdup() of fstype name")); 5435a59a8b3Srsb return (0); 5445a59a8b3Srsb } 5455a59a8b3Srsb } 5465a59a8b3Srsb 5475a59a8b3Srsb return (i); 5485a59a8b3Srsb } 5495a59a8b3Srsb 5505a59a8b3Srsb /* 5515a59a8b3Srsb * After we're done with getopts(), process the rest of the 5525a59a8b3Srsb * operands. We have three cases and this is the priority: 5535a59a8b3Srsb * 5545a59a8b3Srsb * 1) [ operand... ] interval count 5555a59a8b3Srsb * 2) [ operand... ] interval 5565a59a8b3Srsb * 3) [ operand... ] 5575a59a8b3Srsb * 5585a59a8b3Srsb * The trick is that any of the operands might start with a number or even 5595a59a8b3Srsb * be made up exclusively of numbers (and we have to handle negative numbers 5605a59a8b3Srsb * in case a user/script gets out of line). If we find two operands at the 5615a59a8b3Srsb * end of the list then we claim case 1. If we find only one operand at the 5625a59a8b3Srsb * end made up only of number, then we claim case 2. Otherwise, case 3. 5635a59a8b3Srsb * BTW, argc, argv don't change. 5645a59a8b3Srsb */ 5655a59a8b3Srsb int 5665a59a8b3Srsb parse_operands( 5675a59a8b3Srsb int argc, 5685a59a8b3Srsb char **argv, 5695a59a8b3Srsb int optind, 5705a59a8b3Srsb long *interval, 5715a59a8b3Srsb long *count, 5725a59a8b3Srsb entity_t **entityp) /* Array of stat-able entities */ 5735a59a8b3Srsb { 5745a59a8b3Srsb int nentities = 0; /* Number of entities found */ 5755a59a8b3Srsb int out_of_range; /* Set if 2nd-to-last operand out-of-range */ 5765a59a8b3Srsb 5775a59a8b3Srsb if (argc == optind) 5785a59a8b3Srsb return (nentities); /* None found, returns 0 */ 5795a59a8b3Srsb /* 5805a59a8b3Srsb * We know exactly what the maximum number of entities is going 5815a59a8b3Srsb * to be: argc - optind 5825a59a8b3Srsb */ 5835a59a8b3Srsb if ((*entityp = calloc((argc - optind), sizeof (entity_t))) == NULL) { 5845a59a8b3Srsb perror(gettext("calloc")); 5855a59a8b3Srsb return (-1); 5865a59a8b3Srsb } 5875a59a8b3Srsb 5885a59a8b3Srsb for (/* void */; argc > optind; optind++) { 5895a59a8b3Srsb char *endptr; 5905a59a8b3Srsb 5915a59a8b3Srsb /* If we have more than two operands left to process */ 5925a59a8b3Srsb if ((argc - optind) > 2) { 5935a59a8b3Srsb (*entityp)[nentities++].e_name = strdup(argv[optind]); 5945a59a8b3Srsb continue; 5955a59a8b3Srsb } 5965a59a8b3Srsb 5975a59a8b3Srsb /* If we're here, then we only have one or two operands left */ 5985a59a8b3Srsb errno = 0; 5995a59a8b3Srsb out_of_range = 0; 6005a59a8b3Srsb *interval = strtol(argv[optind], &endptr, 10); 6015a59a8b3Srsb if (*endptr && !isdigit((int)*endptr)) { 6025a59a8b3Srsb /* Operand was not a number */ 6035a59a8b3Srsb (*entityp)[nentities++].e_name = strdup(argv[optind]); 6045a59a8b3Srsb continue; 6055a59a8b3Srsb } else if (errno == ERANGE || *interval <= 0 || 6065a59a8b3Srsb *interval > MAXLONG) { 6075a59a8b3Srsb /* Operand was a number, just out of range */ 6085a59a8b3Srsb out_of_range++; 6095a59a8b3Srsb } 6105a59a8b3Srsb 6115a59a8b3Srsb /* 6125a59a8b3Srsb * The last operand we saw was a number. If it happened to 6135a59a8b3Srsb * be the last operand, then it is the interval... 6145a59a8b3Srsb */ 6155a59a8b3Srsb if ((argc - optind) == 1) { 6165a59a8b3Srsb /* ...but we need to check the range. */ 6175a59a8b3Srsb if (out_of_range) { 6185a59a8b3Srsb (void) fprintf(stderr, gettext( 6195a59a8b3Srsb "interval must be between 1 and " 6205a59a8b3Srsb "%ld (inclusive)\n"), MAXLONG); 6215a59a8b3Srsb return (-1); 6225a59a8b3Srsb } else { 6235a59a8b3Srsb /* 6245a59a8b3Srsb * The value of the interval is valid. Set 6255a59a8b3Srsb * count to something really big so it goes 6265a59a8b3Srsb * virtually forever. 6275a59a8b3Srsb */ 6285a59a8b3Srsb *count = MAXLONG; 6295a59a8b3Srsb break; 6305a59a8b3Srsb } 6315a59a8b3Srsb } 6325a59a8b3Srsb 6335a59a8b3Srsb /* 6345a59a8b3Srsb * At this point, we *might* have the interval, but if the 6355a59a8b3Srsb * next operand isn't a number, then we don't have either 6365a59a8b3Srsb * the interval nor the count. Both must be set to the 6375a59a8b3Srsb * defaults. In that case, both the current and the previous 6385a59a8b3Srsb * operands are stat-able entities. 6395a59a8b3Srsb */ 6405a59a8b3Srsb errno = 0; 6415a59a8b3Srsb *count = strtol(argv[optind + 1], &endptr, 10); 6425a59a8b3Srsb if (*endptr && !isdigit((int)*endptr)) { 6435a59a8b3Srsb /* 6445a59a8b3Srsb * Faked out! The last operand wasn't a number so 6455a59a8b3Srsb * the current and previous operands should be 6465a59a8b3Srsb * stat-able entities. We also need to reset interval. 6475a59a8b3Srsb */ 6485a59a8b3Srsb *interval = 0; 6495a59a8b3Srsb (*entityp)[nentities++].e_name = strdup(argv[optind++]); 6505a59a8b3Srsb (*entityp)[nentities++].e_name = strdup(argv[optind++]); 6515a59a8b3Srsb } else if (out_of_range || errno == ERANGE || *count <= 0) { 6525a59a8b3Srsb (void) fprintf(stderr, gettext( 6535a59a8b3Srsb "Both interval and count must be between 1 " 6545a59a8b3Srsb "and %ld (inclusive)\n"), MAXLONG); 6555a59a8b3Srsb return (-1); 6565a59a8b3Srsb } 6575a59a8b3Srsb break; /* Done! */ 6585a59a8b3Srsb } 6595a59a8b3Srsb return (nentities); 6605a59a8b3Srsb } 6615a59a8b3Srsb 6625a59a8b3Srsb /* 6635a59a8b3Srsb * set_mntpt() looks at the entity's name (e_name) and finds its 6645a59a8b3Srsb * mountpoint. To do this, we need to build a list of mountpoints 6655a59a8b3Srsb * from /etc/mnttab. We only need to do this once and we don't do it 6665a59a8b3Srsb * if we don't need to look at any mountpoints. 6675a59a8b3Srsb * Returns 0 on success, non-zero if it couldn't find a mount-point. 6685a59a8b3Srsb */ 6695a59a8b3Srsb int 6705a59a8b3Srsb set_mntpt(entity_t *ep) 6715a59a8b3Srsb { 6725a59a8b3Srsb static struct mnt { 6735a59a8b3Srsb struct mnt *m_next; 6745a59a8b3Srsb char *m_mntpt; 6755a59a8b3Srsb ulong_t m_fsid; /* From statvfs(), set only as needed */ 6765a59a8b3Srsb } *mnt_list = NULL; /* Linked list of mount-points */ 6775a59a8b3Srsb struct mnt *mntp; 6785a59a8b3Srsb struct statvfs statvfsbuf; 6795a59a8b3Srsb char *original_name = ep->e_name; 6805a59a8b3Srsb char path[PATH_MAX]; 6815a59a8b3Srsb 6825a59a8b3Srsb if (original_name == NULL) /* Shouldn't happen */ 6835a59a8b3Srsb return (1); 6845a59a8b3Srsb 6855a59a8b3Srsb /* We only set up mnt_list the first time this is called */ 6865a59a8b3Srsb if (mnt_list == NULL) { 6875a59a8b3Srsb FILE *fp; 6885a59a8b3Srsb struct mnttab mnttab; 6895a59a8b3Srsb 6905a59a8b3Srsb if ((fp = fopen(MNTTAB, "r")) == NULL) { 6915a59a8b3Srsb perror(MNTTAB); 6925a59a8b3Srsb return (1); 6935a59a8b3Srsb } 6945a59a8b3Srsb resetmnttab(fp); 6955a59a8b3Srsb /* 6965a59a8b3Srsb * We insert at the front of the list so that when we 6975a59a8b3Srsb * search entries we'll have the last mounted entries 6985a59a8b3Srsb * first in the list so that we can match the longest 6995a59a8b3Srsb * mountpoint. 7005a59a8b3Srsb */ 7015a59a8b3Srsb while (getmntent(fp, &mnttab) == 0) { 7025a59a8b3Srsb if ((mntp = malloc(sizeof (*mntp))) == NULL) { 7035a59a8b3Srsb perror(gettext("Can't create mount list")); 7045a59a8b3Srsb return (1); 7055a59a8b3Srsb } 7065a59a8b3Srsb mntp->m_mntpt = strdup(mnttab.mnt_mountp); 7075a59a8b3Srsb mntp->m_next = mnt_list; 7085a59a8b3Srsb mnt_list = mntp; 7095a59a8b3Srsb } 7105a59a8b3Srsb (void) fclose(fp); 7115a59a8b3Srsb } 7125a59a8b3Srsb 7135a59a8b3Srsb if (realpath(original_name, path) == NULL) { 7145a59a8b3Srsb perror(original_name); 7155a59a8b3Srsb return (1); 7165a59a8b3Srsb } 7175a59a8b3Srsb 7185a59a8b3Srsb /* 7195a59a8b3Srsb * Now that we have the path, walk through the mnt_list and 7205a59a8b3Srsb * look for the first (best) match. 7215a59a8b3Srsb */ 7225a59a8b3Srsb for (mntp = mnt_list; mntp; mntp = mntp->m_next) { 7235a59a8b3Srsb if (strncmp(path, mntp->m_mntpt, strlen(mntp->m_mntpt)) == 0) { 7245a59a8b3Srsb if (mntp->m_fsid == 0) { 7255a59a8b3Srsb if (statvfs(mntp->m_mntpt, &statvfsbuf)) { 7265a59a8b3Srsb /* Can't statvfs so no match */ 7275a59a8b3Srsb continue; 7285a59a8b3Srsb } else { 7295a59a8b3Srsb mntp->m_fsid = statvfsbuf.f_fsid; 7305a59a8b3Srsb } 7315a59a8b3Srsb } 7325a59a8b3Srsb 7335a59a8b3Srsb if (ep->e_fsid != mntp->m_fsid) { 7345a59a8b3Srsb /* No match - Move on */ 7355a59a8b3Srsb continue; 7365a59a8b3Srsb } 7375a59a8b3Srsb 7385a59a8b3Srsb break; 7395a59a8b3Srsb } 7405a59a8b3Srsb } 7415a59a8b3Srsb 7425a59a8b3Srsb if (mntp == NULL) { 7435a59a8b3Srsb (void) fprintf(stderr, gettext( 7445a59a8b3Srsb "Can't find mount point for %s\n"), path); 7455a59a8b3Srsb return (1); 7465a59a8b3Srsb } 7475a59a8b3Srsb 7485a59a8b3Srsb ep->e_name = strdup(mntp->m_mntpt); 7495a59a8b3Srsb free(original_name); 7505a59a8b3Srsb return (0); 7515a59a8b3Srsb } 7525a59a8b3Srsb 7535a59a8b3Srsb /* 7545a59a8b3Srsb * We have an array of entities that are potentially stat-able. Using 7555a59a8b3Srsb * the name (e_name) of the entity, attempt to construct a ksname suitable 7565a59a8b3Srsb * for use by kstat_lookup(3kstat) and fill it into the e_ksname member. 7575a59a8b3Srsb * 7585a59a8b3Srsb * We check the e_name against the list of file system types. If there is 7595a59a8b3Srsb * no match then test to see if the path is valid. If the path is valid, 7605a59a8b3Srsb * then determine the mountpoint. 7615a59a8b3Srsb */ 7625a59a8b3Srsb void 7635a59a8b3Srsb set_ksnames(entity_t *entities, int nentities, char **fstypes, int nfstypes) 7645a59a8b3Srsb { 7655a59a8b3Srsb int i, j; 7665a59a8b3Srsb struct statvfs statvfsbuf; 7675a59a8b3Srsb 7685a59a8b3Srsb for (i = 0; i < nentities; i++) { 7695a59a8b3Srsb entity_t *ep = &entities[i]; 7705a59a8b3Srsb 7715a59a8b3Srsb /* Check the name against the list of fstypes */ 7725a59a8b3Srsb for (j = 1; j < nfstypes; j++) { 7735a59a8b3Srsb if (fstypes[j] && ep->e_name && 7745a59a8b3Srsb strcmp(ep->e_name, fstypes[j]) == 0) { 7755a59a8b3Srsb /* It's a file system type */ 7765a59a8b3Srsb ep->e_type = ENTYPE_FSTYPE; 7775a59a8b3Srsb (void) snprintf(ep->e_ksname, 7785a59a8b3Srsb KSTAT_STRLEN, "%s%s", 7795a59a8b3Srsb 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) { 7835a59a8b3Srsb perror(gettext("calloc() 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 */ 8025a59a8b3Srsb if (ep->e_name == NULL || statvfs(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) { 8215a59a8b3Srsb perror(gettext("Can't calloc vopstats")); 8225a59a8b3Srsb exit(1); 8235a59a8b3Srsb } 8245a59a8b3Srsb } 8255a59a8b3Srsb } 8265a59a8b3Srsb 8275a59a8b3Srsb void 8285a59a8b3Srsb print_time(int type) 8295a59a8b3Srsb { 8305a59a8b3Srsb time_t t; 831*13c7b6acSrsb static char *fmt = NULL; /* Time format */ 832*13c7b6acSrsb 833*13c7b6acSrsb /* We only need to retrieve this once per invocation */ 834*13c7b6acSrsb if (fmt == NULL) { 835*13c7b6acSrsb fmt = nl_langinfo(_DATE_FMT); 836*13c7b6acSrsb } 8375a59a8b3Srsb 8385a59a8b3Srsb if (time(&t) != -1) { 8395a59a8b3Srsb if (type == UDATE) { 8405a59a8b3Srsb (void) printf("%ld\n", t); 8415a59a8b3Srsb } else if (type == DDATE) { 842*13c7b6acSrsb char dstr[64]; 843*13c7b6acSrsb int len; 8445a59a8b3Srsb 845*13c7b6acSrsb len = strftime(dstr, sizeof (dstr), fmt, localtime(&t)); 846*13c7b6acSrsb if (len > 0) { 847*13c7b6acSrsb (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 */ 8855a59a8b3Srsb long interval = 0; 8865a59a8b3Srsb boolean_t fstypes_only = B_FALSE; /* Display fstypes only */ 8875a59a8b3Srsb char **fstypes; /* Array of names of all fstypes */ 8885a59a8b3Srsb int nentities; /* Number of stat-able entities */ 8895a59a8b3Srsb entity_t *entities; /* Array of stat-able entities */ 8905a59a8b3Srsb kstat_ctl_t *kc; 8915a59a8b3Srsb void (*dfunc)(char *, vopstats_t *, vopstats_t *, int) = dflt_display; 8925a59a8b3Srsb 8935a59a8b3Srsb extern int optind; 8945a59a8b3Srsb 895*13c7b6acSrsb (void) setlocale(LC_ALL, ""); 896*13c7b6acSrsb #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 897*13c7b6acSrsb #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 898*13c7b6acSrsb #endif 899*13c7b6acSrsb (void) textdomain(TEXT_DOMAIN); 900*13c7b6acSrsb 9015a59a8b3Srsb cmdname = argv[0]; 9025a59a8b3Srsb while ((c = getopt(argc, argv, OPTIONS)) != EOF) { 9035a59a8b3Srsb switch (c) { 9045a59a8b3Srsb 9055a59a8b3Srsb default: 9065a59a8b3Srsb usage(); 9075a59a8b3Srsb break; 9085a59a8b3Srsb 9093a401a6bSrsb case 'F': /* Only display available FStypes */ 9103a401a6bSrsb fstypes_only = B_TRUE; 9113a401a6bSrsb break; 9123a401a6bSrsb 9133a401a6bSrsb #if PARSABLE_OUTPUT 9145a59a8b3Srsb case 'P': /* Parsable output */ 9155a59a8b3Srsb dispflag |= DISP_RAW; 9165a59a8b3Srsb break; 9173a401a6bSrsb #endif /* PARSABLE_OUTPUT */ 9185a59a8b3Srsb 9195a59a8b3Srsb case 'T': /* Timestamp */ 9205a59a8b3Srsb if (optarg) { 9215a59a8b3Srsb if (strcmp(optarg, "u") == 0) { 9225a59a8b3Srsb timestamp = UDATE; 9235a59a8b3Srsb } else if (strcmp(optarg, "d") == 0) { 9245a59a8b3Srsb timestamp = DDATE; 9255a59a8b3Srsb } 9265a59a8b3Srsb } 9275a59a8b3Srsb 9285a59a8b3Srsb /* If it was never set properly... */ 9295a59a8b3Srsb if (timestamp == NODATE) { 9305a59a8b3Srsb (void) fprintf(stderr, gettext( 9315a59a8b3Srsb "%s: -T option requires either 'u' or 'd'\n"), 9325a59a8b3Srsb cmdname); 9335a59a8b3Srsb usage(); 9345a59a8b3Srsb } 9355a59a8b3Srsb break; 9365a59a8b3Srsb 9375a59a8b3Srsb case 'a': 9385a59a8b3Srsb set_dispfunc(&dfunc, attr_display); 9395a59a8b3Srsb break; 9405a59a8b3Srsb 9415a59a8b3Srsb case 'f': 9425a59a8b3Srsb set_dispfunc(&dfunc, vop_display); 9435a59a8b3Srsb break; 9445a59a8b3Srsb 9455a59a8b3Srsb case 'i': 9465a59a8b3Srsb set_dispfunc(&dfunc, io_display); 9475a59a8b3Srsb break; 9485a59a8b3Srsb 9495a59a8b3Srsb case 'n': 9505a59a8b3Srsb set_dispfunc(&dfunc, naming_display); 9515a59a8b3Srsb break; 9525a59a8b3Srsb 9535a59a8b3Srsb case 'v': 9545a59a8b3Srsb set_dispfunc(&dfunc, vm_display); 9555a59a8b3Srsb break; 9565a59a8b3Srsb } 9575a59a8b3Srsb } 9585a59a8b3Srsb 9593a401a6bSrsb #if PARSABLE_OUTPUT 9605a59a8b3Srsb if ((dispflag & DISP_RAW) && (timestamp != NODATE)) { 9615a59a8b3Srsb (void) fprintf(stderr, gettext( 9625a59a8b3Srsb "-P and -T options are mutually exclusive\n")); 9635a59a8b3Srsb usage(); 9645a59a8b3Srsb } 9653a401a6bSrsb #endif /* PARSABLE_OUTPUT */ 9665a59a8b3Srsb 9675a59a8b3Srsb /* Gather the list of filesystem types */ 9685a59a8b3Srsb if ((nfstypes = build_fstype_list(&fstypes)) == 0) { 9695a59a8b3Srsb (void) fprintf(stderr, 9705a59a8b3Srsb gettext("Can't build list of fstypes\n")); 9715a59a8b3Srsb exit(1); 9725a59a8b3Srsb } 9735a59a8b3Srsb 9745a59a8b3Srsb nentities = parse_operands( 9755a59a8b3Srsb argc, argv, optind, &interval, &count, &entities); 9765a59a8b3Srsb 9775a59a8b3Srsb if (nentities == -1) /* Set of operands didn't parse properly */ 9785a59a8b3Srsb usage(); 9795a59a8b3Srsb 9803a401a6bSrsb if ((nentities == 0) && (fstypes_only == B_FALSE)) { 9813a401a6bSrsb (void) fprintf(stderr, gettext( 9823a401a6bSrsb "Must specify -F or at least one fstype or mount point\n")); 9833a401a6bSrsb usage(); 9843a401a6bSrsb } 9855a59a8b3Srsb 9863a401a6bSrsb if ((nentities > 0) && (fstypes_only == B_TRUE)) { 9873a401a6bSrsb (void) fprintf(stderr, gettext( 9883a401a6bSrsb "Cannot use -F with fstypes or mount points\n")); 9893a401a6bSrsb usage(); 9903a401a6bSrsb } 9913a401a6bSrsb 9923a401a6bSrsb /* 9933a401a6bSrsb * If we had no operands (except for interval/count) and we 9943a401a6bSrsb * requested FStypes only (-F), then fill in the entities[] 9953a401a6bSrsb * array with all available fstypes. 9963a401a6bSrsb */ 9973a401a6bSrsb if ((nentities == 0) && (fstypes_only == B_TRUE)) { 9985a59a8b3Srsb if ((entities = calloc(nfstypes, sizeof (entity_t))) == NULL) { 9995a59a8b3Srsb (void) fprintf(stderr, 10005a59a8b3Srsb gettext("Can't calloc fstype stats\n")); 10015a59a8b3Srsb exit(1); 10025a59a8b3Srsb } 10035a59a8b3Srsb 10045a59a8b3Srsb for (i = 1; i < nfstypes; i++) { 10055a59a8b3Srsb if (fstypes[i]) { 10065a59a8b3Srsb entities[nentities].e_name = strdup(fstypes[i]); 10075a59a8b3Srsb nentities++; 10085a59a8b3Srsb } 10095a59a8b3Srsb } 10105a59a8b3Srsb } 10115a59a8b3Srsb 10125a59a8b3Srsb set_ksnames(entities, nentities, fstypes, nfstypes); 10135a59a8b3Srsb 10145a59a8b3Srsb if ((kc = kstat_open()) == NULL) { 10155a59a8b3Srsb perror(gettext("kstat_open")); 10165a59a8b3Srsb exit(1); 10175a59a8b3Srsb } 10185a59a8b3Srsb 10195a59a8b3Srsb /* 10205a59a8b3Srsb * The following loop walks through the entities[] list to "prime 10215a59a8b3Srsb * the pump" 10225a59a8b3Srsb */ 10235a59a8b3Srsb for (j = 0, linesout = 0; j < nentities; j++) { 10245a59a8b3Srsb entity_t *ent = &entities[j]; 10255a59a8b3Srsb vopstats_t *vsp = &ent->e_vs[CUR_INDEX]; 10265a59a8b3Srsb kstat_t *ksp = NULL; 10275a59a8b3Srsb 10285a59a8b3Srsb if (get_vopstats(kc, ent->e_ksname, vsp, &ksp) == 0) { 10295a59a8b3Srsb (*dfunc)(ent->e_name, NULL, vsp, 10305a59a8b3Srsb dispflag_policy(linesout == 0, dispflag)); 10315a59a8b3Srsb linesout++; 10325a59a8b3Srsb } else { 10335a59a8b3Srsb /* 10345a59a8b3Srsb * If we can't find it the first time through, then 10355a59a8b3Srsb * get rid of it. 10365a59a8b3Srsb */ 10375a59a8b3Srsb entities[j].e_ksname[0] = 0; 10385a59a8b3Srsb 10395a59a8b3Srsb /* 10403a401a6bSrsb * If we're only displaying FStypes (-F) then don't 10415a59a8b3Srsb * complain about any file systems that might not 10425a59a8b3Srsb * be loaded. Otherwise, let the user know that 10435a59a8b3Srsb * he chose poorly. 10445a59a8b3Srsb */ 10455a59a8b3Srsb if (fstypes_only == B_FALSE) { 10465a59a8b3Srsb (void) fprintf(stderr, gettext( 10475a59a8b3Srsb "No statistics available for %s\n"), 10485a59a8b3Srsb entities[j].e_name); 10495a59a8b3Srsb } 10505a59a8b3Srsb } 10515a59a8b3Srsb } 10525a59a8b3Srsb 10535a59a8b3Srsb BUMP_INDEX(); /* Swap the previous/current indices */ 10545a59a8b3Srsb for (i = 1; i <= count; i++) { 10555a59a8b3Srsb /* 10565a59a8b3Srsb * No telling how many lines will be printed in any interval. 10575a59a8b3Srsb * There should be a minimum of HEADERLINES between any 10585a59a8b3Srsb * header. If we exceed that, no big deal. 10595a59a8b3Srsb */ 10605a59a8b3Srsb if (linesout > HEADERLINES) { 10615a59a8b3Srsb linesout = 0; 10625a59a8b3Srsb printhdr = 1; 10635a59a8b3Srsb } 10645a59a8b3Srsb (void) poll(NULL, 0, interval*1000); 10655a59a8b3Srsb 10665a59a8b3Srsb if (timestamp) { 10675a59a8b3Srsb print_time(timestamp); 10685a59a8b3Srsb linesout++; 10695a59a8b3Srsb } 10705a59a8b3Srsb 10715a59a8b3Srsb for (j = 0, nentities_found = 0; j < nentities; j++) { 10725a59a8b3Srsb entity_t *ent = &entities[j]; 10735a59a8b3Srsb 10745a59a8b3Srsb /* 10755a59a8b3Srsb * If this entry has been cleared, don't attempt 10765a59a8b3Srsb * to process it. 10775a59a8b3Srsb */ 10785a59a8b3Srsb if (ent->e_ksname[0] == 0) { 10795a59a8b3Srsb continue; 10805a59a8b3Srsb } 10815a59a8b3Srsb 10825a59a8b3Srsb if (get_vopstats(kc, ent->e_ksname, 10835a59a8b3Srsb &ent->e_vs[CUR_INDEX], NULL) == 0) { 10845a59a8b3Srsb (*dfunc)(ent->e_name, &ent->e_vs[PREV_INDEX], 10855a59a8b3Srsb &ent->e_vs[CUR_INDEX], 10865a59a8b3Srsb dispflag_policy(printhdr, dispflag)); 10875a59a8b3Srsb linesout++; 10885a59a8b3Srsb nentities_found++; 10895a59a8b3Srsb } else { 10905a59a8b3Srsb if (ent->e_type == ENTYPE_MNTPT) { 10915a59a8b3Srsb (void) printf(gettext( 10925a59a8b3Srsb "<<mount point no longer " 10935a59a8b3Srsb "available: %s>>\n"), ent->e_name); 10945a59a8b3Srsb } else if (ent->e_type == ENTYPE_FSTYPE) { 10955a59a8b3Srsb (void) printf(gettext( 10965a59a8b3Srsb "<<file system module no longer " 10975a59a8b3Srsb "loaded: %s>>\n"), ent->e_name); 10985a59a8b3Srsb } else { 10995a59a8b3Srsb (void) printf(gettext( 11005a59a8b3Srsb "<<%s no longer available>>\n"), 11015a59a8b3Srsb ent->e_name); 11025a59a8b3Srsb } 11035a59a8b3Srsb /* Disable this so it doesn't print again */ 11045a59a8b3Srsb ent->e_ksname[0] = 0; 11055a59a8b3Srsb } 11065a59a8b3Srsb printhdr = 0; /* Always shut this off */ 11075a59a8b3Srsb } 11085a59a8b3Srsb BUMP_INDEX(); /* Bump the previous/current indices */ 11095a59a8b3Srsb 11105a59a8b3Srsb /* 11115a59a8b3Srsb * If the entities we were observing are no longer there 11125a59a8b3Srsb * (file system modules unloaded, file systems unmounted) 11135a59a8b3Srsb * then we're done. 11145a59a8b3Srsb */ 11155a59a8b3Srsb if (nentities_found == 0) 11165a59a8b3Srsb break; 11175a59a8b3Srsb } 11185a59a8b3Srsb 11195a59a8b3Srsb return (0); 11205a59a8b3Srsb } 1121