#include "xsysstats.h"
#include "patchlevel.h"
struct base_types {
char *cmd_option; /* how it is called on the command line */
char *window_name; /* what we call it on the window */
int def_scale; /* default scale for that graph type */
};
struct Command_Line_Options {
char *cmd_option; /* how it is called on the command line */
char num_args; /* Number of options for this command */
short pass; /* What pass this should be processed on. */
void (*func)(); /* function to call when we match this.
* if num_args is true, than that gets passed
* to the function, otherwise nothing is passed
*/
};
/* It is important that the n'th placement of this array corresponds
* to the n'th value type in xsysstats.h (ie, 9'th value corresponds to errors)
*/
struct base_types types[NUM_TYPES] = {
{"cpu", "cpu", 100},
{"packets", "pkts",32},
{"pagei", "pagei",16},
{"swapi", "iswap",16},
{"interrupts", "intr",200},
{"disk", "disk",40},
{"context", "ctxt",128},
{"load1", "load1",2},
{"load5", "load5", 2},
{"load15", "load15", 2},
{"collisions", "coll",8},
{"errors", "errs",8},
{"packetsi", "pktsi",32},
{"packetso", "pktso",32},
{"apagei", "apagei",16},
{"apageo", "apageo",16},
{"pageo", "pageo",16},
{"page", "page",16},
{"swapo", "swapo",16},
{"swap", "swap",16},
{"ucpu", "ucpu", 100},
{"ncpu", "ncpu", 100},
{"scpu", "scpu", 100},
{"icpu", "icpu", 100},
};
/* Various notes:
* we store the values in the 'points' variable. They are stored in the
* form we get from rstat. To figure out where the point should be plotted,
* it is rstat_value * vertical_size / scale.
*/
typedef enum {Nolabel, Minimal, Normal} LabelStyle;
LabelStyle label_style=Normal;
int screen_num, num_graphs=0, **points,point_pos=-1,sleep_time=2,
font_height=0, num_info_lines,
draw_baseline=FALSE,allnames=FALSE,
num_hosts=0,localhost=-1,
show_time=FALSE,
time_ticks=45, /* How many seconds a test entry to the
* ruler should be printed.
*/
split_x=1, split_y=1,
next_window=-1, /* Which window to put the next
* graph into
*/
num_windows_spec=0, /* How many graphs with unspecified
* windows have been processed
*/
split_width,split_height, /* Width and height of the windows.
* This is unadjusted height, which is the
* Total drawing area. The width and height
* stored in the Xss_Window structure is the
* line graph area, adjusted for labels
*/
*lasty, /* Contains the last y points for each
* graph (once the space is allocated.)
* just quickens and simplifies things.
*/
scale_sync=FALSE, /* True if all the graphs should have the
* same scale.
*/
solid=FALSE, /* True if drawing solid (not line) graph
*/
hide_hosts=FALSE, /* If true, don't display hostnames. */
show_max=FALSE, /* If true, show max value graph ever hit */
graph_spacing=INSIDE_BORDER_WIDTH;
/* How many pixels should be between each
* graph and the edge of the window.
*/
unsigned long whitepixel,blackpixel;
Display *display;
Window win;
XFontStruct *font_info;
GC gc;
XColor background, foreground,baseline;
Pixmap new_win; /* used for scrolling */
char *display_name=""; /* name to open the display with */
struct Xss_Window **windows=NULL;
struct Host_Info *hosts;
XSizeHints size_hints = { PMinSize | PWinGravity,0,0,DEFAULT_SIZE,
DEFAULT_SIZE,DEFAULT_SIZE, DEFAULT_SIZE, 0,0,0,0,{0,0},{0,0},0,0,
NorthWestGravity};
static unsigned long defcolors[NUM_TYPES];
char *window_name, *window_title=VERSION;
char *strdup_local(char *str)
{
char *s;
s = malloc(sizeof(char)*(strlen(str)+1));
strcpy(s,str);
return s;
}
int minimal_label_lines(int windowno)
{
int labelwidth=0, lines_needed=0,dummy,i;
char tmp[80];
XCharStruct scale_info;
/* First is to see how many lines we need for the labels. Use a 3
* pixel spacing between each element.
*/
if (windows[windowno]->title!=NULL) {
XTextExtents(font_info, windows[windowno]->title, strlen(windows[windowno]->title),
&dummy, &dummy, &dummy, &scale_info);
labelwidth = scale_info.width+3;
}
if (labelwidth > windows[windowno]->width) {
labelwidth=0;
lines_needed++;
}
for (i=0; i<num_graphs; i++) {
if (graphs[i].window!=windowno) continue;
if (show_max)
sprintf(tmp, "%d/%d", graphs[i].scale, graphs[i].max_val/graphs[i].scale_mult);
else
sprintf(tmp,"%d", graphs[i].scale);
XTextExtents(font_info, tmp, strlen(tmp),
&dummy, &dummy, &dummy, &scale_info);
labelwidth += scale_info.width+3;
if (labelwidth > windows[windowno]->width) {
labelwidth=scale_info.width+3;
lines_needed++;
}
}
/* if there is leftover, it will have to go on the next line. */
if (labelwidth) lines_needed++;
return lines_needed;
}
/* Draws the window label when in Minimal mode. */
void draw_minimal_label(int windowno)
{
int labelwidth=0, on_line=0,dummy,i;
char tmp[80];
XCharStruct scale_info;
/*
* First, draw the window title.
*/
if (windows[windowno]->title!=NULL) {
XTextExtents(font_info, windows[windowno]->title, strlen(windows[windowno]->title),
&dummy, &dummy, &dummy, &scale_info);
labelwidth = scale_info.width+3;
XSetForeground(display, gc, foreground.pixel);
XDrawImageString(display, win, gc,
windows[windowno]->x,
windows[windowno]->y + windows[windowno]->height + 1 +
font_height*show_time + font_info->max_bounds.ascent,
windows[windowno]->title, strlen(windows[windowno]->title));
}
if (labelwidth > windows[windowno]->width) {
labelwidth=0;
on_line++;
}
/*
* Now go through and draw each graph.
*/
for (i=0; i<num_graphs; i++) {
if (graphs[i].window!=windowno) continue;
if (show_max)
sprintf(tmp, "%d/%d", graphs[i].scale, graphs[i].max_val/graphs[i].scale_mult);
else
sprintf(tmp,"%d", graphs[i].scale);
XTextExtents(font_info, tmp, strlen(tmp),
&dummy, &dummy, &dummy, &scale_info);
if (labelwidth + scale_info.width+3> windows[windowno]->width) {
on_line++;
labelwidth=0;
}
XSetForeground(display, gc, graphs[i].color_pixel);
XDrawImageString(display, win, gc,
windows[windowno]->x + labelwidth,
windows[windowno]->y + windows[windowno]->height + 1 +
font_height * (show_time+on_line) +
font_info->max_bounds.ascent, tmp, strlen(tmp));
labelwidth += scale_info.width+3;
}
}
/* This function takes a window number as an argument, and re-draws that
* window.
*/
void redraw_graph(int windowno)
{
int i,pp,x,max_x,font_line=0,fontx=0,fonts_per_line,deltax,pp1,dummy;
char tmp[256];
XCharStruct scale_info;
XClearArea(display, win, windows[windowno]->x, windows[windowno]->y,
split_width, split_height, False);
windows[windowno]->redraw_needed=FALSE;
if (draw_baseline) {
XSetForeground(display, gc, baseline.pixel);
XDrawLine(display, win, gc, windows[windowno]->x,
windows[windowno]->y + windows[windowno]->height,
windows[windowno]->x + windows[windowno]->xpos,
windows[windowno]->y+windows[windowno]->height);
}
if (show_time) {
font_line = 1; /* so that the type/scale information is one
* line further down, leaving room for the rule.
*/
XSetForeground(display, gc, foreground.pixel);
for (i=0; i*time_ticks < windows[windowno]->width * sleep_time; i++) {
if ((time_ticks * i )%60 == 0)
sprintf(tmp, "%dm", time_ticks * i / 60);
else
sprintf(tmp, "%d", time_ticks * i);
XTextExtents(font_info, tmp, strlen(tmp),
&dummy, &dummy, &dummy, &scale_info);
x = windows[windowno]->x + windows[windowno]->width - 1 -
i*time_ticks/sleep_time;
XDrawImageString(display, win, gc, x - scale_info.width / 2,
windows[windowno]->y + windows[windowno]->height + 2 +
font_info->max_bounds.ascent,
tmp, strlen(tmp));
XDrawLine(display, win, gc, x,
windows[windowno]->y + windows[windowno]->height + 1, x,
windows[windowno]->y + windows[windowno]->height + 4);
}
}
if (label_style==Minimal) draw_minimal_label(windowno);
fonts_per_line = windows[windowno]->width / TEXT_WIDTH;
deltax = windows[windowno]->width / fonts_per_line;
for (i=0; i<num_graphs; i++) {
if (graphs[i].window!=windowno) continue;
XSetForeground(display, gc, graphs[i].color_pixel);
if (label_style==Normal) {
XDrawImageString(display, win, gc,
windows[windowno]->x + fontx * deltax,
windows[windowno]->y + windows[windowno]->height + 1 +
font_height*font_line + font_info->max_bounds.ascent,
graphs[i].name, graphs[i].name_len);
if (show_max)
sprintf(tmp, "%d/%d", graphs[i].scale, graphs[i].max_val/graphs[i].scale_mult);
else
sprintf(tmp,"%d", graphs[i].scale);
XTextExtents(font_info, tmp, strlen(tmp),
&x, &x, &x, &scale_info);
XDrawImageString(display, win, gc,
windows[windowno]->x + (fontx + 1) * deltax -
scale_info.width - 1,
windows[windowno]->y + windows[windowno]->height + 1 +
font_height * font_line + font_info->max_bounds.ascent,
tmp, strlen(tmp));
fontx++;
if (fontx==fonts_per_line) {
fontx=0;
font_line++;
}
}
if ( windows[windowno]->xpos<windows[windowno]->width) {
pp=0;
max_x = windows[windowno]->xpos-1;
}
else {
pp = (point_pos + 1) % split_width;
max_x = windows[windowno]->width;
}
for (x=0; x<max_x; x++) {
pp1 = (pp +1) % split_width;
if (solid) {
XFillRectangle(display,win, gc, x + windows[windowno]->x,
1+windows[windowno]->y + windows[windowno]->height -
points[i][pp] * windows[windowno]->height/
(graphs[i].scale * graphs[i].scale_mult),
1,
(points[i][pp] * windows[windowno]->height) /
(graphs[i].scale * graphs[i].scale_mult));
} else {
XDrawLine(display,win, gc, x + windows[windowno]->x,
windows[windowno]->y + windows[windowno]->height -
points[i][pp] * windows[windowno]->height/(graphs[i].scale *
graphs[i].scale_mult), x+windows[windowno]->x+1,
windows[windowno]->y + windows[windowno]->height -
(points[i][pp1] * windows[windowno]->height) /
(graphs[i].scale * graphs[i].scale_mult));
}
pp = pp1;
/* point_pos %2 is to draw it every other point. */
if (graphs[i].scale_lines>0 && !(pp % 2) &&
graphs[i].scale_lines<graphs[i].scale) {
int sline,slineinc=1;
while ((graphs[i].scale_lines*slineinc*
windows[windowno]->height / graphs[i].scale) <
PTSSCALELINE)
slineinc*=2;
sline = slineinc;
/* Yes, it is supposed to be just less than scale,
* not less than equal. No point drawing a line at
* the top of the window.
*/
while (sline*graphs[i].scale_lines < graphs[i].scale) {
XDrawPoint(display, win, gc, x + windows[windowno]->x,
windows[windowno]->y + windows[windowno]->height
- (sline * graphs[i].scale_lines *
windows[windowno]->height) /
graphs[i].scale + graph_spacing);
sline+=slineinc;
}
}
}
}
}
void redraw_all_graphs()
{
int count;
XClearWindow(display,win);
for (count=0;count<split_x*split_y; count++)
redraw_graph(count);
}
void set_graph_windows()
{
int fonts_per_line,x_pos,ypos;
split_width = (size_hints.width - graph_spacing*(split_x+1)) /
split_x;
split_height = (size_hints.height - graph_spacing*(split_y+1)) /
split_y;
fonts_per_line = split_width/TEXT_WIDTH;
for (x_pos=0; x_pos<split_x; x_pos++)
for (ypos=0; ypos<split_y; ypos++) {
int window_num = x_pos + ypos*split_x;
windows[window_num]->width = split_width-1;
windows[window_num]->x = x_pos * split_width +
graph_spacing*(x_pos+1);
windows[window_num]->label_lines =
windows[window_num]->num_graphs / fonts_per_line;
if (windows[window_num]->num_graphs >
windows[window_num]->label_lines*fonts_per_line)
windows[window_num]->label_lines++;
windows[window_num]->y = ypos * split_height +
graph_spacing*(ypos+1);
windows[window_num]->height = split_height;
if (label_style == Normal)
windows[window_num]->height -= font_height *
windows[window_num]->label_lines;
else if (label_style == Minimal) {
windows[window_num]->height -=
font_height*minimal_label_lines(window_num);
}
if (show_time)
windows[window_num]->height -= font_height;
}
}
void resize_window(XEvent event)
{
int i, tmp_split_width, redraw_needed=FALSE,tmp_split_height;
/* Width changes are a little tricky. We need
* malloc new structures, copy the data into
* it, and the free the old ones.
*/
tmp_split_width = (event.xconfigure.width - graph_spacing*(split_x+1)) /
split_x;
tmp_split_height = (event.xconfigure.height - graph_spacing*(split_y+1)) /
split_y;
if (split_width != tmp_split_width &&
event.xconfigure.width>=size_hints.min_width) {
int *new_points,q;
size_hints.width = event.xconfigure.width;
for (i=0; i<num_graphs; i++) {
new_points = (int *) malloc(sizeof(int) *
tmp_split_width);
/* If the window is larger, we need to copy all
* the old points.
*/
if (tmp_split_width>split_width) {
if ( windows[0]->xpos<split_width) {
for (q=0; q<=point_pos; q++)
new_points[q] = points[i][q];
}
else {
int z=0;
for (q=point_pos+1; q<=point_pos+split_width; q++)
new_points[z++]=
points[i][q%split_width];
}
}
/* if the new window is smaller, we only want to
* copy the newest points.
*/
else {
int z=0;
if (windows[0]->xpos<windows[0]->width) {
int xstart;
if (point_pos>tmp_split_width)
xstart=point_pos-
tmp_split_width;
else xstart=0;
for (q=xstart; q<=point_pos; q++)
new_points[z++]=points[i][q];
}
else {
for (q=0; q<tmp_split_width; q++)
new_points[z++]=
points[i][(q+split_width-
tmp_split_width)%
split_width];
}
}
free(points[i]);
points[i] = new_points;
}
/* If we had more points than the new window can display,
* then set the number of points we have to the max the
* window can handle.
* Otherwise, if the old window was filled up, set the
* next point to be drawn at the end of the old window.
* Remember, after the copy, the oldest point will be point
* #0.
* Otherwise, if the old window wasn't filled up and
* the number of points would not fill up the new
* window, keep the point_pos value the same.
*/
if (point_pos > tmp_split_width)
point_pos = tmp_split_width-1;
else if ( windows[0]->xpos>=split_width)
point_pos=split_width-1;
if (tmp_split_width< windows[0]->xpos) {
int tmp_cnt;
for (tmp_cnt=0; tmp_cnt<split_x*split_y; tmp_cnt++)
windows[tmp_cnt]->xpos=tmp_split_width;
}
redraw_needed = TRUE;
}
/* height change is simple. Just update our
* values, and the re-draw function will scale
* everything to the new height.
*/
if (tmp_split_height !=split_height &&
event.xconfigure.height>=size_hints.min_height) {
size_hints.height = event.xconfigure.height;
redraw_needed = TRUE;
}
/* This is true only if width or height actually
* changed in any way.
*/
set_graph_windows();
if (redraw_needed) {
redraw_all_graphs();
XFreePixmap(display, new_win);
new_win = XCreatePixmap(display, win, split_width+1,
split_height,
DefaultDepth(display, screen_num));
}
}
/* This function process various keypresses. If an unknown/unsupported
* key is pressed, it is just ignored. As of now, only 1 character
* codes are used, so only the first character of keypress holds any
* relevance.
*/
void process_keypress(char *keypress)
{
switch (keypress[0]) {
case 'l':
case 'L':
label_style++;
if (label_style > Normal) label_style=Nolabel;
set_graph_windows();
redraw_all_graphs();
break;
case 'm':
case 'M':
show_max = !(show_max);
redraw_all_graphs();
break;
case 'r':
case 'R':
show_time = !(show_time);
set_graph_windows();
redraw_all_graphs();
break;
case 12: /* C-l */
redraw_all_graphs();
break;
#if defined(KEYQUIT)
case 'q':
case 'Q':
XCloseDisplay(display);
exit(0);
#endif
}
}
void check_events()
{
XEvent event;
char keystring[10];
while (XEventsQueued(display,QueuedAfterFlush)) {
XNextEvent(display, &event);
switch (event.type) {
case Expose:
if (event.xexpose.count==0)
redraw_all_graphs();
break;
case ConfigureNotify:
resize_window(event);
break;
case KeyPress:
keystring[0]=0;
XLookupString((XKeyEvent *) &event, keystring,10, NULL, NULL);
process_keypress(keystring);
break;
/* We don't really care about these, but they might
* be generated with the StructureNotifyMask.
*/
case CirculateNotify:
case GravityNotify:
case MapNotify:
case ReparentNotify:
case UnmapNotify:
case MappingNotify:
break;
default:
printf("unknown event: %d\n", event.type);
}
}
}
/* This function updates the window. Call the redraw_graph function
* if the window is marked for redraw. Otherwise, scroll the
* window (if necessary). Then, draw the various lines.
*/
void update_window(int windowno)
{
int count;
if (windows[windowno]->redraw_needed) {
redraw_graph(windowno);
if ( windows[windowno]->xpos>windows[windowno]->width)
windows[windowno]->xpos--;
}
/* Need to scroll the window over */
else if (windows[windowno]->xpos>windows[windowno]->width) {
#if 0
XCopyArea(display, win, new_win, gc, windows[windowno]->x+1,
windows[windowno]->y, windows[windowno]->width, windows[windowno]->height+1,
0, 0);
XClearArea(display, win, windows[windowno]->x +
windows[windowno]->width -1, windows[windowno]->y,
2, windows[windowno]->height+1, False);
XCopyArea(display, new_win, win, gc, 0, 0,
windows[windowno]->width, windows[windowno]->height+1,
windows[windowno]->x, windows[windowno]->y);
#else
XCopyArea(display, win, win, gc, windows[windowno]->x+1,
windows[windowno]->y, windows[windowno]->width, windows[windowno]->height+1,
windows[windowno]->x, windows[windowno]->y);
XClearArea(display, win, windows[windowno]->x +
windows[windowno]->width , windows[windowno]->y,
1, windows[windowno]->height+1, False);
#endif
windows[windowno]->xpos--;
}
if (draw_baseline) {
XSetForeground(display,gc, baseline.pixel);
XDrawPoint(display,win,gc, windows[windowno]->x + windows[windowno]->xpos,
windows[windowno]->y + windows[windowno]->height);
}
for (count=0; count<num_graphs; count++) {
if (graphs[count].window != windowno) continue;
XSetForeground(display, gc, graphs[count].color_pixel);
if (solid) {
XFillRectangle(display,win, gc,
windows[windowno]->x + windows[windowno]->xpos-1,
1+windows[windowno]->y + windows[windowno]->height -
(points[count][point_pos] * windows[windowno]->height) /
(graphs[count].scale * graphs[count].scale_mult),
1,
(points[count][point_pos] * windows[windowno]->height) /
(graphs[count].scale * graphs[count].scale_mult));
} else {
XDrawLine(display,win, gc,
windows[windowno]->x + windows[windowno]->xpos-1,
windows[windowno]->y + windows[windowno]->height-
lasty[count] * windows[windowno]->height/(graphs[count].scale *
graphs[count].scale_mult),
windows[windowno]->x + windows[windowno]->xpos,
windows[windowno]->y +windows[windowno]->height -
(points[count][point_pos] *windows[windowno]->height) /
(graphs[count].scale * graphs[count].scale_mult));
}
lasty[count] = points[count][point_pos];
/* point_pos %2 is to draw it every other point. */
if (graphs[count].scale_lines>0 && !(point_pos % 2) &&
graphs[count].scale_lines<graphs[count].scale) {
int sline,slineinc=1;
/* Keep is so that the number of horizontal lines is
* at least somewhat reasonable.
*/
while ((graphs[count].scale_lines*slineinc*windows[windowno]->height / graphs[count].scale) < PTSSCALELINE)
slineinc*=2;
sline = slineinc;
/* Yes, it is supposed to be just less than scale,
* not less than equal. No point drawing a line at
* the top of the window.
*/
while (sline*graphs[count].scale_lines < graphs[count].scale) {
XDrawPoint(display, win, gc, windows[windowno]->x+ windows[windowno]->xpos -1,
windows[windowno]->y + windows[windowno]->height - (sline *
graphs[count].scale_lines * windows[windowno]->height) /
graphs[count].scale + graph_spacing);
sline+=slineinc;
}
}
}
windows[windowno]->xpos++;
}
void graph_loop()
{
int i;
set_graph_windows();
points =(int **) malloc(sizeof(int * ) * num_graphs);
lasty = (int *) malloc(sizeof(int) * num_graphs);
for (i=0; i<num_graphs; i++) {
points[i] = (int *) malloc(sizeof(int) *
split_width);
graphs[i].running_avg = (graphs[i].scale+1)*split_width *
graphs[i].scale_mult;
if (!hide_hosts &&
graphs[i].host_offset != localhost || allnames) {
strcat(graphs[i].name,"@");
strcat(graphs[i].name,hosts[graphs[i].host_offset].name);
}
graphs[i].name_len = strlen(graphs[i].name);
graphs[i].max_val=0;
}
/* Create a pixmap for scrolling just about the size of the
* window. size_hints.height is used instead of the height
* variable because the labels can be turned on and off. So
* we create a pixmap that is large enough in case the labels
* are turned off.
*/
new_win = XCreatePixmap(display, win, split_width-1,size_hints.height+
graph_spacing, DefaultDepth(display, screen_num));
set_first_values();
sleep(sleep_time);
set_values(); /* this call sets the starting point */
sleep(sleep_time);
for (i=0; i<split_x * split_y; i++)
windows[i]->xpos=1;
for (i=0; i<num_graphs; i++)
lasty[i] = points[i][0];
while (1) {
/* sets the values for all the graphs */
set_values();
for (i=0; i <split_x * split_y; i++)
update_window(i);
/* Sleep for 1 second at a time, then check for events.
* This is done so that there will never be more than
* 1 second before events are handled. The program does
* assume that all other processing time is negligable
* in respect to the sleep amount
*/
for (i=0; i<sleep_time; i++) {
sleep(1);
check_events();
}
}
}
void usage()
{
printf("%s Usage:\n",VERSION);
puts("-allnames Display names of all hosts, even localhost");
puts("-background color background color");
puts("-baseline color draw baseline of color 'color'");
puts("-border number spacing between graphs and window edges");
puts("-color color color of last graph specified by -type");
puts("-defcolor graphtype color Sets default color for all types of graphtype");
puts("-display displayname X server to contact");
puts("-foreground color foreground color");
puts("-geometry geom size (in pixels) and position");
puts("-hidelabels do not display labels at bottom of graph.");
puts("-hidehosts do not add hostnames to labels.");
puts("-host string machine to report statistics on");
puts("-link graph link scale of last graph to graph specified.");
puts(" For graphs on remote hosts, use graph@host from.");
puts("-min number minimum scale of last graph specified by -type");
puts("-max number maximum scale of last graph specified by -type");
puts("-name string Set the X application resource name");
puts("-ruler number draw a ruler below the baseline, with time markings");
puts(" every 'number' seconds.");
puts("-samescale Synchronize scales of similar graphs");
puts("-sample number how many seconds between each update");
puts("-scale number draw dotted horizontal lines of the last graph");
puts(" at value specified.");
puts("-showmax show running maximum value");
puts("-solid Draw a solid graph.");
puts("-split WxH Split the window into W width and H height sections.");
puts("-title string Set the window manager title");
puts("-type string type of graph to display: Choices of cpu,");
puts(" packets, pagei, swap, interrupts, disk, context, load,");
puts(" collisions, errors, ipackets, opackets, apagei, apageo, pageo.");
puts("-window number Window number to plot graph in.");
puts("-wtitle name Sets the default short subwindow name.");
exit(1);
}
void fatal(char *message)
{
fprintf(stderr,"%s\n", message);
exit(1);
}
void set_up_graph(char *graphtype)
{
int i;
char *host;
void set_graph_host(char *);
host = strchr(graphtype,'@');
if (host!=NULL) {
*host='\0';
host++;
}
for (i=0; i < NUM_TYPES; i++) {
if (!strcmp(graphtype, types[i].cmd_option)) {
if (num_graphs==0)
graphs = (struct graph_info *)
malloc(sizeof(struct graph_info));
else
graphs = (struct graph_info *)
realloc(graphs, sizeof(struct graph_info)*
(num_graphs+1));
graphs[num_graphs].type = i;
graphs[num_graphs].scale = types[i].def_scale;
graphs[num_graphs].true_scale = types[i].def_scale;
graphs[num_graphs].color_pixel = defcolors[i];
strcpy(graphs[num_graphs].name,types[i].window_name);
graphs[num_graphs].host_offset = -1;
graphs[num_graphs].scale_lines = 0;
graphs[num_graphs].link = num_graphs;
if (next_window!=-1)
graphs[num_graphs].window = next_window;
else {
graphs[num_graphs].window = num_windows_spec++;
if (num_windows_spec > (split_x * split_y-1))
num_windows_spec--;
}
if (i==CPU || i==UCPU || i==NICECPU || i==SCPU || i==ICPU) {
graphs[num_graphs].max_scale = 100;
graphs[num_graphs].min_scale = 100;
}
else {
graphs[num_graphs].max_scale = 0x7fffffff;
graphs[num_graphs].min_scale = 2;
}
if (i>=LOAD1 && i<=LOAD15)
graphs[num_graphs].scale_mult = LOAD_FACTOR;
else
graphs[num_graphs].scale_mult = 1;
break;
}
}
if (i>=NUM_TYPES) {
fprintf(stderr,"Unknown graph type: %s\n", graphtype);
exit(1);
}
num_graphs++;
if (host!=NULL)
set_graph_host(host);
}
void parse_geometry(char *geostring)
{
int geomask;
/* Would be nice if in the size_hints structure
* declaration in the X11 header files that width
* and height would be declared the same as what
* most calls use..
*/
geomask = XParseGeometry(geostring, &size_hints.x,
&size_hints.y, (unsigned *)&size_hints.width,
(unsigned *)&size_hints.height);
size_hints.min_width = split_x * (DEFAULT_SIZE + graph_spacing -1);
size_hints.min_height = split_y * (DEFAULT_SIZE + graph_spacing -1);
if (size_hints.min_width > size_hints.width)
size_hints.width = size_hints.min_width;
if (size_hints.min_height > size_hints.height)
size_hints.height = size_hints.min_height;
if (geomask & XNegative)
size_hints.x += WidthOfScreen(ScreenOfDisplay(display,screen_num))
- size_hints.width;
if (geomask & YNegative)
size_hints.y += HeightOfScreen(ScreenOfDisplay(display,screen_num))
- size_hints.height;
if (geomask & (XValue | YValue))
size_hints.flags |= USPosition;
if ((geomask & XNegative) && (geomask & YNegative))
size_hints.win_gravity = SouthEastGravity;
else if (geomask & XNegative)
size_hints.win_gravity = NorthEastGravity;
else if (geomask & YNegative)
size_hints.win_gravity = SouthWestGravity;
}
void set_display(char *option)
{
display_name = option;
}
void set_background_color(char *color)
{
XColor exact;
if (!XAllocNamedColor(display, DefaultColormap(display, screen_num),
color, &background,&exact))
fprintf(stderr,"Could not allocated color %s\n",color);
}
void set_foreground_color(char *color)
{
XColor exact;
if (!XAllocNamedColor(display, DefaultColormap(display, screen_num),
color, &foreground,&exact))
fprintf(stderr,"Could not allocated color %s\n",color);
}
void set_baseline(char *color)
{
XColor exact;
if (!XAllocNamedColor(display, DefaultColormap(display, screen_num),
color, &baseline,&exact))
fprintf(stderr,"Could not allocated color %s\n",color);
else draw_baseline = TRUE;
}
void set_sample_time(char *val)
{
sleep_time = atoi(val);
if (sleep_time < 1)
usage();
}
void set_allnames() { allnames=TRUE; }
void set_samescale() { scale_sync = TRUE; }
void set_hidelabels() { label_style = Nolabel; }
void set_solid() { solid=TRUE; }
void set_hidehosts() { hide_hosts = TRUE; }
void set_name(char *val) { window_name = val; }
void set_title(char *val) { window_title = val; }
void set_showmax() { show_max = TRUE; }
void set_ruler(char *val)
{
if (show_time)
fatal("Only one -ruler arguement may be specified.");
else {
time_ticks = atoi(val);
show_time = TRUE;
if (time_ticks < 0)
fatal("Time value for -ruler must be at least 10.");
}
}
void set_border(char *val)
{
graph_spacing = atoi(val);
if (graph_spacing<1 || graph_spacing>50)
fatal("-space value must be between 1 and 50.");
}
void set_split (char *split)
{
int tmpx, tmpy;
if (sscanf(split,"%dx%d", &tmpx, &tmpy)!=2)
fatal("Error in specifying widthxheight split argument");
if (tmpx<1 || tmpy<1)
fatal("Split width and height must be at least 1.");
if (windows!=NULL) {
int i;
for (i=0; i<split_x*split_y; i++)
free(windows[i]);
free(windows);
}
split_x = tmpx;
split_y = tmpy;
windows = malloc(sizeof(struct Xss_Window*) * split_x * split_y);
for (tmpx = 0; tmpx < split_x*split_y; tmpx++) {
windows[tmpx] = malloc(sizeof(struct Xss_Window) * split_x * split_y);
windows[tmpx]->num_graphs = 0;
windows[tmpx]->redraw_needed = FALSE;
windows[tmpx]->title = NULL;
}
}
void set_graph_color(char *color)
{
XColor exact, screen_def;
if (!XAllocNamedColor(display, DefaultColormap(display, screen_num),
color, &screen_def,&exact))
fprintf(stderr,"Could not allocated color %s\n",color);
else {
if (!num_graphs)
fatal("No graphs have been specified");
else
graphs[num_graphs-1].color_pixel =
screen_def.pixel;
}
}
void set_graph_min(char *val)
{
if (!num_graphs)
fatal("No graphs have been specified");
else {
graphs[num_graphs-1].min_scale = atoi(val);
if (graphs[num_graphs-1].scale< graphs[num_graphs-1].min_scale)
graphs[num_graphs-1].true_scale = graphs[num_graphs-1].scale = graphs[num_graphs-1].min_scale;
if (graphs[num_graphs-1].min_scale < 2)
graphs[num_graphs-1].min_scale = 2;
}
}
void set_graph_max(char *val)
{
if (!num_graphs)
fatal("No graphs have been specified");
else {
graphs[num_graphs-1].max_scale = atoi(val);
if (graphs[num_graphs-1].scale> graphs[num_graphs-1].max_scale)
graphs[num_graphs-1].true_scale = graphs[num_graphs-1].scale = graphs[num_graphs-1].max_scale;
if (graphs[num_graphs-1].max_scale < graphs[num_graphs-1].min_scale)
graphs[num_graphs-1].max_scale =
graphs[num_graphs-1].min_scale;
}
}
void set_graph_host(char *host)
{
if (!num_graphs)
fatal("No graphs have been specified");
else {
if (!num_hosts) {
hosts = (struct Host_Info*) malloc(sizeof(struct Host_Info));
hosts[0].name = strdup_local(host);
#ifdef USE_NEW_RSTAT
hosts[0].client=NULL;
#endif
graphs[num_graphs-1].host_offset = 0;
num_hosts++;
}
else {
int s;
for (s=0; s<num_hosts; ++s)
if (!strcmp(hosts[s].name,host)) break;
if (s==num_hosts) {
hosts =(struct Host_Info *)realloc(hosts, (num_hosts+1) * sizeof(struct Host_Info));
hosts[num_hosts].name = strdup_local(host);
#ifdef USE_NEW_RSTAT
hosts[num_hosts].client=NULL;
#endif
num_hosts++;
}
graphs[num_graphs-1].host_offset = s;
}
}
}
void set_graph_scale(char *val)
{
if (!num_graphs)
fatal("No graphs have been specified");
else
graphs[num_graphs-1].scale_lines = atoi(val);
}
void set_link(char *link)
{
if (num_graphs<2)
fatal("Must have selected at least two graphs before you can link\ngraphs together.");
else {
char *graph=link,*host;
int tmp=0,graph_type=0;
/* Break down the arguement so the graph is the graph name
* and host is the host name. Set the host name to the
* local host name if the host name was not specified.
*/
host = strchr(link,'@');
if (host!=NULL) {
*host='\0';
host++;
}
for (tmp=0; tmp<NUM_TYPES; tmp++)
if (!strcmp(graph, types[tmp].cmd_option)) {
graph_type = tmp;
break;
}
if (tmp>=NUM_TYPES) {
fprintf(stderr,"Could not match type to %s\n",graph);
exit(1);
}
for (tmp=0; tmp<(num_graphs-1); tmp++)
if (graphs[tmp].type == graph_type) {
if ((host==NULL && graphs[tmp].host_offset == -1) ||
(graphs[tmp].host_offset!=-1 && !strcmp(host, hosts[graphs[tmp].host_offset].name)))
break;
}
if (tmp==(num_graphs-1)) {
fprintf(stderr, "Could not find match for %s\n", link);
exit(1);
}
else {
while (graphs[tmp].link !=tmp)
tmp = graphs[tmp].link;
graphs[num_graphs-1].link = tmp;
graphs[num_graphs-1].scale = graphs[tmp].scale;
}
}
}
void set_window_title(char *val)
{
int winnum=next_window;
if (winnum==-1) {
winnum=0;
}
if (winnum>split_x*split_y) {
fprintf(stderr,"Internal error - next_window is too high (%d)\n" , next_window);
return;
}
if (windows[winnum]->title)
free(windows[winnum]->title);
windows[winnum]->title = strdup_local(val);
label_style = Minimal;
}
void set_window(char *val)
{
int tmp_window;
tmp_window=atoi(val);
if (tmp_window<1 || tmp_window>split_x*split_y)
fatal("Invalid window number.");
next_window=--tmp_window;
}
void set_def_graph_color(char *graphtype, char *color)
{
int i;
XColor exact, screen_def;
for (i=0; i < NUM_TYPES; i++) {
if (!strcmp(graphtype, types[i].cmd_option)) {
if (!XAllocNamedColor(display, DefaultColormap(display, screen_num),
color, &screen_def,&exact))
fprintf(stderr,"Could not allocated color %s\n",color);
else {
defcolors[i]=screen_def.pixel;
}
break;
}
}
if (i>=NUM_TYPES) {
fprintf(stderr,"Unknown graph type: %s\n", graphtype);
}
}
/* This table is set up by pass number the options are processed.
* As of now, this order is not used, but it makes things a bit clearer
*/
struct Command_Line_Options options[]= {
{"-display", 1, 1, set_display}, /* display needs to be set before */
/* anything else */
{"-name", TRUE, 2, set_name},
{"-title", TRUE, 2, set_title},
{"-split", 1, 2, set_split},
{"-background", 1, 2, set_background_color},
{"-bg", 1, 2, set_background_color},
{"-foreground", 1, 2, set_foreground_color},
{"-fg", 1, 2, set_foreground_color},
{"-baseline", 1, 2, set_baseline},
{"-sample", 1, 2, set_sample_time},
{"-allnames", 0, 2, set_allnames},
{"-showmax", 0, 2, set_showmax},
{"-samescale", 0, 2, set_samescale},
{"-hidelabels", 0, 2, set_hidelabels},
{"-ruler", 1, 2, set_ruler},
{"-border", 1, 2, set_border},
{"-defcolor",2, 2, set_def_graph_color},
{"-hidehosts", 0, 2, set_hidehosts},
{"-geometry", 1, 3, parse_geometry}, /* Geometry needs to be set */
/* after split */
/* Stage 4 is really the graphs and their options */
{"-type", 1, 4, set_up_graph},
{"-color", 1, 4, set_graph_color},
{"-min", 1, 4, set_graph_min},
{"-max", 1, 4, set_graph_max},
{"-host", 1, 4, set_graph_host},
{"-scale", 1, 4, set_graph_scale},
{"-link", 1, 4, set_link},
{"-window", 1, 4, set_window},
{"-wtitle", 1, 4, set_window_title},
{"-solid", 0, 4, set_solid}
};
void parse_args(int argc, char *argv[], int pass)
{
int i, on_arg=1;
while (on_arg<argc) {
for (i=0; i<sizeof(options)/sizeof(struct Command_Line_Options); i++) {
if (!strcmp(options[i].cmd_option, argv[on_arg])) {
/* Found a matching option, but should not be processed on
* this pass. Just skip over it
*/
if (options[i].pass != pass) {
on_arg += options[i].num_args+1;
break;
}
if (options[i].num_args) {
if ((on_arg+options[i].num_args)==argc) {
fprintf(stderr,"%s requires an argument.\n", options[i].cmd_option);
exit(1);
}
else {
if (options[i].num_args==1)
options[i].func(argv[on_arg+1]);
if (options[i].num_args==2)
options[i].func(argv[on_arg+1],argv[on_arg+2]);
on_arg +=options[i].num_args+1;
}
}
else {
options[i].func();
on_arg++;
}
break;
}
}
if (i==sizeof(options)/sizeof(struct Command_Line_Options)) {
fprintf(stderr,"Unknown option: %s\n", argv[on_arg]);
exit(1);
}
}
}
static void set_min_geometry()
{
size_hints.min_width = split_x * (DEFAULT_SIZE + graph_spacing) +
graph_spacing;
size_hints.min_height = split_y * (DEFAULT_SIZE + graph_spacing) +
graph_spacing;
if (size_hints.min_width > size_hints.width)
size_hints.width = size_hints.min_width;
if (size_hints.min_height > size_hints.height)
size_hints.height = size_hints.min_height;
}
int main( argc, argv )
int argc;
char *argv[];
{
XGCValues xgcvalues;
XWMHints wm_hints;
XClassHint class_hints;
XTextProperty wm_title;
XSetWindowAttributes window_attributes;
int i;
char *cp;
/* localhost needs to be static because we set a pointer to it. */
static char localhostname[MAXHOSTNAMELEN];
if (argc==1) usage();
/* By convention, the name of the executable is used as the default
* application name.
*/
for ( window_name = argv[0] + strlen(argv[0]); window_name > argv[0];
--window_name )
if (*window_name == '/') {
++window_name;
break;
}
/* Find the display (and screen number) first. Once we have that
* information, we can process the other arguements better as
* we read them.
*/
parse_args(argc, argv, 1);
if ((display=XOpenDisplay(display_name))==NULL) {
fprintf(stderr, "Unable to open display: %s\n",
XDisplayName(display_name));
exit(1);
}
screen_num = DefaultScreen(display);
whitepixel = WhitePixel(display, screen_num);
blackpixel = BlackPixel(display, screen_num);
foreground.pixel = blackpixel;
foreground.red = foreground.blue = foreground.green = 0;
background.pixel = whitepixel;
background.red = foreground.blue = foreground.green = 65535;
for (i=0; i<NUM_TYPES; i++)
defcolors[i]=foreground.pixel;
windows = malloc(sizeof(struct Xss_Window*));
windows[0]= malloc(sizeof(struct Xss_Window) * split_x * split_y);
windows[0]->num_graphs = 0;
windows[0]->redraw_needed = FALSE;
windows[0]->title=NULL;
parse_args(argc, argv,2);
parse_args(argc, argv,3);
parse_args(argc, argv,4);
if (!num_graphs) {
fprintf(stderr,"At least one graph type needs to be specified\n");
exit(1);
}
/* Following sets up the graphs where a hostname was not specified. First
* find out if the local host was specified for at least one graph.
* Then, go through the graph types. If there are graphs in which a
* host has not been specified, then set it the localhost, and allocate
* the space for it, if that is required.
*/
gethostname(localhostname, MAXHOSTNAMELEN);
set_min_geometry();
for (i=0; i<num_hosts; i++) {
if (!strcmp(hosts[i].name,localhostname)) {
localhost = i;
break;
}
}
for (i=0; i<num_graphs; i++) {
if (scale_sync) {
int j;
for (j=i+1; j<num_graphs; j++) {
if (graphs[j].type == graphs[i].type &&
graphs[j].link == j) {
graphs[j].link = i;
graphs[j].scale = graphs[i].scale;
}
}
}
if (graphs[i].window>=split_x*split_y)
fatal("Graph window larger than actual number of windows");
windows[graphs[i].window]->num_graphs++;
if (graphs[i].host_offset == -1) {
if (localhost==-1) {
if (num_hosts==0)
hosts = (struct Host_Info *)malloc(sizeof(struct Host_Info));
else
hosts=(struct Host_Info *)realloc(hosts, (1+num_hosts) * sizeof(struct Host_Info));
hosts[num_hosts].name = localhostname;
#ifdef USE_NEW_RSTAT
hosts[num_hosts].client = NULL;
#endif
localhost = num_hosts++;
}
graphs[i].host_offset = localhost;
}
}
window_attributes.background_pixel = background.pixel;
window_attributes.backing_store = WhenMapped;
win = XCreateWindow(display, RootWindow(display, screen_num),
size_hints.x, size_hints.y, size_hints.width, size_hints.height,
0, CopyFromParent, InputOutput, CopyFromParent,
CWBackPixel | CWBackingStore, &window_attributes);
XSelectInput(display, win, StructureNotifyMask | ExposureMask |
KeyPressMask);
if ((font_info = XLoadQueryFont(display, FONT))==NULL) {
fprintf(stderr,"Unable to load font %s\n",FONT);
exit(1);
}
font_height=font_info->max_bounds.ascent +
font_info->max_bounds.descent;
xgcvalues.foreground = foreground.pixel;
xgcvalues.background = background.pixel;
/* xgcvalues.line_width = 1;*/
xgcvalues.line_width = 0;
xgcvalues.graphics_exposures = False;
xgcvalues.font = font_info->fid;
/* create the various graphic contexts we need. gc and gc_color
differ only in the we only change the colors on the gc_color context,
and thus, it is only used for brick drawing. gc_color really has no
use on a black & white system */
gc = XCreateGC(display, win, GCFont | GCLineWidth |
GCForeground | GCBackground | GCGraphicsExposures, &xgcvalues);
wm_hints.flags = InputHint;
wm_hints.input = True;
class_hints.res_name = window_name;
class_hints.res_class = "XSysStats";
XStringListToTextProperty(&window_title, 1, &wm_title);
XSetWMProperties(display, win, &wm_title, &wm_title,
argv,argc, &size_hints, &wm_hints, &class_hints);
XMapWindow(display, win);
XFlush(display);
/* initialize random (used to determine next stage) */
graph_loop();
exit(0);
}
syntax highlighted by Code2HTML, v. 0.9.1