1: #include <X/mit-copyright.h> 2: 3: /* Copyright 1985, by the Massachusetts Institute of Technology */ 4: /* xload -- graph load average on X window system display. 5: * K. Shane Hartman and Stuart A. Malone with ripoffs from xclock. 6: * Host name feature added by Jim Gettys. 7: * Scale feature added by Bob Scheifler. 8: * Rescale feature added by Stuart A. Malone. 9: */ 10: #ifndef lint 11: static char *rcsid_xload_c = "$Header: xload.c,v 10.9 86/02/01 16:00:54 tony Rel $"; 12: #endif lint 13: 14: #include <stdio.h> 15: #include <strings.h> 16: #include <nlist.h> 17: #include <sys/time.h> 18: #include <sys/file.h> 19: #include <sys/param.h> 20: #include <X/Xlib.h> 21: 22: typedef enum _bool {FALSE, TRUE} Bool; 23: 24: #define KMEM_FILE "/dev/kmem" 25: #define KMEM_ERROR "cannot open /dev/kmem" 26: 27: #define DEFAULT_BORDER_WIDTH 3 28: #define DEFAULT_UPDATE 5 /* Any smaller leads to lossage */ 29: #define DEFAULT_FONT "6x10" 30: #define DEFAULT_POSITION "=360x120-0+0" /* upper right hand corner */ 31: #define DEFAULT_SCALE 1 32: 33: struct nlist namelist[] = { /* namelist for vmunix grubbing */ 34: #define LOADAV 0 35: {"_avenrun"}, 36: {0} 37: }; 38: 39: extern char *getenv(); 40: 41: /* GLOBAL */ 42: 43: Window win; /* load average window */ 44: double data[2048]; /* accumulated load average data */ 45: int background; /* color of background */ 46: int foreground; /* color of graph */ 47: int highlight; /* color of text, scale */ 48: Font font; /* font for printing hostname */ 49: char *fn = DEFAULT_FONT; /* font for hostname */ 50: FontInfo font_info; 51: char host[256]; /* the host name */ 52: double scale = DEFAULT_SCALE; /* n divisions for graph */ 53: double min_scale = DEFAULT_SCALE; /* minimum value for scale */ 54: double max_loadavg = 0.0; /* maximum loadavg on the graph */ 55: int mapped = 1; /* should really display? */ 56: 57: short gray_bits[16] = { 58: 0xaaaa, 0x5555, 0xaaaa, 0x5555, 59: 0xaaaa, 0x5555, 0xaaaa, 0x5555, 60: 0xaaaa, 0x5555, 0xaaaa, 0x5555, 61: 0xaaaa, 0x5555, 0xaaaa, 0x5555}; 62: 63: /* Diagnostic printer - Print message and exit */ 64: 65: void xload_error(message) 66: char *message; 67: { 68: fprintf(stderr, "xload: %s\n", message); 69: perror("xload"); 70: exit(1); 71: } 72: 73: /* Blts data according to current size, then redraws the load average window. 74: * Next represents the number of valid points in data. Returns the (possibly) 75: * adjusted value of next. If next is 0, this routine draws an empty window 76: * (scale - 1 lines for graph). If next is less than the current window width, 77: * the returned value is identical to the initial value of next and data is 78: * unchanged. Otherwise keeps half a window's worth of data. If data is 79: * changed, then max_loadavg is updated to reflect the largest data point. 80: */ 81: 82: int repaint_window(width, height, next) 83: register int width, height, next; 84: { 85: register int i, j; 86: 87: if (mapped) 88: XClear(win); 89: if (next >= width) { 90: j = width >> 1; 91: bcopy((char *)(data + next - j),(char *)data, j * sizeof(double)); 92: next = j; 93: /* Since we just lost some data, recompute the max_loadavg. */ 94: max_loadavg = 0.0; 95: for (i = 0; i < next; i++) { 96: if (data[i] > max_loadavg) max_loadavg = data[i]; 97: } 98: } 99: 100: /* Compute the minimum scale required to graph the data, but don't go 101: lower than min_scale. */ 102: if (max_loadavg > min_scale) scale = ((int)max_loadavg) + 1; 103: else scale = min_scale; 104: 105: if (!mapped) return(next); 106: 107: /* Print hostname */ 108: XTextMask(win, 2, 2, host, strlen(host), font, highlight); 109: 110: /* Draw graph reference lines */ 111: for (i = 1; i < scale; i++) { 112: j = (i * height) / scale; 113: XLine(win, 0, j, width, j, 1, 1, highlight, GXcopy, AllPlanes); 114: } 115: 116: /* Draw data point lines. */ 117: for (i = 0; i < next; i++) 118: XLine(win, i, height, i, (int)(height - (data[i] * height) / scale), 119: 1, 1, foreground, GXcopy, AllPlanes); 120: return(next); 121: } 122: 123: /* Exit with message describing command line format */ 124: 125: void usage() 126: { 127: fprintf(stderr, 128: "usage: xload [-fn {font}] [-update {seconds}] [-scale {integer}] [-rv]\n" 129: ); 130: fprintf(stderr, 131: " [=[{width}][x{height}][{+-}{xoff}[{+-}{yoff}]]] [[{host}]:[{vs}]]\n" 132: ); 133: fprintf(stderr, 134: " [-fg {color}] [-bg {color}] [-hl {color}] [-bd {color}] [-bw {pixels}]\n"); 135: exit(1); 136: } 137: 138: /* Returns pointer to first char in search which is also in what, else NULL. */ 139: 140: char *strscan(search, what) 141: register char *search; 142: register char *what; 143: { 144: register int i; 145: register len = strlen(what); 146: register char c; 147: while (c = *(search++)) 148: for (i = 0; i < len; i++) 149: if (c == what[i]) return (--search); 150: return (NULL); 151: } 152: 153: 154: void main(argc, argv) 155: int argc; 156: char **argv; 157: { 158: char *arg; 159: 160: register int i; 161: 162: register int kmem; /* kmem pointer */ 163: register double loadavg; /* load average value */ 164: long loadavg_seek; /* offset to load average in kmem */ 165: 166: char display[256]; /* will contain vs host */ 167: int vsnum; /* will contain vs number */ 168: 169: int reverse = 0; 170: char *border_color; 171: char *fore_color; 172: char *back_color; 173: char *high_color; 174: int border_pixmap; 175: int border_width = DEFAULT_BORDER_WIDTH; 176: int update = DEFAULT_UPDATE; 177: Color cdef; 178: OpaqueFrame window; /* frame for the window */ 179: char *geometry = NULL; 180: char *def = DEFAULT_POSITION; /* default position */ 181: char *option; 182: 183: XEvent event; 184: 185: int maxfds; /* for select call */ 186: int readfds; 187: int fdmask; 188: struct timeval timeout; /* will contain update interval */ 189: 190: /* Get name list. Then open kmem so we can seek for load average. */ 191: 192: nlist("/vmunix", namelist); 193: if (namelist[LOADAV].n_type == 0) xload_error("cannot get name list"); 194: loadavg_seek = namelist[LOADAV].n_value; 195: kmem = open(KMEM_FILE, O_RDONLY); 196: if (kmem < 0) xload_error(KMEM_ERROR); 197: 198: gethostname(host, sizeof(host)); /* Who are we? */ 199: display[0] = '\0'; 200: 201: if ((option = XGetDefault(argv[0],"ReverseVideo")) != NULL ) 202: if (strcmp (option, "on") == 0) 203: reverse = 1; 204: if ((option = XGetDefault(argv[0],"BorderWidth")) != NULL) 205: border_width = atoi(option); 206: if ((option = XGetDefault(argv[0],"BodyFont")) != NULL) 207: fn = option; 208: if ((border_color = XGetDefault(argv[0],"Border")) == NULL) 209: border_color = XGetDefault(argv[0],"BorderColor"); 210: back_color = XGetDefault(argv[0],"Background"); 211: fore_color = XGetDefault(argv[0],"Foreground"); 212: high_color = XGetDefault(argv[0],"Highlight"); 213: if ((option = XGetDefault(argv[0],"Update")) != NULL) 214: update = atoi(option); 215: if ((option = XGetDefault(argv[0],"Scale")) != NULL) 216: min_scale = atoi(option); 217: 218: for (i = 1; i < argc; i++) { /* Parse line */ 219: if (argv[i][0] == '=') { 220: geometry = argv[i]; 221: continue; 222: } 223: if (index(argv[i], ':') != NULL) { /* host:display */ 224: strncpy(display, argv[i], sizeof(display)); 225: continue; 226: } 227: if (strcmp(argv[i], "-rv") == 0 || 228: strcmp(argv[i], "-reverse") == 0) { /* black on white */ 229: reverse = 1; 230: continue; 231: } 232: if (strcmp(argv[i], "-fw") == 0 || 233: strcmp(argv[i], "-forward") == 0) { /* white on black */ 234: reverse = 0; 235: continue; 236: } 237: if (strcmp(argv[i], "-bw") == 0 || 238: strcmp(argv[i], "-border") == 0) { /* border width */ 239: if (++i >= argc) usage(); 240: border_width = atoi(argv[i]); 241: continue; 242: } 243: if (strcmp(argv[i], "-fn") == 0 || 244: strcmp(argv[i], "-font") == 0) { /* host name font */ 245: if (++i >= argc) usage(); 246: fn = argv[i]; 247: continue; 248: } 249: if (strcmp(argv[i], "-bd") == 0 || 250: strcmp(argv[i], "-color") == 0) { /* border color */ 251: if (++i >= argc) usage(); 252: border_color = argv[i]; 253: continue; 254: } 255: if (strcmp(argv[i], "-fg") == 0 || 256: strcmp(argv[i], "-foreground") == 0) { /* foreground color */ 257: if (++i >= argc) usage(); 258: fore_color = argv[i]; 259: continue; 260: } 261: if (strcmp(argv[i], "-bg") == 0 || 262: strcmp(argv[i], "-background") == 0) { /* background color */ 263: if (++i >= argc) usage(); 264: back_color = argv[i]; 265: continue; 266: } 267: if (strcmp(argv[i], "-hl") == 0 || 268: strcmp(argv[i], "-highlight") == 0) { /* highlight color */ 269: if (++i >= argc) usage(); 270: high_color = argv[i]; 271: continue; 272: } 273: if (strcmp(argv[i], "-u") == 0 || 274: strcmp(argv[i], "-update") == 0) { /* update interval */ 275: if (++i >= argc) usage(); 276: update = atoi(argv[i]); 277: continue; 278: } 279: if (strcmp(argv[i], "-s") == 0 || 280: strcmp(argv[i], "-scale") == 0) { /* load scale */ 281: if (++i >= argc) usage(); 282: min_scale = atoi(argv[i]); 283: continue; 284: } 285: usage(); 286: } 287: 288: if (border_width < 0) border_width = DEFAULT_BORDER_WIDTH; 289: if (update < DEFAULT_UPDATE) update = DEFAULT_UPDATE; 290: if (min_scale <= 0) min_scale = DEFAULT_SCALE; 291: scale = min_scale; 292: 293: /* Open display */ 294: if (!XOpenDisplay(display)) 295: xload_error("cannot open display"); 296: 297: /* Need a font to print hostname in */ 298: font = XGetFont(fn); 299: if (!font) 300: xload_error("cannot open font"); 301: XQueryFont(font, &font_info); 302: 303: if (border_color && DisplayCells() > 2 && 304: XParseColor(border_color, &cdef) && XGetHardwareColor(&cdef)) 305: border_pixmap = XMakeTile(cdef.pixel); 306: else if (border_color && strcmp(border_color, "black") == 0) 307: border_pixmap = BlackPixmap; 308: else if (border_color && strcmp(border_color, "white") == 0) 309: border_pixmap = WhitePixmap; 310: else 311: border_pixmap = XMakePixmap ((Bitmap) XStoreBitmap (16, 16, gray_bits), 312: BlackPixel, WhitePixel); 313: 314: if (back_color && DisplayCells() > 2 && 315: XParseColor(back_color, &cdef) && XGetHardwareColor(&cdef)) { 316: background = cdef.pixel; 317: } else if (back_color && strcmp(back_color, "white") == 0) { 318: background = WhitePixel; 319: reverse = 0; 320: } else if (back_color && strcmp(back_color, "black") == 0) { 321: background = BlackPixel; 322: reverse = 0; 323: } else 324: background = BlackPixel; 325: 326: if (fore_color && DisplayCells() > 2 && 327: XParseColor(fore_color, &cdef) && XGetHardwareColor(&cdef)) { 328: foreground = cdef.pixel; 329: } else if (fore_color && strcmp(fore_color, "black") == 0) { 330: foreground = BlackPixel; 331: reverse = 0; 332: } else if (fore_color && strcmp(fore_color, "white") == 0) { 333: foreground = WhitePixel; 334: reverse = 0; 335: } else 336: foreground = WhitePixel; 337: 338: if (high_color && DisplayCells() > 2 && 339: XParseColor(high_color, &cdef) && XGetHardwareColor(&cdef)) 340: highlight = cdef.pixel; 341: else if (reverse) 342: highlight = background; 343: else 344: highlight = foreground; 345: 346: if (reverse) { 347: background = foreground; 348: foreground = highlight; 349: } 350: 351: window.bdrwidth = border_width; 352: window.border = border_pixmap; 353: window.background = XMakeTile(background); 354: win = XCreate ("Load Average", argv[0], geometry, def, &window, 355: font_info.width * strlen(host) + 4, font_info.height + 4); 356: XSelectInput(win, ExposeWindow|UnmapWindow); 357: XMapWindow(win); /* Map window to screen */ 358: timeout.tv_sec = update; /* Set up timeout for select */ 359: timeout.tv_usec = 0; 360: maxfds = dpyno() + 1; /* Set up select arguments */ 361: fdmask = 1 << dpyno(); 362: i = 0; /* Window is initially empty */ 363: 364: while (1) { /* Main loop */ 365: if (XPending()) { 366: XNextEvent(&event); 367: switch (event.type) { 368: case ExposeWindow: 369: mapped = 1; 370: window.width = ((XExposeEvent *) &event)->width; 371: window.height = ((XExposeEvent *) &event)->height; 372: i = repaint_window(window.width, window.height, i); 373: break; 374: case UnmapWindow: 375: mapped = 0; 376: break; 377: default: 378: xload_error("unexpected X event"); 379: } 380: } 381: else if (i >= window.width) i = repaint_window(window.width, window.height, i); 382: /* Get the load average, stash the point and draw corresponding line. */ 383: lseek(kmem, loadavg_seek, 0); 384: #ifdef sun 385: { 386: long temp; 387: read(kmem, (char *)&temp, sizeof(long)); 388: loadavg = (double)temp/FSCALE; 389: } 390: #else 391: read(kmem, (char *)&loadavg, sizeof(double)); 392: #endif 393: 394: /* Keep max_loadavg up to date, and if this data point is off the 395: graph, change the scale to make it fit. */ 396: if (loadavg > max_loadavg) { 397: max_loadavg = loadavg; 398: if (max_loadavg > scale) { 399: scale = ((int)max_loadavg) + 1; 400: i = repaint_window(window.width, window.height, i); 401: } 402: } 403: 404: data[i] = loadavg; 405: if (mapped) { 406: XLine(win, i, window.height, i, 407: (int)(window.height - (window.height * loadavg) / scale), 408: 1, 1, foreground, GXcopy, AllPlanes); 409: XFlush(); /* Flush output buffers */ 410: } 411: i++; /* Next point */ 412: readfds = fdmask; /* Initialize select mask */ 413: /* and select on display fd */ 414: if (select(maxfds, &readfds, NULL, NULL, &timeout) == -1) 415: xload_error("select error on display file descriptor"); 416: } /* while */ 417: } /* main */