Suvayu Ali <fatkasuvayu+linux@xxxxxxxxx> writes: > On Mon, Jul 22, 2013 at 12:33:53PM +0200, lee wrote: >> >> I've done that yesterday. It's working already, showing an >> automatically scaling graph of the used network bandwidth over 360 >> samples, with the update interval given on the command line. Let it >> update every 10 seconds, and you see the last hour. Xosview can't do >> that, and conky probably can't, either. >> >> It also has almost zero CPU usage, and memory usage is only 40064/2272 >> virt/res. Now I could add showing processes or whatever I like to see >> :) > > If you do not mind, it be educational for me to see your source. > > :) Sure --- you'll need libsx to compile it, though. Since the Fedora package providing libsx unfortunately is orphaned, I took the Debian source package, applied their patches as much as possible and managed to compile libsx. I can tar up what I have and send it to you if you want, just let me know. I added load average monitoring today. The code is somewhat ugly because it's a derivative of a monitor I wrote to use with i3, and that one doesn't have any graphics. Ah, two keys are currently defined: q: quit t: set update interval If you know a faster way to shift arrays, please let me know. I tried memmove() and it segfaulted, and I haven't looked into it further yet. The text display can be messed up for you, depending on what fonts it will use. With libsx, you can only get the width of a text to be printed with a particular font but not the height, so for now I simply hardcoded the positions so that it fits the font it's using here. // bwstats // This software is licensed under the GPL. // Author: lee@xxxxxxxxxxxxxxx, 2013-07-21 // // compile with something like: // gcc -lsx -lXpm -lXaw -lXt -lX11 -O2 -Wall -fomit-frame-pointer // -fexpensive-optimizations -march=native bwstat.c -o bwstat // // That should compile without warnings. Now run: // // bwstat /sys/class/net/em1/statistics/rx_bytes // /sys/class/net/em1/statistics/tx_bytes // // Replace these files with whatever files have the statistics // of the network interface you want to monitor. // #include <stdio.h> #include <sys/time.h> #include <time.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <sys/sysinfo.h> #include <libsx.h> #define BUFFSIZE 128 #define KIB (1024) #define SAMPLES 360 #define TEXT 64 typedef struct { unsigned long rx; unsigned long tx; struct timeval tv; } rxtx; typedef struct { FILE *rx; FILE *tx; char buffer[BUFFSIZE]; } filestuff; typedef struct { double rxbw; double txbw; time_t timestamp; } bandwidth; typedef struct { Widget w; int width; int height; float valuesTX[SAMPLES]; float valuesRX[SAMPLES]; float loadavgs[SAMPLES]; int index; } DrawAreaData; typedef struct { Widget w; int width; int height; } DrawAreaText; typedef struct { int txbar; int rxbar; int sepline; int loadavg; int bg; } colours; typedef struct { filestuff *t; rxtx *new; rxtx *old; bandwidth *bw; int notimeout; unsigned long tick; DrawAreaData Data; DrawAreaText Text; colours col; } mydata; typedef struct { float maxTX; float maxRX; float maxAV; } maxtxrx; typedef struct { int days; int hours; int minutes; int seconds; } updays; void sec2days(long seconds, updays *ud) { ud->days = seconds / 86400; seconds -= 86400 * ud->days; ud->hours = seconds / 3600; seconds -= 3600 * ud->hours; ud->minutes = seconds / 60; ud->seconds = seconds - 60 * ud->minutes; } int getstat(filestuff *f, rxtx *s) { int ret = 0; if(gettimeofday( &(s->tv), NULL) ) { perror("gettimeofday"); ret = 3; } else { bzero(f->buffer, BUFFSIZE); int c; unsigned long offset = 0; while( ( ( c = fgetc(f->rx) ) != EOF) && offset < BUFFSIZE) { *(f->buffer + offset) = c; offset++; } if(offset < BUFFSIZE) { s->rx = atol(f->buffer); bzero(f->buffer, BUFFSIZE); offset = 0; while( ( ( c = fgetc(f->tx) ) != EOF) && offset < BUFFSIZE) { *(f->buffer + offset) = c; offset++; } if(offset < BUFFSIZE) { s->tx = atol(f->buffer); } else { ret = 2; } } else { ret = 1; } } return ret; } void delta(rxtx *old, rxtx *new, bandwidth *bw) { unsigned long drx = new->rx - old->rx; unsigned long dtx = new->tx - old->tx; double dtime = (double)(new->tv.tv_sec - old->tv.tv_sec) + (double)( (double)(new->tv.tv_usec - old->tv.tv_usec) / (double)1000000.0); bw->rxbw = (double)drx / dtime; bw->txbw = (double)dtx / dtime; bw->timestamp = new->tv.tv_sec; } void array_shift_left(float arrayRX[], float arrayTX[], float arrayAV[], int idx) { int n = 1; while(n <= idx) { arrayRX[n] = arrayRX[n + 1]; arrayTX[n] = arrayTX[n + 1]; arrayAV[n] = arrayAV[n + 1]; n++; } } void get_data(mydata *d) { rewind(d->t->rx); rewind(d->t->tx); if( !getstat(d->t, d->new) ) { delta(d->old, d->new, d->bw); memcpy(d->old, d->new, sizeof(rxtx) ); double loads[1]; if(getloadavg(loads, 1) != 1) { loads[0] = 0.0; puts("error: getloadavg() failed"); } if(d->Data.index < SAMPLES - 1) { d->Data.valuesRX[d->Data.index] = (float)(d->bw->rxbw / (double)KIB); d->Data.valuesTX[d->Data.index] = (float)(d->bw->txbw / (double)KIB); d->Data.loadavgs[d->Data.index] = (float)loads[0]; (d->Data.index)++; } else { array_shift_left(d->Data.valuesRX, d->Data.valuesTX, d->Data.loadavgs, d->Data.index); d->Data.valuesRX[d->Data.index] = (float)(d->bw->rxbw / (double)KIB); d->Data.valuesTX[d->Data.index] = (float)(d->bw->txbw / (double)KIB); d->Data.loadavgs[d->Data.index] = (float)loads[0]; } } else { puts("error: getstat() failed"); } } void get_maxval(float vTX[], float vRX[], float vAV[], maxtxrx *m, int idx) { m->maxTX = m->maxRX = m->maxAV = 0.0; int n = 0; while(n <= idx) { if(m->maxTX < vTX[n] ) { m->maxTX = vTX[n]; } if(m->maxRX < vRX[n] ) { m->maxRX = vRX[n]; } if(m->maxAV < vAV[n] ) { m->maxAV = vAV[n]; } n++; } } float comp_percentage(float ref, float this) { return ( (ref == 0.0) ? 0 : (int)(this * 100.0 / ref + 0.5) ); } int comp_percent(float from, float percentage) { return (int)(percentage * from / 100.0 + 0.5); } void draw_bar(void *data) { mydata *d = data; SetDrawArea(d->Data.w); SetColor(d->col.sepline); SetLineWidth(2); ClearDrawArea(); int height = d->Data.height; --height; int halfheight = height / 2; DrawLine(0, halfheight, d->Data.width, halfheight); SetLineWidth(d->Data.width / SAMPLES); int idx = d->Data.index; maxtxrx m; get_maxval(d->Data.valuesTX, d->Data.valuesRX, d->Data.loadavgs, &m, idx); XPoint pline[SAMPLES]; int n = 0; while(n <= idx) { float percentage = comp_percentage(m.maxTX, d->Data.valuesTX[n] ); int h = comp_percent( (float)halfheight, percentage); SetColor(d->col.txbar); DrawLine(n, halfheight - 1, n, halfheight - h); percentage = comp_percentage(m.maxRX, d->Data.valuesRX[n] ); h = comp_percent( (float)halfheight, percentage); SetColor(d->col.rxbar); DrawLine(n, halfheight + 1, n, halfheight + h); percentage = comp_percentage(m.maxAV, d->Data.loadavgs[n] ); h = comp_percent( (float)height, percentage); pline[n].x = n; pline[n].y = height - h; n++; } SetColor(d->col.loadavg); DrawPolyline(pline, idx); // SyncDisplay(); SetDrawArea(d->Text.w); ClearDrawArea(); idx -= idx < SAMPLES - 1; double loads[3]; if(getloadavg(loads, 3) == 3) { char txt[TEXT * 2]; snprintf(txt, TEXT * 2 - 1, "rx: %8.2f (%8.2f) tx: %8.2f (%8.2f)", d->Data.valuesRX[idx], m.maxRX, d->Data.valuesTX[idx], m.maxTX); SetColor(d->col.txbar); DrawText(txt, 8, 14); struct sysinfo syi; updays up; if( !sysinfo( &syi) ) { sec2days(syi.uptime, &up); } else { memset( &up, 0, sizeof(updays) ); } snprintf(txt, TEXT * 2 - 1, "%5.2f %5.2f %5.2f (%5.2f), up: %d:%02d:%02d:%02d", d->Data.loadavgs[idx], loads[1], loads[2], m.maxAV, up.days, up.hours, up.minutes, up.seconds); SetColor(d->col.loadavg); DrawText(txt, 12, 28); } else { char txt[TEXT * 2]; snprintf(txt, TEXT * 2 - 1, "rx: %8.2f (%8.2f) tx: %8.2f (%8.2f), %5.2f (%5.2f)", d->Data.valuesRX[idx], m.maxRX, d->Data.valuesTX[idx], m.maxTX, d->Data.loadavgs[idx], m.maxAV); DrawText(txt, 10, d->Text.height - 10); puts("error: getloadavg() failed"); } // SyncDisplay(); } void update_display(void *data) { mydata *d = data; if( !(d->notimeout) ) { get_data(d); } draw_bar(d); if( !(d->notimeout) ) { AddTimeOut(d->tick, update_display, data); } else { d->notimeout = 0; } } void cleanup(void *data) { mydata *d = data; if(d->t->rx != NULL) { fclose(d->t->rx); } if(d->t->tx != NULL) { fclose(d->t->tx); } } void keypress(Widget w, char *input, int up_or_down, void *data) { if( *input == 'q') { mydata *d = data; cleanup(d); exit(0); } if( ( *input == 't') && up_or_down) { char txt[TEXT]; mydata *d = data; snprintf(txt, TEXT - 1, "%f", (float)(d->tick / 1000.0) ); char *enter = GetText("Update Interval", txt, 128, 50); if(enter) { d->tick = (unsigned long)(atof(enter) * 1000.0); if(d->tick <= 0) { d->tick = 1000; } } } } void cb_drawarea(Widget w, int width, int height, void *data) { mydata *d = data; if(w == d->Data.w) { d->Data.width = width; d->Data.height = height; } if(w == d->Text.w) { d->Text.width = width; d->Text.height = height; } d->notimeout = 1; update_display(data); } int main(int argc, char *argv[] ) { if( !OpenDisplay(argc, argv) ) { puts("cannot open display"); exit(1); } if(argc != 3) { puts("usage: bwstat interval /path/to/statsfile-rx /path/to/statsfile-tx"); exit(1); } filestuff t; t.rx = fopen(argv[1], "r"); if(t.rx == NULL) { perror(argv[1] ); exit(2); } t.tx = fopen(argv[2], "r"); if(t.tx == NULL) { perror(argv[2] ); fclose(t.rx); exit(3); } bandwidth bw; memset( &bw, 0, sizeof(bandwidth) ); rxtx new; memset( &new, 0, sizeof(rxtx) ); rxtx old; memset( &old, 0, sizeof(rxtx) ); mydata display; memset( &display, 0, sizeof(mydata) ); display.t = &t; display.new = &new; display.old = &old; display.bw = &bw; display.Data.width = 360; display.Data.height = 201; display.Text.width = display.Data.width; display.Text.height = 32; display.notimeout = 0; display.tick = 5000; display.Data.w = MakeDrawArea(display.Data.width, display.Data.height, cb_drawarea, &display); display.Text.w = MakeDrawArea(display.Text.width, display.Text.height, cb_drawarea, &display); SetWidgetPos(display.Text.w, PLACE_UNDER, display.Data.w, NO_CARE, NULL); SetKeypressCB(display.Data.w, keypress); SetKeypressCB(display.Text.w, keypress); ShowDisplay(); display.col.rxbar = GetNamedColor("green"); display.col.txbar = GetNamedColor("darkgreen"); display.col.sepline = GetNamedColor("darkgoldenrod"); display.col.loadavg = GetNamedColor("lightslategray"); display.col.bg = GetNamedColor("gray6"); SetBgColor(display.Data.w, display.col.bg); SetBgColor(display.Text.w, display.col.bg); update_display( (void *)( &display) ); MainLoop(); exit(0); } -- Fedora release 19 (Schrödinger’s Cat) -- users mailing list users@xxxxxxxxxxxxxxxxxxxxxxx To unsubscribe or change subscription options: https://admin.fedoraproject.org/mailman/listinfo/users Guidelines: http://fedoraproject.org/wiki/Mailing_list_guidelines Have a question? Ask away: http://ask.fedoraproject.org