/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* rstat service: built with rstat.x
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <utmpx.h>
#include <nlist.h>
#include <fcntl.h>
#include <syslog.h>
#include <kstat.h>
#include <errno.h>
#include <sys/sysmacros.h>
#include "rstat.h"
#include "rstat_v2.h"
typedef struct {
} _cpu_stats_t;
/*
* system and cpu stats
*/
static int ncpus;
static int hz;
/*
* network interface stats
*/
typedef struct mib_item_s {
long group;
long mib_id;
long length;
char *valp;
} mib_item_t;
/*
* disk stats
*/
struct diskinfo {
};
static int ndisks;
/*
* net stats
*/
struct netinfo {
};
static int nnets;
/*
* Define EXIT_WHEN_IDLE if you are able to have this program invoked
* automatically on demand (as from inetd). When defined, the service
* will terminated after being idle for 120 seconds.
*/
#ifdef EXIT_WHEN_IDLE
#endif /* def EXIT_WHEN_IDLE */
/* V2 support for backwards compatibility to pre-5.0 systems */
static int stat_is_init = 0;
static void fail(int, char *, ...);
static void safe_zalloc(void **, int, int);
static void *safe_kstat_data_lookup(kstat_t *, char *);
static void system_stat_init(void);
static int system_stat_load(void);
static void init_disks(void);
static int diskinfo_load(void);
static void init_net(void);
static int netinfo_load(void);
static void updatestat(int);
static int mibopen(void);
static void
stat_init(void)
{
stat_is_init = 1;
/*
* Preallocate minimal set of drive entries.
*/
(int *)calloc(RSTAT_DK_NDRIVE, sizeof (int));
}
init_disks();
init_net();
/*
* To get the boot time, use utmpx, which is per-zone, but fall back
* to the system-wide kstat if utmpx is hosed for any reason.
*/
else {
}
endutxent();
updatestat(0);
alarm(1);
}
statsvar *
void *argp;
{
if (! stat_is_init)
stat_init();
#ifdef EXIT_WHEN_IDLE
sincelastreq = 0;
#endif
return (&stats_s4);
}
void *argp;
{
if (! stat_is_init)
stat_init();
#ifdef EXIT_WHEN_IDLE
sincelastreq = 0;
#endif
return (&stats_s3);
}
void *argp;
{
if (! stat_is_init)
stat_init();
#ifdef EXIT_WHEN_IDLE
sincelastreq = 0;
#endif
return (&stats_s2);
}
uint_t *
void *argp;
{
}
uint_t *
void *argp;
{
if (! stat_is_init)
stat_init();
#ifdef EXIT_WHEN_IDLE
sincelastreq = 0;
#endif
return (&have);
}
uint_t *
void *argp;
{
}
void
{
extern int _rpcpmstart; /* Started by a port monitor ? */
extern int _rpcsvcdirty; /* Still serving ? */
#ifdef DEBUG
#endif
#ifdef EXIT_WHEN_IDLE
#ifdef DEBUG
#endif
exit(0);
}
sincelastreq++;
#endif /* def EXIT_WHEN_IDLE */
(void) alarm(0);
#ifdef DEBUG
#endif
(void) kstat_chain_update(kc);
init_disks();
init_net();
}
#ifdef DEBUG
#endif
/* current time */
/* swtch not in V1 */
#ifdef DEBUG
"pgin: %d pgout: %d swpin: %d swpout: %d intr: %d swtch: %d\n",
#endif
/*
* V2 and V3 of rstat are limited to RSTAT_DK_NDRIVE drives
*/
RSTAT_DK_NDRIVE * sizeof (int));
RSTAT_DK_NDRIVE * sizeof (int));
#ifdef DEBUG
#endif
/* no s2 opackets */
#ifdef DEBUG
#endif
alarm(1);
}
/* --------------------------------- MIBGET -------------------------------- */
static mib_item_t *
{
int flags;
int j, getcode;
flags = 0;
perror("mibget: putmsg(ctl) failed");
goto error_exit;
}
/*
* each reply consists of a ctl part for one fixed structure
* or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
* len is the size of the data part of the message.
*/
/*CSTYLED*/
for (j = 1; ; j++) {
flags = 0;
if (getcode == -1) {
#ifdef DEBUG_MIB
perror("mibget getmsg(ctl) failed");
i = 0;
#endif /* DEBUG_MIB */
goto error_exit;
}
if (getcode == 0 &&
#ifdef DEBUG_MIB
"mibget getmsg() %d returned EOD (level %d, name %d)\n",
#endif /* DEBUG_MIB */
return (first_item); /* this is EOD msg */
}
#ifdef DEBUG_MIB
"mibget %d gives T_ERROR_ACK: TLI_error = 0x%x, UNIX_error = 0x%x\n",
#endif /* DEBUG_MIB */
goto error_exit;
}
#ifdef DEBUG_MIB
"mibget getmsg(ctl) %d returned %d, ctlbuf.len = %d, PRIM_type = %d\n",
"T_OPTMGMT_ACK: MGMT_flags = 0x%x, req->len = %d\n",
#endif /* DEBUG_MIB */
goto error_exit;
}
if (!temp) {
perror("mibget malloc failed");
goto error_exit;
}
if (last_item)
else
first_item = temp;
#ifdef DEBUG_MIB
"msg %d: group = %4d mib_id = %5d length = %d\n",
#endif /* DEBUG_MIB */
flags = 0;
if (getcode == -1) {
perror("mibget getmsg(data) failed");
goto error_exit;
} else if (getcode != 0) {
"mibget getmsg(data) returned %d, databuf.maxlen = %d, databuf.len = %d\n",
goto error_exit;
}
}
while (first_item) {
}
}
return (first_item);
}
static int
mibopen(void)
{
int sd;
/* gives us ip w/ arp on top */
if (sd == -1) {
perror("arp open");
return (-1);
}
perror("tcp I_PUSH");
return (-1);
}
perror("udp I_PUSH");
return (-1);
}
return (sd);
}
static char *
{
int i;
char *cp;
if (op)
switch (code) {
case 'd':
break;
case 'a':
break;
case 'h':
default:
cp += 3;
break;
}
cp--;
*cp = '\0';
return (buf);
}
static void
{
if (do_perror)
exit(2);
}
static void
{
}
{
if (kstat_chain_id == -1)
return (kstat_chain_id);
}
kstat_t *
char *ks_name)
{
fail(0, "kstat_lookup('%s', %d, '%s') failed",
return (ksp);
}
void *
{
fail(0, "kstat_data_lookup('%s', '%s') failed",
}
return (fp);
}
/*
* Get various KIDs for subsequent system_stat_load operations.
*/
static void
system_stat_init(void)
{
int i, nvmks;
/*
* Global statistics
*/
"avenrun_1min");
"avenrun_5min");
"avenrun_15min");
/*
* Per-CPU statistics
*/
ncpus = 0;
ncpus++;
1);
ncpus = 0;
1);
else
fail(0, "couldn't find per-CPU VM statistics");
ncpus++;
}
if (ncpus == 0)
fail(0, "couldn't find per-CPU statistics");
}
/*
* load statistics, summing across CPUs where needed
*/
static int
system_stat_load(void)
{
int i, j;
/*
* Global statistics
*/
/*
* Per-CPU statistics.
*/
for (i = 0; i < ncpus; i++) {
return (1);
if (i == 0) {
1);
} else {
/*
* Other CPUs' statistics are accumulated in
* cpu_stats_all, initialized at the first iteration of
* the loop.
*/
}
}
return (0);
}
static int
{
int cmp;
if (cmp != 0)
return (cmp);
if (cmp != 0)
return (cmp);
}
static void
init_disks(void)
{
ndisks = 0;
/*
* Patch the snip in the diskinfo list (see below)
*/
if (snip)
continue;
else {
sizeof (struct diskinfo), 0);
}
/*
* Insertion sort on (ks_module, ks_instance, ks_name)
*/
}
ndisks++;
}
/*
* Put a snip in the linked list of diskinfos. The idea:
* If there was a state change such that now there are fewer
* disks, we snip the list and retain the tail, rather than
* freeing it. At the next state change, we clip the tail back on.
*/
ndisks * sizeof (int), 1);
}
}
static int
diskinfo_load(void)
{
int i;
return (1);
}
return (0);
}
static void
init_net(void)
{
static int sd;
if (sd) {
}
while (netstat_item) {
item = netstat_item;
}
}
if (sd == -1) {
#ifdef DEBUG
#endif
sd = 0;
} else {
#ifdef DEBUG
#endif
sd = 0;
}
}
#ifdef DEBUG
#endif
nnets = 0;
if (netsnip)
#ifdef DEBUG_MIB
"Group = %d, mib_id = %d, length = %d, valp = 0x%x\n",
#endif
continue;
#ifdef DEBUG
#endif
continue;
/*
* We found a device of interest.
* Now, let's see if there's a kstat for it.
* First we try to query the "link" kstats in case
* the link is renamed. If that fails, fallback
* to legacy ktats for those non-GLDv3 links.
*/
continue;
}
continue;
continue;
else {
sizeof (struct netinfo), 0);
}
"ipackets");
"opackets");
"ierrors");
"oerrors");
"collisions");
/*
* Insertion sort on the name
*/
}
nnets++;
}
#ifdef DEBUG
#endif
}
/*
* Put a snip in the linked list of netinfos. The idea:
* If there was a state change such that now there are fewer
* nets, we snip the list and retain the tail, rather than
* freeing it. At the next state change, we clip the tail back on.
*/
}
static int
netinfo_load(void)
{
if (netstat_item == NULL) {
#ifdef DEBUG
#endif
return (0);
}
stats_s4.if_collisions = 0;
return (1);
if (net->collisions)
}
#ifdef DEBUG
"ipackets: %d opackets: %d ierrors: %d oerrors: %d colls: %d\n",
#endif
return (0);
}
static void
{
if (fr)
} else {
dst->ks_data_size = 0;
}
}