fsstat.c revision 13c7b6ac430bc41eee84ba0b625cf5fde3241b10
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <kstat.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <limits.h>
#include <time.h>
#include <langinfo.h>
#include <values.h>
#include <poll.h>
#include <ctype.h>
#include <libintl.h>
#include <locale.h>
/*
* For now, parsable output is turned off. Once we gather feedback and
* stablize the output format, we'll turn it back on. This prevents
* the situation where users build tools which depend on a specific
* format before we declare the output stable.
*/
#define PARSABLE_OUTPUT 0
#if PARSABLE_OUTPUT
#define OPTIONS "FPT:afginv"
#else
#define OPTIONS "FT:afginv"
#endif
/* Time stamp values */
#define NODATE 0 /* Default: No time stamp */
/*
* The following are used for the nicenum() function
*/
#define KILO_VAL 1024
#define ONE_INDEX 3
/*
* structure. We only need two per entity and we can swap between them.
*/
/*
* An "entity" is anything we're collecting statistics on, it could
* be a mountpoint or an FS-type.
* e_name is the name of the entity (e.g. mount point or FS-type)
* e_ksname is the name of the associated kstat
* e_vs is an array of vopstats. This is used to keep track of "previous"
* and "current" vopstats.
*/
typedef struct entity {
char *e_name; /* name of entity */
int e_type; /* type of entity */
} entity_t;
/* Types of entities (e_type) */
#define ENTYPE_UNKNOWN 0 /* UNKNOWN must be zero since we calloc() */
#define ENTYPE_FSTYPE 1
#define ENTYPE_MNTPT 2
/* If more sub-one units are added, make sure to adjust ONE_INDEX above */
static char units[] = "num KMGTPE";
static char *cmdname; /* name of this command */
static int vs_i = 0; /* Index of current vs[] slot */
static void
usage()
{
"Usage: %s [-a|f|i|n|v] [-T d|u] {-F | {fstype | fspath}...} "
"[interval [count]]\n"), cmdname);
exit(2);
}
/*
* Given a 64-bit number and a starting unit (e.g., n - nanoseconds),
* convert the number to a 5-character representation including any
* decimal point and single-character unit. Put that representation
* into the array "buf" (which had better be big enough).
*/
char *
{
int unit_index;
int index;
char u;
if (unit == '\0')
unit = ' ';
unit_index = 0;
unit_index++;
return (buf);
}
}
index = 0;
while (n >= KILO_VAL) {
index++;
unit_index++;
}
return (buf);
}
u = units[unit_index];
if (unit_index == ONE_INDEX) {
} else {
}
return (buf);
}
(isnice) ? \
: \
/* Values for display flag */
#define DISP_HEADER 0x1
#define DISP_RAW 0x2
/*
* The policy for dealing with multiple flags is dealt with here.
* Currently, if we are displaying raw output, then don't allow
* headers to be printed.
*/
int
{
/* If we're not displaying raw output, then allow headers to print */
if (printhdr) {
dispflag |= DISP_HEADER;
}
}
return (dispflag);
}
static void
{
if (dispflag & DISP_HEADER) {
" new name name attr attr lookup rddir read read write write\n"
" file remov chng get set ops ops ops bytes ops bytes\n"));
}
}
static void
{
if (dispflag & DISP_HEADER) {
" read read write write rddir rddir rwlock rwulock\n"
" ops bytes ops bytes ops bytes ops ops\n"));
}
}
static void
{
if (dispflag & DISP_HEADER) {
(void) printf(
gettext(" map addmap delmap getpag putpag pagio\n"));
}
}
static void
{
if (dispflag & DISP_HEADER) {
}
}
static void
{
if (dispflag & DISP_HEADER) {
"lookup creat remov link renam mkdir rmdir rddir symlnk rdlnk\n"));
}
}
if (niceflag) \
if (niceflag) \
(void) printf("\n");
static void
{
if (niceflag) {
}
if (niceflag) {
/* Make it easier on the eyes */
(void) printf("\n");
} else {
}
}
/*
* Retrieve the vopstats. If kspp (pointer to kstat_t pointer) is non-NULL,
* then pass it back to the caller.
*
* Returns 0 on success, non-zero on failure.
*/
int
{
return (1);
errno = 0;
/* wait for a possibly up-to-date chain */
errno = 0;
continue;
}
exit(1);
}
return (1);
}
return (1);
}
if (kspp)
return (0);
}
/*
* Given a file system type name, determine if it's part of the
* exception list of file systems that are not to be displayed.
*/
int
is_exception(char *fsname)
{
char **xlp; /* Pointer into the exception list */
static char *exception_list[] = {
"specfs",
"fifofs",
"fd",
"swapfs",
"ctfs",
"objfs",
"nfsdyn",
};
return (1);
}
return (0);
}
/*
* Plain and simple, build an array of names for fstypes
* Returns 0, if it encounters a problem.
*/
int
build_fstype_list(char ***fstypep)
{
int i;
int nfstype;
return (0);
}
return (0);
}
for (i = 1; i < nfstype; i++) {
return (0);
}
if (buf[0] == 0)
continue;
/* If this is part of the exception list, move on */
if (is_exception(buf))
continue;
return (0);
}
}
return (i);
}
/*
* After we're done with getopts(), process the rest of the
* operands. We have three cases and this is the priority:
*
* 1) [ operand... ] interval count
* 2) [ operand... ] interval
* 3) [ operand... ]
*
* The trick is that any of the operands might start with a number or even
* be made up exclusively of numbers (and we have to handle negative numbers
* end of the list then we claim case 1. If we find only one operand at the
* end made up only of number, then we claim case 2. Otherwise, case 3.
* BTW, argc, argv don't change.
*/
int
int argc,
char **argv,
int optind,
long *interval,
long *count,
{
int nentities = 0; /* Number of entities found */
int out_of_range; /* Set if 2nd-to-last operand out-of-range */
return (nentities); /* None found, returns 0 */
/*
* We know exactly what the maximum number of entities is going
* to be: argc - optind
*/
return (-1);
}
char *endptr;
/* If we have more than two operands left to process */
continue;
}
/* If we're here, then we only have one or two operands left */
errno = 0;
out_of_range = 0;
/* Operand was not a number */
continue;
/* Operand was a number, just out of range */
out_of_range++;
}
/*
* The last operand we saw was a number. If it happened to
* be the last operand, then it is the interval...
*/
/* ...but we need to check the range. */
if (out_of_range) {
"interval must be between 1 and "
"%ld (inclusive)\n"), MAXLONG);
return (-1);
} else {
/*
* The value of the interval is valid. Set
* count to something really big so it goes
* virtually forever.
*/
break;
}
}
/*
* At this point, we *might* have the interval, but if the
* next operand isn't a number, then we don't have either
* the interval nor the count. Both must be set to the
* defaults. In that case, both the current and the previous
* operands are stat-able entities.
*/
errno = 0;
/*
* Faked out! The last operand wasn't a number so
* the current and previous operands should be
* stat-able entities. We also need to reset interval.
*/
*interval = 0;
"Both interval and count must be between 1 "
"and %ld (inclusive)\n"), MAXLONG);
return (-1);
}
break; /* Done! */
}
return (nentities);
}
/*
* set_mntpt() looks at the entity's name (e_name) and finds its
* mountpoint. To do this, we need to build a list of mountpoints
* if we don't need to look at any mountpoints.
* Returns 0 on success, non-zero if it couldn't find a mount-point.
*/
int
{
static struct mnt {
char *m_mntpt;
struct statvfs statvfsbuf;
return (1);
/* We only set up mnt_list the first time this is called */
return (1);
}
/*
* We insert at the front of the list so that when we
* search entries we'll have the last mounted entries
* first in the list so that we can match the longest
* mountpoint.
*/
return (1);
}
}
}
return (1);
}
/*
* Now that we have the path, walk through the mnt_list and
* look for the first (best) match.
*/
/* Can't statvfs so no match */
continue;
} else {
}
}
/* No match - Move on */
continue;
}
break;
}
}
"Can't find mount point for %s\n"), path);
return (1);
}
return (0);
}
/*
* We have an array of entities that are potentially stat-able. Using
* the name (e_name) of the entity, attempt to construct a ksname suitable
* for use by kstat_lookup(3kstat) and fill it into the e_ksname member.
*
* We check the e_name against the list of file system types. If there is
* no match then test to see if the path is valid. If the path is valid,
* then determine the mountpoint.
*/
void
{
int i, j;
struct statvfs statvfsbuf;
for (i = 0; i < nentities; i++) {
/* Check the name against the list of fstypes */
for (j = 1; j < nfstypes; j++) {
/* It's a file system type */
KSTAT_STRLEN, "%s%s",
/* Now allocate the vopstats array */
exit(1);
}
break;
}
}
if (j < nfstypes) /* Found it! */
continue;
/*
* If the entity in the exception list of fstypes, then
* null out the entry so it isn't displayed and move along.
*/
continue;
}
/* If we didn't find it, see if it's a path */
/* Error - Make sure the entry is nulled out */
continue;
}
gettext("Can't determine type of \"%s\"\n"),
} else {
}
/* Now allocate the vopstats array */
exit(1);
}
}
}
void
print_time(int type)
{
time_t t;
/* We only need to retrieve this once per invocation */
}
if (time(&t) != -1) {
(void) printf("%ld\n", t);
char dstr[64];
int len;
if (len > 0) {
}
}
}
}
/*
* The idea is that 'dspfunc' should only be modified from the default
* once since the display options are mutually exclusive. If 'dspfunc'
* only contains the default display function, then all is good and we
* can set it to the new display function. Otherwise, bail.
*/
void
{
if (*dspfunc != dflt_display) {
"%s: Display options -{a|f|i|n|v} are mutually exclusive\n"),
cmdname);
usage();
}
}
int
{
int c;
int i, j; /* Generic counters */
int nentities_found;
int linesout; /* Keeps track of lines printed */
int printhdr = 0; /* Print a header? 0 = no, 1 = yes */
int nfstypes; /* Number of fstypes */
int dispflag = 0; /* Flags for display control */
long count = 0; /* Number of iterations for display */
long interval = 0;
char **fstypes; /* Array of names of all fstypes */
int nentities; /* Number of stat-able entities */
extern int optind;
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#endif
(void) textdomain(TEXT_DOMAIN);
switch (c) {
default:
usage();
break;
case 'F': /* Only display available FStypes */
break;
#if PARSABLE_OUTPUT
case 'P': /* Parsable output */
break;
#endif /* PARSABLE_OUTPUT */
case 'T': /* Timestamp */
if (optarg) {
}
}
/* If it was never set properly... */
"%s: -T option requires either 'u' or 'd'\n"),
cmdname);
usage();
}
break;
case 'a':
break;
case 'f':
break;
case 'i':
break;
case 'n':
break;
case 'v':
break;
}
}
#if PARSABLE_OUTPUT
"-P and -T options are mutually exclusive\n"));
usage();
}
#endif /* PARSABLE_OUTPUT */
/* Gather the list of filesystem types */
gettext("Can't build list of fstypes\n"));
exit(1);
}
usage();
"Must specify -F or at least one fstype or mount point\n"));
usage();
}
"Cannot use -F with fstypes or mount points\n"));
usage();
}
/*
* requested FStypes only (-F), then fill in the entities[]
* array with all available fstypes.
*/
gettext("Can't calloc fstype stats\n"));
exit(1);
}
for (i = 1; i < nfstypes; i++) {
if (fstypes[i]) {
nentities++;
}
}
}
exit(1);
}
/*
* The following loop walks through the entities[] list to "prime
* the pump"
*/
linesout++;
} else {
/*
* If we can't find it the first time through, then
* get rid of it.
*/
/*
* If we're only displaying FStypes (-F) then don't
* complain about any file systems that might not
* be loaded. Otherwise, let the user know that
* he chose poorly.
*/
if (fstypes_only == B_FALSE) {
"No statistics available for %s\n"),
}
}
}
BUMP_INDEX(); /* Swap the previous/current indices */
for (i = 1; i <= count; i++) {
/*
* No telling how many lines will be printed in any interval.
* There should be a minimum of HEADERLINES between any
* header. If we exceed that, no big deal.
*/
if (linesout > HEADERLINES) {
linesout = 0;
printhdr = 1;
}
if (timestamp) {
linesout++;
}
for (j = 0, nentities_found = 0; j < nentities; j++) {
/*
* If this entry has been cleared, don't attempt
* to process it.
*/
continue;
}
linesout++;
} else {
"<<mount point no longer "
"<<file system module no longer "
} else {
"<<%s no longer available>>\n"),
}
/* Disable this so it doesn't print again */
}
printhdr = 0; /* Always shut this off */
}
BUMP_INDEX(); /* Bump the previous/current indices */
/*
* If the entities we were observing are no longer there
* (file system modules unloaded, file systems unmounted)
* then we're done.
*/
if (nentities_found == 0)
break;
}
return (0);
}