/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#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 <values.h>
#include <poll.h>
#include <ctype.h>
#include <libintl.h>
#include <locale.h>
#include <signal.h>
#include "statcommon.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
#else
#endif
/* Time stamp values */
/*
* The following are used for the nicenum() function
*/
/*
* 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 {
} entity_t;
/* Types of entities (e_type) */
/* If more sub-one units are added, make sure to adjust ONE_INDEX above */
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 */
/*
* 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) {
(void) printf(
" 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) {
(void) printf(
" 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(" map addmap delmap getpag putpag pagio\n");
}
}
static void
{
if (dispflag & DISP_HEADER) {
(void) printf("getattr setattr getsec setsec\n");
}
}
static void
{
if (dispflag & DISP_HEADER) {
(void) printf(
"lookup creat remov link renam mkdir rmdir rddir symlnk rdlnk\n");
}
}
if (niceflag) \
if (niceflag) \
(void) printf("\n");
static void
{
if (niceflag) {
(void) printf(" operation #ops bytes\n");
}
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;
}
perror("kstat_chain_update");
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
{
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
{
int i;
int nfstype;
perror("sysfs(GETNFSTYP)");
return (0);
}
perror("calloc() fstypes");
return (0);
}
for (i = 1; i < nfstype; i++) {
perror("sysfs(GETFSTYP)");
return (0);
}
if (buf[0] == 0)
continue;
/* If this is part of the exception list, move on */
if (is_exception(buf))
continue;
perror("strdup() fstype name");
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,
{
return (nentities); /* None found, returns 0 */
/*
* We know exactly what the maximum number of entities is going
* to be: argc - optind
*/
perror("calloc() entities");
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;
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.
*/
perror("malloc() mount list");
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;
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 */
/* Now allocate the vopstats array */
perror("calloc() fstype vopstats");
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 */
perror("calloc() vopstats array");
exit(1);
}
}
}
/*
* 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;
long interval = 0;
extern int optind;
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#endif
(void) textdomain(TEXT_DOMAIN);
/* Don't let buffering interfere with piped output. */
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... */
if (timestamp_fmt == NODATE) {
"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.
*/
perror("calloc() fstype stats");
exit(1);
}
for (i = 1; i < nfstypes; i++) {
if (fstypes[i]) {
nentities++;
}
}
}
perror("kstat_open");
exit(1);
}
/* Set start time */
/* Initial timestamp */
if (timestamp_fmt != NODATE) {
linesout++;
}
/*
* 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"),
}
}
printhdr = 0;
}
if (count > 1)
/* Set up signal handler for SIGCONT */
BUMP_INDEX(); /* Swap the previous/current indices */
i = 1;
/*
* 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;
}
/* Have a kip */
if (timestamp_fmt != NODATE) {
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);
}