#include /* Copyright 1985, by the Massachusetts Institute of Technology */ /* xload -- graph load average on X window system display. * K. Shane Hartman and Stuart A. Malone with ripoffs from xclock. * Host name feature added by Jim Gettys. * Scale feature added by Bob Scheifler. * Rescale feature added by Stuart A. Malone. */ #ifndef lint static char *rcsid_xload_c = "$Header: xload.c,v 10.9 86/02/01 16:00:54 tony Rel $"; #endif lint #include #include #include #include #include #include #include typedef enum _bool {FALSE, TRUE} Bool; #define KMEM_FILE "/dev/kmem" #define KMEM_ERROR "cannot open /dev/kmem" #define DEFAULT_BORDER_WIDTH 3 #define DEFAULT_UPDATE 5 /* Any smaller leads to lossage */ #define DEFAULT_FONT "6x10" #define DEFAULT_POSITION "=360x120-0+0" /* upper right hand corner */ #define DEFAULT_SCALE 1 struct nlist namelist[] = { /* namelist for vmunix grubbing */ #define LOADAV 0 {"_avenrun"}, {0} }; extern char *getenv(); /* GLOBAL */ Window win; /* load average window */ double data[2048]; /* accumulated load average data */ int background; /* color of background */ int foreground; /* color of graph */ int highlight; /* color of text, scale */ Font font; /* font for printing hostname */ char *fn = DEFAULT_FONT; /* font for hostname */ FontInfo font_info; char host[256]; /* the host name */ double scale = DEFAULT_SCALE; /* n divisions for graph */ double min_scale = DEFAULT_SCALE; /* minimum value for scale */ double max_loadavg = 0.0; /* maximum loadavg on the graph */ int mapped = 1; /* should really display? */ short gray_bits[16] = { 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555}; /* Diagnostic printer - Print message and exit */ void xload_error(message) char *message; { fprintf(stderr, "xload: %s\n", message); perror("xload"); exit(1); } /* Blts data according to current size, then redraws the load average window. * Next represents the number of valid points in data. Returns the (possibly) * adjusted value of next. If next is 0, this routine draws an empty window * (scale - 1 lines for graph). If next is less than the current window width, * the returned value is identical to the initial value of next and data is * unchanged. Otherwise keeps half a window's worth of data. If data is * changed, then max_loadavg is updated to reflect the largest data point. */ int repaint_window(width, height, next) register int width, height, next; { register int i, j; if (mapped) XClear(win); if (next >= width) { j = width >> 1; bcopy((char *)(data + next - j),(char *)data, j * sizeof(double)); next = j; /* Since we just lost some data, recompute the max_loadavg. */ max_loadavg = 0.0; for (i = 0; i < next; i++) { if (data[i] > max_loadavg) max_loadavg = data[i]; } } /* Compute the minimum scale required to graph the data, but don't go lower than min_scale. */ if (max_loadavg > min_scale) scale = ((int)max_loadavg) + 1; else scale = min_scale; if (!mapped) return(next); /* Print hostname */ XTextMask(win, 2, 2, host, strlen(host), font, highlight); /* Draw graph reference lines */ for (i = 1; i < scale; i++) { j = (i * height) / scale; XLine(win, 0, j, width, j, 1, 1, highlight, GXcopy, AllPlanes); } /* Draw data point lines. */ for (i = 0; i < next; i++) XLine(win, i, height, i, (int)(height - (data[i] * height) / scale), 1, 1, foreground, GXcopy, AllPlanes); return(next); } /* Exit with message describing command line format */ void usage() { fprintf(stderr, "usage: xload [-fn {font}] [-update {seconds}] [-scale {integer}] [-rv]\n" ); fprintf(stderr, " [=[{width}][x{height}][{+-}{xoff}[{+-}{yoff}]]] [[{host}]:[{vs}]]\n" ); fprintf(stderr, " [-fg {color}] [-bg {color}] [-hl {color}] [-bd {color}] [-bw {pixels}]\n"); exit(1); } /* Returns pointer to first char in search which is also in what, else NULL. */ char *strscan(search, what) register char *search; register char *what; { register int i; register len = strlen(what); register char c; while (c = *(search++)) for (i = 0; i < len; i++) if (c == what[i]) return (--search); return (NULL); } void main(argc, argv) int argc; char **argv; { char *arg; register int i; register int kmem; /* kmem pointer */ register double loadavg; /* load average value */ long loadavg_seek; /* offset to load average in kmem */ char display[256]; /* will contain vs host */ int vsnum; /* will contain vs number */ int reverse = 0; char *border_color; char *fore_color; char *back_color; char *high_color; int border_pixmap; int border_width = DEFAULT_BORDER_WIDTH; int update = DEFAULT_UPDATE; Color cdef; OpaqueFrame window; /* frame for the window */ char *geometry = NULL; char *def = DEFAULT_POSITION; /* default position */ char *option; XEvent event; int maxfds; /* for select call */ int readfds; int fdmask; struct timeval timeout; /* will contain update interval */ /* Get name list. Then open kmem so we can seek for load average. */ nlist("/vmunix", namelist); if (namelist[LOADAV].n_type == 0) xload_error("cannot get name list"); loadavg_seek = namelist[LOADAV].n_value; kmem = open(KMEM_FILE, O_RDONLY); if (kmem < 0) xload_error(KMEM_ERROR); gethostname(host, sizeof(host)); /* Who are we? */ display[0] = '\0'; if ((option = XGetDefault(argv[0],"ReverseVideo")) != NULL ) if (strcmp (option, "on") == 0) reverse = 1; if ((option = XGetDefault(argv[0],"BorderWidth")) != NULL) border_width = atoi(option); if ((option = XGetDefault(argv[0],"BodyFont")) != NULL) fn = option; if ((border_color = XGetDefault(argv[0],"Border")) == NULL) border_color = XGetDefault(argv[0],"BorderColor"); back_color = XGetDefault(argv[0],"Background"); fore_color = XGetDefault(argv[0],"Foreground"); high_color = XGetDefault(argv[0],"Highlight"); if ((option = XGetDefault(argv[0],"Update")) != NULL) update = atoi(option); if ((option = XGetDefault(argv[0],"Scale")) != NULL) min_scale = atoi(option); for (i = 1; i < argc; i++) { /* Parse line */ if (argv[i][0] == '=') { geometry = argv[i]; continue; } if (index(argv[i], ':') != NULL) { /* host:display */ strncpy(display, argv[i], sizeof(display)); continue; } if (strcmp(argv[i], "-rv") == 0 || strcmp(argv[i], "-reverse") == 0) { /* black on white */ reverse = 1; continue; } if (strcmp(argv[i], "-fw") == 0 || strcmp(argv[i], "-forward") == 0) { /* white on black */ reverse = 0; continue; } if (strcmp(argv[i], "-bw") == 0 || strcmp(argv[i], "-border") == 0) { /* border width */ if (++i >= argc) usage(); border_width = atoi(argv[i]); continue; } if (strcmp(argv[i], "-fn") == 0 || strcmp(argv[i], "-font") == 0) { /* host name font */ if (++i >= argc) usage(); fn = argv[i]; continue; } if (strcmp(argv[i], "-bd") == 0 || strcmp(argv[i], "-color") == 0) { /* border color */ if (++i >= argc) usage(); border_color = argv[i]; continue; } if (strcmp(argv[i], "-fg") == 0 || strcmp(argv[i], "-foreground") == 0) { /* foreground color */ if (++i >= argc) usage(); fore_color = argv[i]; continue; } if (strcmp(argv[i], "-bg") == 0 || strcmp(argv[i], "-background") == 0) { /* background color */ if (++i >= argc) usage(); back_color = argv[i]; continue; } if (strcmp(argv[i], "-hl") == 0 || strcmp(argv[i], "-highlight") == 0) { /* highlight color */ if (++i >= argc) usage(); high_color = argv[i]; continue; } if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "-update") == 0) { /* update interval */ if (++i >= argc) usage(); update = atoi(argv[i]); continue; } if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "-scale") == 0) { /* load scale */ if (++i >= argc) usage(); min_scale = atoi(argv[i]); continue; } usage(); } if (border_width < 0) border_width = DEFAULT_BORDER_WIDTH; if (update < DEFAULT_UPDATE) update = DEFAULT_UPDATE; if (min_scale <= 0) min_scale = DEFAULT_SCALE; scale = min_scale; /* Open display */ if (!XOpenDisplay(display)) xload_error("cannot open display"); /* Need a font to print hostname in */ font = XGetFont(fn); if (!font) xload_error("cannot open font"); XQueryFont(font, &font_info); if (border_color && DisplayCells() > 2 && XParseColor(border_color, &cdef) && XGetHardwareColor(&cdef)) border_pixmap = XMakeTile(cdef.pixel); else if (border_color && strcmp(border_color, "black") == 0) border_pixmap = BlackPixmap; else if (border_color && strcmp(border_color, "white") == 0) border_pixmap = WhitePixmap; else border_pixmap = XMakePixmap ((Bitmap) XStoreBitmap (16, 16, gray_bits), BlackPixel, WhitePixel); if (back_color && DisplayCells() > 2 && XParseColor(back_color, &cdef) && XGetHardwareColor(&cdef)) { background = cdef.pixel; } else if (back_color && strcmp(back_color, "white") == 0) { background = WhitePixel; reverse = 0; } else if (back_color && strcmp(back_color, "black") == 0) { background = BlackPixel; reverse = 0; } else background = BlackPixel; if (fore_color && DisplayCells() > 2 && XParseColor(fore_color, &cdef) && XGetHardwareColor(&cdef)) { foreground = cdef.pixel; } else if (fore_color && strcmp(fore_color, "black") == 0) { foreground = BlackPixel; reverse = 0; } else if (fore_color && strcmp(fore_color, "white") == 0) { foreground = WhitePixel; reverse = 0; } else foreground = WhitePixel; if (high_color && DisplayCells() > 2 && XParseColor(high_color, &cdef) && XGetHardwareColor(&cdef)) highlight = cdef.pixel; else if (reverse) highlight = background; else highlight = foreground; if (reverse) { background = foreground; foreground = highlight; } window.bdrwidth = border_width; window.border = border_pixmap; window.background = XMakeTile(background); win = XCreate ("Load Average", argv[0], geometry, def, &window, font_info.width * strlen(host) + 4, font_info.height + 4); XSelectInput(win, ExposeWindow|UnmapWindow); XMapWindow(win); /* Map window to screen */ timeout.tv_sec = update; /* Set up timeout for select */ timeout.tv_usec = 0; maxfds = dpyno() + 1; /* Set up select arguments */ fdmask = 1 << dpyno(); i = 0; /* Window is initially empty */ while (1) { /* Main loop */ if (XPending()) { XNextEvent(&event); switch (event.type) { case ExposeWindow: mapped = 1; window.width = ((XExposeEvent *) &event)->width; window.height = ((XExposeEvent *) &event)->height; i = repaint_window(window.width, window.height, i); break; case UnmapWindow: mapped = 0; break; default: xload_error("unexpected X event"); } } else if (i >= window.width) i = repaint_window(window.width, window.height, i); /* Get the load average, stash the point and draw corresponding line. */ lseek(kmem, loadavg_seek, 0); #ifdef sun { long temp; read(kmem, (char *)&temp, sizeof(long)); loadavg = (double)temp/FSCALE; } #else read(kmem, (char *)&loadavg, sizeof(double)); #endif /* Keep max_loadavg up to date, and if this data point is off the graph, change the scale to make it fit. */ if (loadavg > max_loadavg) { max_loadavg = loadavg; if (max_loadavg > scale) { scale = ((int)max_loadavg) + 1; i = repaint_window(window.width, window.height, i); } } data[i] = loadavg; if (mapped) { XLine(win, i, window.height, i, (int)(window.height - (window.height * loadavg) / scale), 1, 1, foreground, GXcopy, AllPlanes); XFlush(); /* Flush output buffers */ } i++; /* Next point */ readfds = fdmask; /* Initialize select mask */ /* and select on display fd */ if (select(maxfds, &readfds, NULL, NULL, &timeout) == -1) xload_error("select error on display file descriptor"); } /* while */ } /* main */