#include "config.h"
#include "xsysstats.h"
#include "headers.h"
#ifdef __DGUX__
#define signed int
#endif
#ifndef HAVE_RSTAT
#ifndef HAVE_CALLRPC
#error "Neither rstat nor callrpc available"
#endif
#ifndef HAVE_XDR_STATSTIME
#error "Neither rstat nor xdr_statstime available"
#endif
int rstat(char *host, struct statstime *stats)
{
return callrpc(host, RSTATPROG, RSTATVERS_TIME, RSTATPROC_STATS,
(xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_statstime,
(char *) stats);
}
#endif /* HAVE_RSTAT */
#ifdef USE_NEW_RSTAT
#include <rpc/rpc.h>
int
rstat_local(struct Host_Info *host, struct statstime *statval)
{
enum clnt_stat stat;
struct timeval timeout;
int onetry=0;
/* Reopen the connection. We could probably come up with a smarter
* method of backoff's, but trying to reopen ever time if it closed down
* probably isn't too costly.
*/
reopen:
if (host->client==NULL) {
int val;
val=open_host(host);
if (val==-1) {
host->client=NULL;
return -1;
}
}
if ((stat = clnt_call(host->client, RSTATPROC_STATS, (xdrproc_t)xdr_void,
NULL, (xdrproc_t) xdr_statstime,
(char *) statval, timeout))!=RPC_SUCCESS) {
/* could perhaps do something more useful here */
fprintf(stderr,"%s: clnt_call unsuccessfull, error %s\n", host->name, clnt_sperrno(stat));
auth_destroy(host->client->cl_auth);
clnt_destroy(host->client);
host->client=NULL;
/* It seems that we periodically loose the connection (not sure why -
* maybe things aren't guarentted to wrok long term) If so, lets try
* to reopen, but only try once (otherwise, we could get in an endless
* loop.
*/
}
return stat;
}
#else
int rstat_local(struct Host_Info *host, struct statstime *statval)
{
return rstat(host->name, statval);
}
#endif
struct statstime *our_stats;
static int *rstat_errors;
static int new_stat=0;
#ifdef USE_NEW_RSTAT
int open_host(struct Host_Info *host)
{
struct timeval timeout;
timeout.tv_sec=1;
timeout.tv_usec=0;
#if 1
if (!(host->client = clnt_create(host->name, RSTATPROG, RSTATVERS_TIME, "udp"))) {
/* fprintf(stderr,"Could not open rpc connection to %s via udp\n", host->name);*/
return -1;
}
#else
if (!(host->client = clnt_create_timed(host->name, RSTATPROG, RSTATVERS_TIME, "udp",&timeout))) {
fprintf(stderr,"Could not open rpc connection to %s via udp\n", host->name);
return -1;
}
#endif
if (!clnt_control(host->client, CLSET_TIMEOUT, (char*)&timeout)) {
/* fprintf(stderr,"could not set default timeout for %s\n", host);*/
auth_destroy(host->client->cl_auth);
clnt_destroy(host->client);
return -1;
}
if (!clnt_control(host->client, CLSET_RETRY_TIMEOUT, (char*)&timeout)) {
/* fprintf(stderr,"could not set default retry timeout for %s\n", host);*/
auth_destroy(host->client->cl_auth);
clnt_destroy(host->client);
return -1;
}
return 0;
}
#endif
/* set_first_values allocates the space to actually store the values,
* add then reads in a first set into them.
*/
void set_first_values()
{
int i;
our_stats = (struct statstime *)
malloc(sizeof(struct statstime) * num_hosts * 2);
rstat_errors = malloc(sizeof(int) * num_hosts*2);
for (i=0; i<num_hosts; i++) {
#ifdef USE_NEW_RSTAT
hosts[i].client=NULL;
open_host(&hosts[i]);
#endif
rstat_errors[i*2+new_stat] = rstat_local(&hosts[i], &our_stats[i*2 + new_stat]);
}
new_stat++;
new_stat %=2;
}
/* This sets the scale of linked graphs to the same value. The highest
* scale value is used. The graph argument that is passed is the graph
* value that has just changed. It may or may not be more efficient to wait
* until all the graphs have changed scale before going through and
* synchronizing the other graphs to match. It depends on the circumstances.
* of rescaling.
*
* It is assumed that as part of the set up, multiple links are all set
* to the first element. That is, if graph 0 is cpu, and graph 3 links
* to graph 0 (thus, graphs[3].link = 0) and graph 4 links to graph 3,
* then graph[4].link = 0 (the graph that graph 3 is linked to.) This
* makes things much simpler here, and is very little cost to do at startup.
*
*/
static void sync_scales(struct graph_info graph)
{
int max=0,num_match=0,i;
for (i=0; i<num_graphs; i++)
if (graphs[i].link == graph.link) {
num_match++;
if (graphs[i].true_scale>max)
max=graphs[i].true_scale;
}
/* If this graph is linked, clear the redraw flag, and only
* set it if we change the scale of any of the graphs. If
* this graph is not linked, then certainly rescale.
*/
if (num_match>1) {
windows[graph.window]->redraw_needed=FALSE;
for (i=0; i<num_graphs; i++)
if (graphs[i].link == graph.link) {
if (graphs[i].scale!=max) {
windows[graphs[i].window]->redraw_needed=TRUE;
graphs[i].scale = max;
}
}
}
else
windows[graph.window]->redraw_needed=TRUE;
}
void set_values()
{
int i,newstat,oldstat;
static int num_ticks=0;
num_ticks++;
for (i=0; i<num_hosts; i++)
rstat_errors[i*2 + new_stat] =
rstat_local(&hosts[i], &our_stats[i*2 + new_stat]);
new_stat++;
new_stat %= 2;
point_pos++;
point_pos %= split_width;
for (i=0; i<num_graphs; i++) {
newstat = graphs[i].host_offset * 2 + ((1 + new_stat) % 2);
oldstat = graphs[i].host_offset * 2 + new_stat;
/* If we got an error this time, data is meaningless, so set values to
* 0. If we got an error last time, we can get an accurate difference
* (entries in rstat structure will be zeroed), so also set values
* to zero.
*/
if (rstat_errors[graphs[i].host_offset*2]!=RPC_SUCCESS ||
rstat_errors[graphs[i].host_offset*2+1]!=RPC_SUCCESS) {
/* fprintf(stderr,"Have no valid data for host %s\n", hosts[graphs[i].host_offset].name);*/
points[i][point_pos]=0;
}
else { switch (graphs[i].type) {
case CPU:
case ICPU: {
int j[4],k;
for (k=0; k<4; k++)
j[k] = our_stats[newstat].cp_time[k] -
our_stats[oldstat].cp_time[k];
if (j[3] == 0) points[i][point_pos] = 100;
else points[i][point_pos] = 100 -
(100 * j[3]) / (j[0]+j[1]+j[2]+j[3]);
break;
}
case UCPU: {
int j[4],k;
for (k=0; k<4; k++)
j[k] = our_stats[newstat].cp_time[k] -
our_stats[oldstat].cp_time[k];
points[i][point_pos] = 100*j[0]/(j[0]+j[1]+j[2]+j[3]);
break;
}
case NICECPU: {
int j[4],k;
for (k=0; k<4; k++)
j[k] = our_stats[newstat].cp_time[k] -
our_stats[oldstat].cp_time[k];
points[i][point_pos] = 100*j[1]/(j[0]+j[1]+j[2]+j[3]);
break;
}
case SCPU: {
int j[4],k;
for (k=0; k<4; k++)
j[k] = our_stats[newstat].cp_time[k] -
our_stats[oldstat].cp_time[k];
points[i][point_pos] = 100*j[2]/(j[0]+j[1]+j[2]+j[3]);
break;
}
case IPACKETS:
points[i][point_pos] = (our_stats[newstat].if_ipackets
- our_stats[oldstat].if_ipackets) / sleep_time;
break;
case OPACKETS:
points[i][point_pos] = (our_stats[newstat].if_opackets
- our_stats[oldstat].if_opackets) / sleep_time;
break;
case PACKETS:
points[i][point_pos] = (our_stats[newstat].if_ipackets +
our_stats[newstat].if_opackets -
our_stats[oldstat].if_opackets
- our_stats[oldstat].if_ipackets) / sleep_time;
break;
case PAGE:
points[i][point_pos] = our_stats[newstat].v_pgpgin +
our_stats[newstat].v_pgpgout -
our_stats[oldstat].v_pgpgin -
our_stats[oldstat].v_pgpgout;
break;
case PAGEI:
points[i][point_pos] = our_stats[newstat].v_pgpgin
- our_stats[oldstat].v_pgpgin;
break;
case APAGEI:
points[i][point_pos] = (our_stats[newstat].v_pgpgin
- our_stats[oldstat].v_pgpgin)/sleep_time;
break;
case PAGEO:
points[i][point_pos] = our_stats[newstat].v_pgpgout
- our_stats[oldstat].v_pgpgout;
break;
case APAGEO:
points[i][point_pos] = (our_stats[newstat].v_pgpgout
- our_stats[oldstat].v_pgpgout)/sleep_time;
break;
case SWAP:
points[i][point_pos] = our_stats[newstat].v_pswpin +
our_stats[newstat].v_pswpout -
our_stats[oldstat].v_pswpin -
our_stats[oldstat].v_pswpout;
break;
case SWAPI:
points[i][point_pos] = our_stats[newstat].v_pswpin
- our_stats[oldstat].v_pswpin;
break;
case SWAPO:
points[i][point_pos] = our_stats[newstat].v_pswpin
- our_stats[oldstat].v_pswpin;
break;
case INT:
points[i][point_pos] = ((signed)our_stats[newstat].v_intr
- (signed)our_stats[oldstat].v_intr)/sleep_time;
if (points[i][point_pos] < 0)
points[i][point_pos] = 0;
break;
case DISK:
points[i][point_pos] = (our_stats[newstat].dk_xfer[0]
+our_stats[newstat].dk_xfer[1]
+our_stats[newstat].dk_xfer[2]
+our_stats[newstat].dk_xfer[3]
-our_stats[oldstat].dk_xfer[0]
-our_stats[oldstat].dk_xfer[1]
-our_stats[oldstat].dk_xfer[2]
-our_stats[oldstat].dk_xfer[3])
/sleep_time;
break;
case CONTEXT:
points[i][point_pos] = (our_stats[newstat].v_swtch
- our_stats[oldstat].v_swtch)/sleep_time;
break;
/* Load averages get handles a bit differently. By default,
* dividing the avenrun[] by 256 gets you the load average.
* However, often load average is fairly low - under 5.
* This does not give very good resolution in decimal form.
* So it is multiplied by LOAD_FACTOR and then divided.
* For scale, the same thing is true. When display the
* legend at the bottom of the screen, scale is then
* divided by LOAD_FACTOR. If LOAD_FACTOR is 100, is
* effectively keeps two decimal points, making for
* a fairly fine graph.
* graphs[i].scale_mult has the same value as LOAD_FACTOR
* for the load average graphs.
*/
case LOAD1:
points[i][point_pos] =
(our_stats[newstat].avenrun[0] * LOAD_FACTOR) / FSCALE;
break;
case LOAD5:
points[i][point_pos] =
(our_stats[newstat].avenrun[1] * LOAD_FACTOR) / FSCALE;
break;
case LOAD15:
points[i][point_pos] =
(our_stats[newstat].avenrun[2] * LOAD_FACTOR) / FSCALE;
break;
case COLL:
points[i][point_pos] = our_stats[newstat].if_collisions
- our_stats[oldstat].if_collisions;
break;
case ERRORS:
points[i][point_pos] = our_stats[newstat].if_ierrors
- our_stats[oldstat].if_ierrors;
break;
default:
fprintf(stderr,"Unknown graph type: %d, graph %d\n",graphs[i].type, i);
}
}
if (points[i][point_pos]<0) points[i][point_pos]=0;
graphs[i].running_avg += points[i][point_pos] -
graphs[i].running_avg;
/* Check the max value. If it's new, make a note to redraw the
* graph to reflect it (if max value display is on).
*/
if (points[i][point_pos] > graphs[i].max_val) {
extern int show_max;
graphs[i].max_val = points[i][point_pos];
if (show_max)
windows[graphs[i].window]->redraw_needed = TRUE;
}
/* Check to see if the graph needs to be downscaled. Only actually
* attempt to do downscaling once every 10 ticks. This is to
* prevent excessive runthroughs of the points array, finding
* the max value it holds.
* We do the check on true_scale. IF graphs are synchronized,
* there is no point seeing if we can downscale when we know
* that we should, but another graph has a higher scale.
*/
if (!(num_ticks % DOWNSCALE_OCCURANCE) &&
graphs[i].running_avg<graphs[i].true_scale*graphs[i].scale_mult
&& graphs[i].true_scale>graphs[i].min_scale) {
int k,max=0;
for (k=0; k<split_width; k++)
if (points[i][k]>max) max=points[i][k];
if (graphs[i].true_scale*graphs[i].scale_mult / 2 > max) {
while (graphs[i].running_avg<graphs[i].true_scale * graphs[i].scale_mult &&
graphs[i].true_scale * graphs[i].scale_mult/2 > max &&
graphs[i].true_scale > graphs[i].min_scale)
graphs[i].true_scale /=2;
if (graphs[i].true_scale < graphs[i].min_scale)
graphs[i].true_scale = graphs[i].min_scale;
graphs[i].scale = graphs[i].true_scale;
windows[graphs[i].window]->redraw_needed=TRUE;
if (graphs[i].link != -1) sync_scales(graphs[i]);
}
}
if (points[i][point_pos]>graphs[i].true_scale*graphs[i].scale_mult &&
graphs[i].true_scale<graphs[i].max_scale) {
do {
graphs[i].true_scale *= 2;
} while (points[i][point_pos]>=graphs[i].true_scale * graphs[i].scale_mult &&
graphs[i].true_scale < graphs[i].max_scale);
if (graphs[i].true_scale>graphs[i].max_scale)
graphs[i].true_scale = graphs[i].max_scale;
graphs[i].scale = graphs[i].true_scale;
windows[graphs[i].window]->redraw_needed=TRUE;
if (graphs[i].link != -1) sync_scales(graphs[i]);
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1