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