8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / stat / fsstat / fsstat.c
blob5563f410ca7222287ecfb3dc7b7703e2c486eaa3
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
27 #include <stdio.h>
28 #include <kstat.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <strings.h>
32 #include <errno.h>
33 #include <limits.h>
34 #include <sys/types.h>
35 #include <time.h>
36 #include <sys/time.h>
37 #include <sys/uio.h>
38 #include <sys/vnode.h>
39 #include <sys/vfs.h>
40 #include <sys/statvfs.h>
41 #include <sys/fstyp.h>
42 #include <sys/fsid.h>
43 #include <sys/mnttab.h>
44 #include <values.h>
45 #include <poll.h>
46 #include <ctype.h>
47 #include <libintl.h>
48 #include <locale.h>
49 #include <signal.h>
51 #include "statcommon.h"
54 * For now, parsable output is turned off. Once we gather feedback and
55 * stablize the output format, we'll turn it back on. This prevents
56 * the situation where users build tools which depend on a specific
57 * format before we declare the output stable.
59 #define PARSABLE_OUTPUT 0
61 #if PARSABLE_OUTPUT
62 #define OPTIONS "FPT:afginv"
63 #else
64 #define OPTIONS "FT:afginv"
65 #endif
67 /* Time stamp values */
68 #define NODATE 0 /* Default: No time stamp */
69 #define DDATE 1 /* Standard date format */
70 #define UDATE 2 /* Internal representation of Unix time */
72 #define RETRY_DELAY 250 /* Timeout for poll() */
73 #define HEADERLINES 12 /* Number of lines between display headers */
75 #define LBUFSZ 64 /* Generic size for local buffer */
78 * The following are used for the nicenum() function
80 #define KILO_VAL 1024
81 #define ONE_INDEX 3
83 #define NENTITY_INIT 1 /* Initial number of entities to allocate */
86 * We need to have a mechanism for an old/previous and new/current vopstat
87 * structure. We only need two per entity and we can swap between them.
89 #define VS_SIZE 2 /* Size of vopstat array */
90 #define CUR_INDEX (vs_i)
91 #define PREV_INDEX ((vs_i == 0) ? 1 : 0) /* Opposite of CUR_INDEX */
92 #define BUMP_INDEX() vs_i = ((vs_i == 0) ? 1 : 0)
95 * An "entity" is anything we're collecting statistics on, it could
96 * be a mountpoint or an FS-type.
97 * e_name is the name of the entity (e.g. mount point or FS-type)
98 * e_ksname is the name of the associated kstat
99 * e_vs is an array of vopstats. This is used to keep track of "previous"
100 * and "current" vopstats.
102 typedef struct entity {
103 char *e_name; /* name of entity */
104 vopstats_t *e_vs; /* Array of vopstats */
105 ulong_t e_fsid; /* fsid for ENTYPE_MNTPT only */
106 int e_type; /* type of entity */
107 char e_ksname[KSTAT_STRLEN]; /* kstat name */
108 } entity_t;
110 /* Types of entities (e_type) */
111 #define ENTYPE_UNKNOWN 0 /* UNKNOWN must be zero since we calloc() */
112 #define ENTYPE_FSTYPE 1
113 #define ENTYPE_MNTPT 2
115 /* If more sub-one units are added, make sure to adjust ONE_INDEX above */
116 static char units[] = "num KMGTPE";
118 char *cmdname; /* name of this command */
119 int caught_cont = 0; /* have caught a SIGCONT */
121 static uint_t timestamp_fmt = NODATE; /* print timestamp with stats */
123 static int vs_i = 0; /* Index of current vs[] slot */
125 static void
126 usage()
128 (void) fprintf(stderr, gettext(
129 "Usage: %s [-a|f|i|n|v] [-T d|u] {-F | {fstype | fspath}...} "
130 "[interval [count]]\n"), cmdname);
131 exit(2);
135 * Given a 64-bit number and a starting unit (e.g., n - nanoseconds),
136 * convert the number to a 5-character representation including any
137 * decimal point and single-character unit. Put that representation
138 * into the array "buf" (which had better be big enough).
140 char *
141 nicenum(uint64_t num, char unit, char *buf)
143 uint64_t n = num;
144 int unit_index;
145 int index;
146 char u;
148 /* If the user passed in a NUL/zero unit, use the blank value for 1 */
149 if (unit == '\0')
150 unit = ' ';
152 unit_index = 0;
153 while (units[unit_index] != unit) {
154 unit_index++;
155 if (unit_index > sizeof (units) - 1) {
156 (void) sprintf(buf, "??");
157 return (buf);
161 index = 0;
162 while (n >= KILO_VAL) {
163 n = (n + (KILO_VAL / 2)) / KILO_VAL; /* Round up or down */
164 index++;
165 unit_index++;
168 if (unit_index >= sizeof (units) - 1) {
169 (void) sprintf(buf, "??");
170 return (buf);
173 u = units[unit_index];
175 if (unit_index == ONE_INDEX) {
176 (void) sprintf(buf, "%llu", (u_longlong_t)n);
177 } else if (n < 10 && (num & (num - 1)) != 0) {
178 (void) sprintf(buf, "%.2f%c",
179 (double)num / (1ULL << 10 * index), u);
180 } else if (n < 100 && (num & (num - 1)) != 0) {
181 (void) sprintf(buf, "%.1f%c",
182 (double)num / (1ULL << 10 * index), u);
183 } else {
184 (void) sprintf(buf, "%llu%c", (u_longlong_t)n, u);
187 return (buf);
191 #define RAWVAL(ptr, member) ((ptr)->member.value.ui64)
192 #define DELTA(member) \
193 (newvsp->member.value.ui64 - (oldvsp ? oldvsp->member.value.ui64 : 0))
195 #define PRINTSTAT(isnice, nicestring, rawstring, rawval, unit, buf) \
196 (isnice) ? \
197 (void) printf((nicestring), nicenum(rawval, unit, buf)) \
199 (void) printf((rawstring), (rawval))
201 /* Values for display flag */
202 #define DISP_HEADER 0x1
203 #define DISP_RAW 0x2
206 * The policy for dealing with multiple flags is dealt with here.
207 * Currently, if we are displaying raw output, then don't allow
208 * headers to be printed.
211 dispflag_policy(int printhdr, int dispflag)
213 /* If we're not displaying raw output, then allow headers to print */
214 if ((dispflag & DISP_RAW) == 0) {
215 if (printhdr) {
216 dispflag |= DISP_HEADER;
220 return (dispflag);
223 static void
224 dflt_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
226 int niceflag = ((dispflag & DISP_RAW) == 0);
227 longlong_t nnewfile;
228 longlong_t nnamerm;
229 longlong_t nnamechg;
230 longlong_t nattrret;
231 longlong_t nattrchg;
232 longlong_t nlookup;
233 longlong_t nreaddir;
234 longlong_t ndataread;
235 longlong_t ndatawrite;
236 longlong_t readthruput;
237 longlong_t writethruput;
238 char buf[LBUFSZ];
240 nnewfile = DELTA(ncreate) + DELTA(nmkdir) + DELTA(nsymlink);
241 nnamerm = DELTA(nremove) + DELTA(nrmdir);
242 nnamechg = DELTA(nrename) + DELTA(nlink) + DELTA(nsymlink);
243 nattrret = DELTA(ngetattr) + DELTA(naccess) +
244 DELTA(ngetsecattr) + DELTA(nfid);
245 nattrchg = DELTA(nsetattr) + DELTA(nsetsecattr) + DELTA(nspace);
246 nlookup = DELTA(nlookup);
247 nreaddir = DELTA(nreaddir);
248 ndataread = DELTA(nread);
249 ndatawrite = DELTA(nwrite);
250 readthruput = DELTA(read_bytes);
251 writethruput = DELTA(write_bytes);
253 if (dispflag & DISP_HEADER) {
254 (void) printf(
255 " new name name attr attr lookup rddir read read write write\n"
256 " file remov chng get set ops ops ops bytes ops bytes\n");
259 PRINTSTAT(niceflag, "%5s ", "%lld:", nnewfile, ' ', buf);
260 PRINTSTAT(niceflag, "%5s ", "%lld:", nnamerm, ' ', buf);
261 PRINTSTAT(niceflag, "%5s ", "%lld:", nnamechg, ' ', buf);
262 PRINTSTAT(niceflag, "%5s ", "%lld:", nattrret, ' ', buf);
263 PRINTSTAT(niceflag, "%5s ", "%lld:", nattrchg, ' ', buf);
264 PRINTSTAT(niceflag, " %5s ", "%lld:", nlookup, ' ', buf);
265 PRINTSTAT(niceflag, "%5s ", "%lld:", nreaddir, ' ', buf);
266 PRINTSTAT(niceflag, "%5s ", "%lld:", ndataread, ' ', buf);
267 PRINTSTAT(niceflag, "%5s ", "%lld:", readthruput, ' ', buf);
268 PRINTSTAT(niceflag, "%5s ", "%lld:", ndatawrite, ' ', buf);
269 PRINTSTAT(niceflag, "%5s ", "%lld:", writethruput, ' ', buf);
270 (void) printf("%s\n", name);
273 static void
274 io_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
276 int niceflag = ((dispflag & DISP_RAW) == 0);
277 char buf[LBUFSZ];
279 if (dispflag & DISP_HEADER) {
280 (void) printf(
281 " read read write write rddir rddir rwlock rwulock\n"
282 " ops bytes ops bytes ops bytes ops ops\n");
285 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nread), ' ', buf);
286 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(read_bytes), ' ', buf);
288 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nwrite), ' ', buf);
289 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(write_bytes), ' ', buf);
291 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf);
292 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(readdir_bytes), ' ', buf);
294 PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nrwlock), ' ', buf);
295 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrwunlock), ' ', buf);
297 (void) printf("%s\n", name);
300 static void
301 vm_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
303 int niceflag = ((dispflag & DISP_RAW) == 0);
304 char buf[LBUFSZ];
306 if (dispflag & DISP_HEADER) {
307 (void) printf(" map addmap delmap getpag putpag pagio\n");
310 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmap), ' ', buf);
311 PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(naddmap), ' ', buf);
312 PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ndelmap), ' ', buf);
313 PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetpage), ' ', buf);
314 PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nputpage), ' ', buf);
315 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(npageio), ' ', buf);
316 (void) printf("%s\n", name);
319 static void
320 attr_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
322 int niceflag = ((dispflag & DISP_RAW) == 0);
323 char buf[LBUFSZ];
325 if (dispflag & DISP_HEADER) {
326 (void) printf("getattr setattr getsec setsec\n");
329 PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetattr), ' ', buf);
330 PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetattr), ' ', buf);
331 PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetsecattr), ' ', buf);
332 PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetsecattr), ' ', buf);
334 (void) printf("%s\n", name);
337 static void
338 naming_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
340 int niceflag = ((dispflag & DISP_RAW) == 0);
341 char buf[LBUFSZ];
343 if (dispflag & DISP_HEADER) {
344 (void) printf(
345 "lookup creat remov link renam mkdir rmdir rddir symlnk rdlnk\n");
348 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlookup), ' ', buf);
349 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(ncreate), ' ', buf);
350 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nremove), ' ', buf);
351 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlink), ' ', buf);
352 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrename), ' ', buf);
353 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmkdir), ' ', buf);
354 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrmdir), ' ', buf);
355 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf);
356 PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsymlink), ' ', buf);
357 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreadlink), ' ', buf);
358 (void) printf("%s\n", name);
362 #define PRINT_VOPSTAT_CMN(niceflag, vop) \
363 if (niceflag) \
364 (void) printf("%10s ", #vop); \
365 PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(n##vop), ' ', buf);
367 #define PRINT_VOPSTAT(niceflag, vop) \
368 PRINT_VOPSTAT_CMN(niceflag, vop); \
369 if (niceflag) \
370 (void) printf("\n");
372 #define PRINT_VOPSTAT_IO(niceflag, vop) \
373 PRINT_VOPSTAT_CMN(niceflag, vop); \
374 PRINTSTAT(niceflag, " %5s\n", "%lld:", \
375 DELTA(vop##_bytes), ' ', buf);
377 static void
378 vop_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag)
380 int niceflag = ((dispflag & DISP_RAW) == 0);
381 char buf[LBUFSZ];
383 if (niceflag) {
384 (void) printf("%s\n", name);
385 (void) printf(" operation #ops bytes\n");
388 PRINT_VOPSTAT(niceflag, open);
389 PRINT_VOPSTAT(niceflag, close);
390 PRINT_VOPSTAT_IO(niceflag, read);
391 PRINT_VOPSTAT_IO(niceflag, write);
392 PRINT_VOPSTAT(niceflag, ioctl);
393 PRINT_VOPSTAT(niceflag, setfl);
394 PRINT_VOPSTAT(niceflag, getattr);
395 PRINT_VOPSTAT(niceflag, setattr);
396 PRINT_VOPSTAT(niceflag, access);
397 PRINT_VOPSTAT(niceflag, lookup);
398 PRINT_VOPSTAT(niceflag, create);
399 PRINT_VOPSTAT(niceflag, remove);
400 PRINT_VOPSTAT(niceflag, link);
401 PRINT_VOPSTAT(niceflag, rename);
402 PRINT_VOPSTAT(niceflag, mkdir);
403 PRINT_VOPSTAT(niceflag, rmdir);
404 PRINT_VOPSTAT_IO(niceflag, readdir);
405 PRINT_VOPSTAT(niceflag, symlink);
406 PRINT_VOPSTAT(niceflag, readlink);
407 PRINT_VOPSTAT(niceflag, fsync);
408 PRINT_VOPSTAT(niceflag, inactive);
409 PRINT_VOPSTAT(niceflag, fid);
410 PRINT_VOPSTAT(niceflag, rwlock);
411 PRINT_VOPSTAT(niceflag, rwunlock);
412 PRINT_VOPSTAT(niceflag, seek);
413 PRINT_VOPSTAT(niceflag, cmp);
414 PRINT_VOPSTAT(niceflag, frlock);
415 PRINT_VOPSTAT(niceflag, space);
416 PRINT_VOPSTAT(niceflag, realvp);
417 PRINT_VOPSTAT(niceflag, getpage);
418 PRINT_VOPSTAT(niceflag, putpage);
419 PRINT_VOPSTAT(niceflag, map);
420 PRINT_VOPSTAT(niceflag, addmap);
421 PRINT_VOPSTAT(niceflag, delmap);
422 PRINT_VOPSTAT(niceflag, poll);
423 PRINT_VOPSTAT(niceflag, dump);
424 PRINT_VOPSTAT(niceflag, pathconf);
425 PRINT_VOPSTAT(niceflag, pageio);
426 PRINT_VOPSTAT(niceflag, dumpctl);
427 PRINT_VOPSTAT(niceflag, dispose);
428 PRINT_VOPSTAT(niceflag, getsecattr);
429 PRINT_VOPSTAT(niceflag, setsecattr);
430 PRINT_VOPSTAT(niceflag, shrlock);
431 PRINT_VOPSTAT(niceflag, vnevent);
432 PRINT_VOPSTAT(niceflag, reqzcbuf);
433 PRINT_VOPSTAT(niceflag, retzcbuf);
435 if (niceflag) {
436 /* Make it easier on the eyes */
437 (void) printf("\n");
438 } else {
439 (void) printf("%s\n", name);
445 * Retrieve the vopstats. If kspp (pointer to kstat_t pointer) is non-NULL,
446 * then pass it back to the caller.
448 * Returns 0 on success, non-zero on failure.
451 get_vopstats(kstat_ctl_t *kc, char *ksname, vopstats_t *vsp, kstat_t **kspp)
453 kstat_t *ksp;
455 if (ksname == NULL || *ksname == 0)
456 return (1);
458 errno = 0;
459 /* wait for a possibly up-to-date chain */
460 while (kstat_chain_update(kc) == -1) {
461 if (errno == EAGAIN) {
462 errno = 0;
463 (void) poll(NULL, 0, RETRY_DELAY);
464 continue;
466 perror("kstat_chain_update");
467 exit(1);
470 if ((ksp = kstat_lookup(kc, NULL, -1, ksname)) == NULL) {
471 return (1);
474 if (kstat_read(kc, ksp, vsp) == -1) {
475 return (1);
478 if (kspp)
479 *kspp = ksp;
481 return (0);
485 * Given a file system type name, determine if it's part of the
486 * exception list of file systems that are not to be displayed.
489 is_exception(char *fsname)
491 char **xlp; /* Pointer into the exception list */
493 static char *exception_list[] = {
494 "specfs",
495 "fifofs",
496 "fd",
497 "swapfs",
498 "ctfs",
499 "objfs",
500 "nfsdyn",
501 NULL
504 for (xlp = &exception_list[0]; *xlp != NULL; xlp++) {
505 if (strcmp(fsname, *xlp) == 0)
506 return (1);
509 return (0);
513 * Plain and simple, build an array of names for fstypes
514 * Returns 0, if it encounters a problem.
517 build_fstype_list(char ***fstypep)
519 int i;
520 int nfstype;
521 char buf[FSTYPSZ + 1];
523 if ((nfstype = sysfs(GETNFSTYP)) < 0) {
524 perror("sysfs(GETNFSTYP)");
525 return (0);
528 if ((*fstypep = calloc(nfstype, sizeof (char *))) == NULL) {
529 perror("calloc() fstypes");
530 return (0);
533 for (i = 1; i < nfstype; i++) {
534 if (sysfs(GETFSTYP, i, buf) < 0) {
535 perror("sysfs(GETFSTYP)");
536 return (0);
539 if (buf[0] == 0)
540 continue;
542 /* If this is part of the exception list, move on */
543 if (is_exception(buf))
544 continue;
546 if (((*fstypep)[i] = strdup(buf)) == NULL) {
547 perror("strdup() fstype name");
548 return (0);
552 return (i);
556 * After we're done with getopts(), process the rest of the
557 * operands. We have three cases and this is the priority:
559 * 1) [ operand... ] interval count
560 * 2) [ operand... ] interval
561 * 3) [ operand... ]
563 * The trick is that any of the operands might start with a number or even
564 * be made up exclusively of numbers (and we have to handle negative numbers
565 * in case a user/script gets out of line). If we find two operands at the
566 * end of the list then we claim case 1. If we find only one operand at the
567 * end made up only of number, then we claim case 2. Otherwise, case 3.
568 * BTW, argc, argv don't change.
571 parse_operands(
572 int argc,
573 char **argv,
574 int optind,
575 long *interval,
576 long *count,
577 entity_t **entityp) /* Array of stat-able entities */
579 int nentities = 0; /* Number of entities found */
580 int out_of_range; /* Set if 2nd-to-last operand out-of-range */
582 if (argc == optind)
583 return (nentities); /* None found, returns 0 */
585 * We know exactly what the maximum number of entities is going
586 * to be: argc - optind
588 if ((*entityp = calloc((argc - optind), sizeof (entity_t))) == NULL) {
589 perror("calloc() entities");
590 return (-1);
593 for (/* void */; argc > optind; optind++) {
594 char *endptr;
596 /* If we have more than two operands left to process */
597 if ((argc - optind) > 2) {
598 (*entityp)[nentities++].e_name = strdup(argv[optind]);
599 continue;
602 /* If we're here, then we only have one or two operands left */
603 errno = 0;
604 out_of_range = 0;
605 *interval = strtol(argv[optind], &endptr, 10);
606 if (*endptr && !isdigit((int)*endptr)) {
607 /* Operand was not a number */
608 (*entityp)[nentities++].e_name = strdup(argv[optind]);
609 continue;
610 } else if (errno == ERANGE || *interval <= 0 ||
611 *interval > MAXLONG) {
612 /* Operand was a number, just out of range */
613 out_of_range++;
617 * The last operand we saw was a number. If it happened to
618 * be the last operand, then it is the interval...
620 if ((argc - optind) == 1) {
621 /* ...but we need to check the range. */
622 if (out_of_range) {
623 (void) fprintf(stderr, gettext(
624 "interval must be between 1 and "
625 "%ld (inclusive)\n"), MAXLONG);
626 return (-1);
627 } else {
629 * The value of the interval is valid. Set
630 * count to something really big so it goes
631 * virtually forever.
633 *count = MAXLONG;
634 break;
639 * At this point, we *might* have the interval, but if the
640 * next operand isn't a number, then we don't have either
641 * the interval nor the count. Both must be set to the
642 * defaults. In that case, both the current and the previous
643 * operands are stat-able entities.
645 errno = 0;
646 *count = strtol(argv[optind + 1], &endptr, 10);
647 if (*endptr && !isdigit((int)*endptr)) {
649 * Faked out! The last operand wasn't a number so
650 * the current and previous operands should be
651 * stat-able entities. We also need to reset interval.
653 *interval = 0;
654 (*entityp)[nentities++].e_name = strdup(argv[optind++]);
655 (*entityp)[nentities++].e_name = strdup(argv[optind++]);
656 } else if (out_of_range || errno == ERANGE || *count <= 0) {
657 (void) fprintf(stderr, gettext(
658 "Both interval and count must be between 1 "
659 "and %ld (inclusive)\n"), MAXLONG);
660 return (-1);
662 break; /* Done! */
664 return (nentities);
668 * set_mntpt() looks at the entity's name (e_name) and finds its
669 * mountpoint. To do this, we need to build a list of mountpoints
670 * from /etc/mnttab. We only need to do this once and we don't do it
671 * if we don't need to look at any mountpoints.
672 * Returns 0 on success, non-zero if it couldn't find a mount-point.
675 set_mntpt(entity_t *ep)
677 static struct mnt {
678 struct mnt *m_next;
679 char *m_mntpt;
680 ulong_t m_fsid; /* From statvfs(), set only as needed */
681 } *mnt_list = NULL; /* Linked list of mount-points */
682 struct mnt *mntp;
683 struct statvfs64 statvfsbuf;
684 char *original_name = ep->e_name;
685 char path[PATH_MAX];
687 if (original_name == NULL) /* Shouldn't happen */
688 return (1);
690 /* We only set up mnt_list the first time this is called */
691 if (mnt_list == NULL) {
692 FILE *fp;
693 struct mnttab mnttab;
695 if ((fp = fopen(MNTTAB, "r")) == NULL) {
696 perror(MNTTAB);
697 return (1);
699 resetmnttab(fp);
701 * We insert at the front of the list so that when we
702 * search entries we'll have the last mounted entries
703 * first in the list so that we can match the longest
704 * mountpoint.
706 while (getmntent(fp, &mnttab) == 0) {
707 if ((mntp = malloc(sizeof (*mntp))) == NULL) {
708 perror("malloc() mount list");
709 return (1);
711 mntp->m_mntpt = strdup(mnttab.mnt_mountp);
712 mntp->m_next = mnt_list;
713 mnt_list = mntp;
715 (void) fclose(fp);
718 if (realpath(original_name, path) == NULL) {
719 perror(original_name);
720 return (1);
724 * Now that we have the path, walk through the mnt_list and
725 * look for the first (best) match.
727 for (mntp = mnt_list; mntp; mntp = mntp->m_next) {
728 if (strncmp(path, mntp->m_mntpt, strlen(mntp->m_mntpt)) == 0) {
729 if (mntp->m_fsid == 0) {
730 if (statvfs64(mntp->m_mntpt, &statvfsbuf)) {
731 /* Can't statvfs so no match */
732 continue;
733 } else {
734 mntp->m_fsid = statvfsbuf.f_fsid;
738 if (ep->e_fsid != mntp->m_fsid) {
739 /* No match - Move on */
740 continue;
743 break;
747 if (mntp == NULL) {
748 (void) fprintf(stderr, gettext(
749 "Can't find mount point for %s\n"), path);
750 return (1);
753 ep->e_name = strdup(mntp->m_mntpt);
754 free(original_name);
755 return (0);
759 * We have an array of entities that are potentially stat-able. Using
760 * the name (e_name) of the entity, attempt to construct a ksname suitable
761 * for use by kstat_lookup(3kstat) and fill it into the e_ksname member.
763 * We check the e_name against the list of file system types. If there is
764 * no match then test to see if the path is valid. If the path is valid,
765 * then determine the mountpoint.
767 void
768 set_ksnames(entity_t *entities, int nentities, char **fstypes, int nfstypes)
770 int i, j;
771 struct statvfs64 statvfsbuf;
773 for (i = 0; i < nentities; i++) {
774 entity_t *ep = &entities[i];
776 /* Check the name against the list of fstypes */
777 for (j = 1; j < nfstypes; j++) {
778 if (fstypes[j] && ep->e_name &&
779 strcmp(ep->e_name, fstypes[j]) == 0) {
780 /* It's a file system type */
781 ep->e_type = ENTYPE_FSTYPE;
782 (void) snprintf(ep->e_ksname, KSTAT_STRLEN,
783 "%s%s", VOPSTATS_STR, ep->e_name);
784 /* Now allocate the vopstats array */
785 ep->e_vs = calloc(VS_SIZE, sizeof (vopstats_t));
786 if (entities[i].e_vs == NULL) {
787 perror("calloc() fstype vopstats");
788 exit(1);
790 break;
793 if (j < nfstypes) /* Found it! */
794 continue;
797 * If the entity in the exception list of fstypes, then
798 * null out the entry so it isn't displayed and move along.
800 if (is_exception(ep->e_name)) {
801 ep->e_ksname[0] = 0;
802 continue;
805 /* If we didn't find it, see if it's a path */
806 if (ep->e_name == NULL || statvfs64(ep->e_name, &statvfsbuf)) {
807 /* Error - Make sure the entry is nulled out */
808 ep->e_ksname[0] = 0;
809 continue;
811 (void) snprintf(ep->e_ksname, KSTAT_STRLEN, "%s%lx",
812 VOPSTATS_STR, statvfsbuf.f_fsid);
813 ep->e_fsid = statvfsbuf.f_fsid;
814 if (set_mntpt(ep)) {
815 (void) fprintf(stderr,
816 gettext("Can't determine type of \"%s\"\n"),
817 ep->e_name ? ep->e_name : gettext("<NULL>"));
818 } else {
819 ep->e_type = ENTYPE_MNTPT;
822 /* Now allocate the vopstats array */
823 ep->e_vs = calloc(VS_SIZE, sizeof (vopstats_t));
824 if (entities[i].e_vs == NULL) {
825 perror("calloc() vopstats array");
826 exit(1);
832 * The idea is that 'dspfunc' should only be modified from the default
833 * once since the display options are mutually exclusive. If 'dspfunc'
834 * only contains the default display function, then all is good and we
835 * can set it to the new display function. Otherwise, bail.
837 void
838 set_dispfunc(
839 void (**dspfunc)(char *, vopstats_t *, vopstats_t *, int),
840 void (*newfunc)(char *, vopstats_t *, vopstats_t *, int))
842 if (*dspfunc != dflt_display) {
843 (void) fprintf(stderr, gettext(
844 "%s: Display options -{a|f|i|n|v} are mutually exclusive\n"),
845 cmdname);
846 usage();
848 *dspfunc = newfunc;
852 main(int argc, char *argv[])
854 int c;
855 int i, j; /* Generic counters */
856 int nentities_found;
857 int linesout = 0; /* Keeps track of lines printed */
858 int printhdr = 0; /* Print a header? 0 = no, 1 = yes */
859 int nfstypes; /* Number of fstypes */
860 int dispflag = 0; /* Flags for display control */
861 long count = 0; /* Number of iterations for display */
862 int forever; /* Run forever */
863 long interval = 0;
864 boolean_t fstypes_only = B_FALSE; /* Display fstypes only */
865 char **fstypes; /* Array of names of all fstypes */
866 int nentities; /* Number of stat-able entities */
867 entity_t *entities; /* Array of stat-able entities */
868 kstat_ctl_t *kc;
869 void (*dfunc)(char *, vopstats_t *, vopstats_t *, int) = dflt_display;
870 hrtime_t start_n; /* Start time */
871 hrtime_t period_n; /* Interval in nanoseconds */
873 extern int optind;
875 (void) setlocale(LC_ALL, "");
876 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
877 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
878 #endif
879 (void) textdomain(TEXT_DOMAIN);
881 /* Don't let buffering interfere with piped output. */
882 (void) setvbuf(stdout, NULL, _IOLBF, 0);
884 cmdname = argv[0];
885 while ((c = getopt(argc, argv, OPTIONS)) != EOF) {
886 switch (c) {
888 default:
889 usage();
890 break;
892 case 'F': /* Only display available FStypes */
893 fstypes_only = B_TRUE;
894 break;
896 #if PARSABLE_OUTPUT
897 case 'P': /* Parsable output */
898 dispflag |= DISP_RAW;
899 break;
900 #endif /* PARSABLE_OUTPUT */
902 case 'T': /* Timestamp */
903 if (optarg) {
904 if (strcmp(optarg, "u") == 0) {
905 timestamp_fmt = UDATE;
906 } else if (strcmp(optarg, "d") == 0) {
907 timestamp_fmt = DDATE;
911 /* If it was never set properly... */
912 if (timestamp_fmt == NODATE) {
913 (void) fprintf(stderr, gettext("%s: -T option "
914 "requires either 'u' or 'd'\n"), cmdname);
915 usage();
917 break;
919 case 'a':
920 set_dispfunc(&dfunc, attr_display);
921 break;
923 case 'f':
924 set_dispfunc(&dfunc, vop_display);
925 break;
927 case 'i':
928 set_dispfunc(&dfunc, io_display);
929 break;
931 case 'n':
932 set_dispfunc(&dfunc, naming_display);
933 break;
935 case 'v':
936 set_dispfunc(&dfunc, vm_display);
937 break;
941 #if PARSABLE_OUTPUT
942 if ((dispflag & DISP_RAW) && (timestamp_fmt != NODATE)) {
943 (void) fprintf(stderr, gettext(
944 "-P and -T options are mutually exclusive\n"));
945 usage();
947 #endif /* PARSABLE_OUTPUT */
949 /* Gather the list of filesystem types */
950 if ((nfstypes = build_fstype_list(&fstypes)) == 0) {
951 (void) fprintf(stderr,
952 gettext("Can't build list of fstypes\n"));
953 exit(1);
956 nentities = parse_operands(
957 argc, argv, optind, &interval, &count, &entities);
958 forever = count == MAXLONG;
959 period_n = (hrtime_t)interval * NANOSEC;
961 if (nentities == -1) /* Set of operands didn't parse properly */
962 usage();
964 if ((nentities == 0) && (fstypes_only == B_FALSE)) {
965 (void) fprintf(stderr, gettext(
966 "Must specify -F or at least one fstype or mount point\n"));
967 usage();
970 if ((nentities > 0) && (fstypes_only == B_TRUE)) {
971 (void) fprintf(stderr, gettext(
972 "Cannot use -F with fstypes or mount points\n"));
973 usage();
977 * If we had no operands (except for interval/count) and we
978 * requested FStypes only (-F), then fill in the entities[]
979 * array with all available fstypes.
981 if ((nentities == 0) && (fstypes_only == B_TRUE)) {
982 if ((entities = calloc(nfstypes, sizeof (entity_t))) == NULL) {
983 perror("calloc() fstype stats");
984 exit(1);
987 for (i = 1; i < nfstypes; i++) {
988 if (fstypes[i]) {
989 entities[nentities].e_name = strdup(fstypes[i]);
990 nentities++;
995 set_ksnames(entities, nentities, fstypes, nfstypes);
997 if ((kc = kstat_open()) == NULL) {
998 perror("kstat_open");
999 exit(1);
1002 /* Set start time */
1003 start_n = gethrtime();
1005 /* Initial timestamp */
1006 if (timestamp_fmt != NODATE) {
1007 print_timestamp(timestamp_fmt);
1008 linesout++;
1012 * The following loop walks through the entities[] list to "prime
1013 * the pump"
1015 for (j = 0, printhdr = 1; j < nentities; j++) {
1016 entity_t *ent = &entities[j];
1017 vopstats_t *vsp = &ent->e_vs[CUR_INDEX];
1018 kstat_t *ksp = NULL;
1020 if (get_vopstats(kc, ent->e_ksname, vsp, &ksp) == 0) {
1021 (*dfunc)(ent->e_name, NULL, vsp,
1022 dispflag_policy(printhdr, dispflag));
1023 linesout++;
1024 } else {
1026 * If we can't find it the first time through, then
1027 * get rid of it.
1029 entities[j].e_ksname[0] = 0;
1032 * If we're only displaying FStypes (-F) then don't
1033 * complain about any file systems that might not
1034 * be loaded. Otherwise, let the user know that
1035 * they chose poorly.
1037 if (fstypes_only == B_FALSE) {
1038 (void) fprintf(stderr, gettext(
1039 "No statistics available for %s\n"),
1040 entities[j].e_name);
1043 printhdr = 0;
1046 if (count > 1)
1047 /* Set up signal handler for SIGCONT */
1048 if (signal(SIGCONT, cont_handler) == SIG_ERR)
1049 fail(1, "signal failed");
1052 BUMP_INDEX(); /* Swap the previous/current indices */
1053 i = 1;
1054 while (forever || i++ <= count) {
1056 * No telling how many lines will be printed in any interval.
1057 * There should be a minimum of HEADERLINES between any
1058 * header. If we exceed that, no big deal.
1060 if (linesout > HEADERLINES) {
1061 linesout = 0;
1062 printhdr = 1;
1064 /* Have a kip */
1065 sleep_until(&start_n, period_n, forever, &caught_cont);
1067 if (timestamp_fmt != NODATE) {
1068 print_timestamp(timestamp_fmt);
1069 linesout++;
1072 for (j = 0, nentities_found = 0; j < nentities; j++) {
1073 entity_t *ent = &entities[j];
1076 * If this entry has been cleared, don't attempt
1077 * to process it.
1079 if (ent->e_ksname[0] == 0) {
1080 continue;
1083 if (get_vopstats(kc, ent->e_ksname,
1084 &ent->e_vs[CUR_INDEX], NULL) == 0) {
1085 (*dfunc)(ent->e_name, &ent->e_vs[PREV_INDEX],
1086 &ent->e_vs[CUR_INDEX],
1087 dispflag_policy(printhdr, dispflag));
1088 linesout++;
1089 nentities_found++;
1090 } else {
1091 if (ent->e_type == ENTYPE_MNTPT) {
1092 (void) printf(gettext(
1093 "<<mount point no longer "
1094 "available: %s>>\n"), ent->e_name);
1095 } else if (ent->e_type == ENTYPE_FSTYPE) {
1096 (void) printf(gettext(
1097 "<<file system module no longer "
1098 "loaded: %s>>\n"), ent->e_name);
1099 } else {
1100 (void) printf(gettext(
1101 "<<%s no longer available>>\n"),
1102 ent->e_name);
1104 /* Disable this so it doesn't print again */
1105 ent->e_ksname[0] = 0;
1107 printhdr = 0; /* Always shut this off */
1109 BUMP_INDEX(); /* Bump the previous/current indices */
1112 * If the entities we were observing are no longer there
1113 * (file system modules unloaded, file systems unmounted)
1114 * then we're done.
1116 if (nentities_found == 0)
1117 break;
1120 return (0);