11 #define MAXNUM 8 /* maximum number of numbers on data line */
13 typedef struct Graph Graph;
14 typedef struct Machine Machine;
15 typedef struct Req Req;
28 void (*newvalue)(Machine*, long*, long*, long*);
29 void (*update)(Graph*, long, long, long);
48 int seq; /* sequence number */
49 vlong time; /* time sent */
83 Ysqueeze = 2, /* vertical squeezing of label text */
84 Labspace = 2, /* room around label */
85 Dot = 2, /* height of dot */
86 Opwid = 5, /* strlen("add ") or strlen("drop ") */
98 char *menu2str[Nmenu2+1] = {
105 void rttval(Machine*, long*, long*, long*);
106 void lostval(Machine*, long*, long*, long*);
108 Menu menu2 = {menu2str, nil};
110 void (*newvaluefn[Nmenu2])(Machine*, long*, long*, long*) = {
115 Image *cols[Ncolor][3];
120 int parity; /* toggled to avoid patterns in textured background */
122 int ngraph; /* totaly number is ngraph*nmach */
128 void startproc(void (*)(void*), void*);
131 int which2index(int);
132 int index2which(int);
140 for(i=0; i<NPROC; i++)
141 if(pids[i] && pids[i]!=pid)
142 postnote(PNPROC, pids[i], "kill");
152 fprint(2, "%s: out of memory allocating %ld: %r\n", argv0, sz);
160 erealloc(void *v, ulong sz)
164 fprint(2, "%s: out of memory reallocating %ld: %r\n", argv0, sz);
174 if((t = strdup(s)) == nil) {
175 fprint(2, "%s: out of memory in strdup(%.10s): %r\n", argv0, s);
182 mkcol(int i, int c0, int c1, int c2)
184 cols[i][0] = allocimagemix(display, c0, DWhite);
185 cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
186 cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
193 mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
195 mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
197 mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
199 mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
201 mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
203 cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
204 cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
205 cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
209 loadbuf(Machine *m, int *fd)
217 n = read(*fd, m->buf, sizeof m->buf);
229 label(Point p, int dy, char *text)
240 if(p.y+font->height-Ysqueeze > maxy)
242 w = chartorune(r, s);
244 w = runestringwidth(font, r);
247 runestring(screen, p, display->black, ZP, font, r);
248 p.y += font->height-Ysqueeze;
253 hashmark(Point p, int dy, long v, long vmax, char *label)
259 y = p.y + (dy*(vmax-v))/vmax;
260 draw(screen, Rect(p.x, y-1, p.x+Labspace, y+1), display->black, nil, ZP);
261 if(dy > 5*font->height)
262 string(screen, Pt(x, y-font->height/2),
263 display->black, ZP, font, label);
267 hashmarks(Point p, int dy, int which)
269 switch(index2which(which)){
271 hashmark(p, dy, rttscale(1000000), Rttmax, "1.");
272 hashmark(p, dy, rttscale(100000), Rttmax, "0.1");
273 hashmark(p, dy, rttscale(10000), Rttmax, "0.01");
274 hashmark(p, dy, rttscale(1000), Rttmax, "0.001");
277 hashmark(p, dy, 75, 100, " 75%");
278 hashmark(p, dy, 50, 100, " 50%");
279 hashmark(p, dy, 25, 100, " 25%");
287 return Pt(x+parity, 0);
291 datapoint(Graph *g, int x, long v, long vmax)
296 p.y = g->r.max.y - Dy(g->r)*v/vmax - Dot;
299 if(p.y > g->r.max.y-Dot)
300 p.y = g->r.max.y-Dot;
305 drawdatum(Graph *g, int x, long prev, long v, long vmax)
311 p = datapoint(g, x, v, vmax);
312 q = datapoint(g, x, prev, vmax);
314 draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
315 draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
316 draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
318 draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
319 draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
320 draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
326 drawmark(Graph *g, int x)
330 c = (g->colindex+1)&Ncolor;
331 draw(screen, Rect(x, g->r.min.y, x+1, g->r.max.y), cols[c][2], nil, ZP);
335 redraw(Graph *g, int vmax)
340 draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
341 for(i=1; i<Dx(g->r); i++)
342 drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
343 drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
349 if(g->overtmp != nil)
350 draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
355 drawmsg(Graph *g, char *msg)
357 if(g->overtmp == nil)
360 /* save previous contents of screen */
361 draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
364 if(strlen(msg) > g->overtmplen)
365 msg[g->overtmplen] = 0;
366 string(screen, g->overtmp->r.min, display->black, ZP, font, msg);
370 clearcursor(Graph *g)
375 if(g->overtmp == nil)
378 if(g->cursor > 0 && g->cursor < g->ndata){
379 x = g->r.max.x - g->cursor;
382 prev = g->data[g->cursor-1];
383 drawdatum(g, x, prev, g->data[g->cursor], g->vmax);
389 drawcursor(Graph *g, int x)
391 if(g->overtmp == nil)
394 draw(screen, Rect(x, g->r.min.y, x+1, g->r.max.y), cols[g->colindex][2], nil, ZP);
398 update1(Graph *g, long v, long vmax, long mark)
402 /* put back screen value sans message */
403 if(g->overflow || *g->msg){
408 draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
409 drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
411 drawmark(g, g->r.max.x-1);
412 memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
416 sprint(buf, "%ld", v);
423 if(g->cursor >= g->ndata){
435 pinglost(Machine *m, Req*)
441 pingreply(Machine *m, Req *r)
453 pingclean(Machine *m, ushort seq, vlong now, int)
458 y = 10LL*1000000000LL;
459 for(l = &m->first; *l; ){
462 if(x > y || r->seq == seq){
480 char buf[128], err[ERRMAX];
484 ip = (Icmphdr *)(buf + IPV4HDR_LEN);
485 memset(buf, 0, sizeof buf);
486 r = malloc(sizeof *r);
490 for(i = 32; i < MSGLEN; i++)
492 ip->type = EchoRequest;
495 ip->seq[1] = m->seq>>8;
499 pingclean(m, -1, nsec(), 0);
507 if(write(m->pingfd, buf, MSGLEN) < MSGLEN){
508 errstr(err, sizeof err);
509 if(strstr(err, "unreach")||strstr(err, "exceed"))
528 ip = (Icmphdr *)(buf + IPV4HDR_LEN);
529 fd = dup(m->pingfd, -1);
531 n = read(fd, buf, sizeof(buf));
536 print("bad len %d/%d\n", n, MSGLEN);
539 for(i = 32; i < MSGLEN; i++)
540 if(buf[i] != (i&0xff))
542 x = (ip->seq[1]<<8) | ip->seq[0];
543 if(ip->type != EchoReply || ip->code != 0)
546 pingclean(m, x, now, ip4->ttl);
552 initmach(Machine *m, char *name)
557 p = strchr(name, '!');
560 m->name = estrdup(p+1);
564 m->name = estrdup(p);
566 m->pingfd = dial(netmkaddr(m->name, "icmp", "1"), 0, 0, 0);
568 sysfatal("dialing %s: %r", m->name);
569 startproc(pingrcv, m);
577 x = 10.0*log10(x) - 20.0;
590 return pow(10.0, dx/10.0);
594 rttval(Machine *m, long *v, long *vmax, long *mark)
601 x = m->rttsum/m->rttmsgs;
602 m->rttsum = m->rttmsgs = 0;
612 lostval(Machine *m, long *v, long *vmax, long *mark)
616 if(m->rcvdmsgs+m->lostmsgs > 0)
617 x = (m->lostavg>>1) + (((m->lostmsgs*100)/(m->lostmsgs + m->rcvdmsgs))>>1);
621 m->lostmsgs = m->rcvdmsgs = 0;
636 alarmed(void *a, char *s)
638 if(strcmp(s, "alarm") == 0)
639 notejmp(a, catchalarm, 1);
646 fprint(2, "usage: %s machine [machine...]\n", argv0);
657 if(n > nelem(menu2str))
659 /* avoid two adjacent graphs of same color */
660 if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
663 graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
664 for(i=0; i<nmach; i++)
665 for(j=0; j<ngraph; j++)
666 graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
669 for(i=0; i<nmach; i++){
670 g = &graph[i*ngraph+(ngraph-1)];
671 memset(g, 0, sizeof(Graph));
672 g->label = menu2str[n]+Opwid;
673 g->newvalue = newvaluefn[n];
674 g->update = update1; /* no other update functions yet */
676 g->colindex = nadd%Ncolor;
683 which2index(int which)
688 for(i=0; i<ngraph; i++){
689 if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
695 fprint(2, "%s: internal error can't drop graph\n", argv0);
702 index2which(int index)
707 for(i=0; i<Nmenu2; i++){
708 if(strcmp(menu2str[i]+Opwid, graph[index].label) == 0){
714 fprint(2, "%s: internal error can't identify graph\n", argv0);
726 if(which > nelem(menu2str))
728 /* convert n to index in graph table */
729 n = which2index(which);
731 graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
732 for(i=0; i<nmach; i++){
734 graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
735 free(ograph[i*ngraph+j].data);
736 freeimage(ograph[i*ngraph+j].overtmp);
737 for(j++; j<ngraph; j++)
738 graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
746 addmachine(char *name)
749 fprint(2, "%s: internal error: ngraph>0 in addmachine()\n", argv0);
753 sysfatal("too many machines");
754 initmach(&mach[nmach++], name);
761 int i, j, n, startx, starty, x, y, dx, dy, hashdx, ondata;
767 draw(screen, screen->r, display->white, nil, ZP);
769 /* label left edge */
771 y = screen->r.min.y + Labspace+font->height+Labspace;
772 dy = (screen->r.max.y - y)/ngraph;
773 dx = Labspace+stringwidth(font, "0")+Labspace;
776 for(i=0; i<ngraph; i++,y+=dy){
777 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
778 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
779 label(Pt(x, y), dy, graph[i].label);
780 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
783 /* label right edge */
784 dx = Labspace+stringwidth(font, "0.001")+Labspace;
786 x = screen->r.max.x - dx;
787 y = screen->r.min.y + Labspace+font->height+Labspace;
788 for(i=0; i<ngraph; i++,y+=dy){
789 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
790 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
791 hashmarks(Pt(x, y), dy, i);
792 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
796 dx = (screen->r.max.x - dx - startx)/nmach;
797 for(x=startx, i=0; i<nmach; i++,x+=dx){
798 draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
799 j = dx/stringwidth(font, "0");
801 if(n>1 && j>=1+3+(n>10)+(n>100)){ /* first char of name + (n) */
802 j -= 3+(n>10)+(n>100);
805 snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].name, n);
807 snprint(buf, sizeof buf, "%.*s", j, mach[i].name);
808 string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP,
811 /* draw last vertical line */
813 Rect(screen->r.max.x-hashdx-1, starty-1, screen->r.max.x-hashdx, screen->r.max.y),
814 display->black, nil, ZP);
817 for(i=0; i<nmach; i++){
818 machr = Rect(startx+i*dx, starty, screen->r.max.x, screen->r.max.y);
820 machr.max.x = startx+(i+1)*dx - 1;
822 machr.max.x = screen->r.max.x - hashdx - 1;
824 for(j=0; j<ngraph; j++, y+=dy){
825 g = &graph[i*ngraph+j];
828 g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
829 g->data = erealloc(g->data, g->ndata*sizeof(long));
830 if(g->ndata > ondata)
831 memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(long));
835 g->r.max.y = y+dy - 1;
837 g->r.max.y = screen->r.max.y;
838 draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
841 freeimage(g->overtmp);
845 r.max.y = r.min.y+font->height;
846 n = (g->r.max.x - r.min.x)/stringwidth(font, "9");
850 r.max.x = r.min.x+stringwidth(font, "9")*n;
852 g->overtmp = allocimage(display, r, screen->chan, 0, -1);
854 g->newvalue(g->mach, &v, &vmax, &mark);
859 flushimage(display, 1);
865 lockdisplay(display);
866 if(new && getwindow(display, Refnone) < 0) {
867 fprint(2, "%s: can't reattach to window\n", argv0);
871 unlockdisplay(display);
879 for(i=0; i<Nmenu2; i++)
881 memmove(menu2str[i], "drop ", Opwid);
883 memmove(menu2str[i], "add ", Opwid);
884 i = emenuhit(3, m, &menu2);
902 for(i = 0; i < ngraph*nmach; i++){
903 if(ptinrect(m->xy, graph[i].r))
906 if(i == ngraph*nmach)
910 if(g->overtmp == nil)
913 /* clear any previous message and cursor */
914 if(g->overflow || *g->msg){
920 dx = g->r.max.x - m->xy.x;
922 dt = dx*pinginterval;
923 e = &g->msg[sizeof(g->msg)];
924 seprint(g->msg, e, "%s", ctime(starttime-dt/1000)+11);
928 switch(index2which(i)){
930 f = rttunscale(g->data[dx]);
931 seprint(g->msg+n, e, " %3.3g", f/1000000);
934 seprint(g->msg+n, e, " %ld%%", g->data[dx]);
939 drawcursor(g, m->xy.x);
949 if(mouse.buttons == 4){
950 lockdisplay(display);
952 unlockdisplay(display);
953 } else if(mouse.buttons == 1){
954 lockdisplay(display);
956 unlockdisplay(display);
962 startproc(void (*f)(void*), void *arg)
966 switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
968 fprint(2, "%s: fork failed: %r\n", argv0);
969 killall("fork failed");
972 killall("process died");
979 main(int argc, char *argv[])
983 char flags[10], *f, *p;
985 fmtinstall('V', eipfmt);
988 pinginterval = 5000; /* 5 seconds */
994 pinginterval = atoi(p);
997 if(f - flags >= sizeof(flags)-1)
1004 for(i=0; i<argc; i++)
1005 addmachine(argv[i]);
1007 for(f = flags; *f; f++)
1023 for(i=0; i<nmach; i++)
1024 for(j=0; j<ngraph; j++)
1025 graph[i*ngraph+j].mach = &mach[i];
1027 if(initdraw(nil, nil, argv0) < 0){
1028 fprint(2, "%s: initdraw failed: %r\n", argv0);
1034 startproc(mouseproc, 0);
1035 display->locking = 1; /* tell library we're using the display lock */
1039 starttime = time(0);
1041 unlockdisplay(display); /* display is still locked from initdraw() */
1043 lockdisplay(display);
1047 for(i=0; i<nmach*ngraph; i++){
1048 graph[i].newvalue(graph[i].mach, &v, &vmax, &mark);
1049 graph[i].update(&graph[i], v, vmax, mark);
1051 starttime = time(0);
1053 flushimage(display, 1);
1054 unlockdisplay(display);
1055 pingsend(&mach[j%nmach]);
1056 sleep(pinginterval/nmach);