1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <auth.h>
5 #include <fcall.h>
6 #include <draw.h>
7 #include <event.h>
8 #include <ip.h>
9 #include "icmp.h"
10
11 #define MAXNUM 8 /* maximum number of numbers on data line */
12
13 typedef struct Graph Graph;
14 typedef struct Machine Machine;
15 typedef struct Req Req;
16
17 enum {
18 Gmsglen = 16,
19 };
20
21 struct Graph
22 {
23 int colindex;
24 Rectangle r;
25 long *data;
26 int ndata;
27 char *label;
28 void (*newvalue)(Machine*, long*, long*, long*);
29 void (*update)(Graph*, long, long, long);
30 Machine *mach;
31 int overflow;
32 Image *overtmp;
33 int overtmplen;
34 char msg[Gmsglen];
35 int cursor;
36 int vmax;
37 };
38
39 enum
40 {
41 MSGLEN = 64,
42
43 Rttmax = 50,
44 };
45
46 struct Req
47 {
48 int seq; /* sequence number */
49 vlong time; /* time sent */
50 // int rtt;
51 Req *next;
52 };
53
54 struct Machine
55 {
56 Lock;
57 char *name;
58 int pingfd;
59 int nproc;
60
61 int rttmsgs;
62 ulong rttsum;
63 ulong lastrtt;
64
65 int lostmsgs;
66 int rcvdmsgs;
67 ulong lostavg;
68 int unreachable;
69
70 ushort seq;
71 Req *first;
72 Req *last;
73 Req *rcvd;
74
75 char buf[1024];
76 char *bufp;
77 char *ebufp;
78 };
79
80 enum
81 {
82 Ncolor = 6,
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 ") */
87 NPROC = 128,
88 NMACH = 32,
89 };
90
91 enum Menu2
92 {
93 Mrtt,
94 Mlost,
95 Nmenu2,
96 };
97
98 char *menu2str[Nmenu2+1] = {
99 "add sec rtt",
100 "add % lost ",
101 nil,
102 };
103
104
105 void rttval(Machine*, long*, long*, long*);
106 void lostval(Machine*, long*, long*, long*);
107
108 Menu menu2 = {menu2str, nil};
109 int present[Nmenu2];
110 void (*newvaluefn[Nmenu2])(Machine*, long*, long*, long*) = {
111 rttval,
112 lostval,
113 };
114
115 Image *cols[Ncolor][3];
116 Graph *graph;
117 Machine mach[NMACH];
118 Font *mediumfont;
119 int pids[NPROC];
120 int npid;
121 int parity; /* toggled to avoid patterns in textured background */
122 int nmach;
123 int ngraph; /* totaly number is ngraph*nmach */
124 long starttime;
125 int pinginterval;
126
127 void dropgraph(int);
128 void addgraph(int);
129 void startproc(void (*)(void*), void*);
130 void resize(void);
131 long rttscale(long);
132 int which2index(int);
133 int index2which(int);
134
135 void
killall(char * s)136 killall(char *s)
137 {
138 int i, pid;
139
140 pid = getpid();
141 for(i=0; i<NPROC; i++)
142 if(pids[i] && pids[i]!=pid)
143 postnote(PNPROC, pids[i], "kill");
144 exits(s);
145 }
146
147 void*
emalloc(ulong sz)148 emalloc(ulong sz)
149 {
150 void *v;
151 v = malloc(sz);
152 if(v == nil) {
153 fprint(2, "%s: out of memory allocating %ld: %r\n", argv0, sz);
154 killall("mem");
155 }
156 memset(v, 0, sz);
157 return v;
158 }
159
160 void*
erealloc(void * v,ulong sz)161 erealloc(void *v, ulong sz)
162 {
163 v = realloc(v, sz);
164 if(v == nil) {
165 fprint(2, "%s: out of memory reallocating %ld: %r\n", argv0, sz);
166 killall("mem");
167 }
168 return v;
169 }
170
171 char*
estrdup(char * s)172 estrdup(char *s)
173 {
174 char *t;
175 if((t = strdup(s)) == nil) {
176 fprint(2, "%s: out of memory in strdup(%.10s): %r\n", argv0, s);
177 killall("mem");
178 }
179 return t;
180 }
181
182 void
mkcol(int i,int c0,int c1,int c2)183 mkcol(int i, int c0, int c1, int c2)
184 {
185 cols[i][0] = allocimagemix(display, c0, DWhite);
186 cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
187 cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
188 }
189
190 void
colinit(void)191 colinit(void)
192 {
193 mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font");
194 if(mediumfont == nil)
195 mediumfont = font;
196
197 /* Peach */
198 mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
199 /* Aqua */
200 mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
201 /* Yellow */
202 mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
203 /* Green */
204 mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
205 /* Blue */
206 mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
207 /* Grey */
208 cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
209 cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
210 cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
211 }
212
213 int
loadbuf(Machine * m,int * fd)214 loadbuf(Machine *m, int *fd)
215 {
216 int n;
217
218
219 if(*fd < 0)
220 return 0;
221 seek(*fd, 0, 0);
222 n = read(*fd, m->buf, sizeof m->buf);
223 if(n <= 0){
224 close(*fd);
225 *fd = -1;
226 return 0;
227 }
228 m->bufp = m->buf;
229 m->ebufp = m->buf+n;
230 return 1;
231 }
232
233 void
label(Point p,int dy,char * text)234 label(Point p, int dy, char *text)
235 {
236 char *s;
237 Rune r[2];
238 int w, maxw, maxy;
239
240 p.x += Labspace;
241 maxy = p.y+dy;
242 maxw = 0;
243 r[1] = '\0';
244 for(s=text; *s; ){
245 if(p.y+mediumfont->height-Ysqueeze > maxy)
246 break;
247 w = chartorune(r, s);
248 s += w;
249 w = runestringwidth(mediumfont, r);
250 if(w > maxw)
251 maxw = w;
252 runestring(screen, p, display->black, ZP, mediumfont, r);
253 p.y += mediumfont->height-Ysqueeze;
254 }
255 }
256
257 void
hashmark(Point p,int dy,long v,long vmax,char * label)258 hashmark(Point p, int dy, long v, long vmax, char *label)
259 {
260 int y;
261 int x;
262
263 x = p.x + Labspace;
264 y = p.y + (dy*(vmax-v))/vmax;
265 draw(screen, Rect(p.x, y-1, p.x+Labspace, y+1), display->black, nil, ZP);
266 if(dy > 5*mediumfont->height)
267 string(screen, Pt(x, y-mediumfont->height/2),
268 display->black, ZP, mediumfont, label);
269 }
270
271 void
hashmarks(Point p,int dy,int which)272 hashmarks(Point p, int dy, int which)
273 {
274 switch(index2which(which)){
275 case Mrtt:
276 hashmark(p, dy, rttscale(1000000), Rttmax, "1.");
277 hashmark(p, dy, rttscale(100000), Rttmax, "0.1");
278 hashmark(p, dy, rttscale(10000), Rttmax, "0.01");
279 hashmark(p, dy, rttscale(1000), Rttmax, "0.001");
280 break;
281 case Mlost:
282 hashmark(p, dy, 75, 100, " 75%");
283 hashmark(p, dy, 50, 100, " 50%");
284 hashmark(p, dy, 25, 100, " 25%");
285 break;
286 }
287 }
288
289 Point
paritypt(int x)290 paritypt(int x)
291 {
292 return Pt(x+parity, 0);
293 }
294
295 Point
datapoint(Graph * g,int x,long v,long vmax)296 datapoint(Graph *g, int x, long v, long vmax)
297 {
298 Point p;
299
300 p.x = x;
301 p.y = g->r.max.y - Dy(g->r)*v/vmax - Dot;
302 if(p.y < g->r.min.y)
303 p.y = g->r.min.y;
304 if(p.y > g->r.max.y-Dot)
305 p.y = g->r.max.y-Dot;
306 return p;
307 }
308
309 void
drawdatum(Graph * g,int x,long prev,long v,long vmax)310 drawdatum(Graph *g, int x, long prev, long v, long vmax)
311 {
312 int c;
313 Point p, q;
314
315 c = g->colindex;
316 p = datapoint(g, x, v, vmax);
317 q = datapoint(g, x, prev, vmax);
318 if(p.y < q.y){
319 draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
320 draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
321 draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
322 }else{
323 draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
324 draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
325 draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
326 }
327 g->vmax = vmax;
328 }
329
330 void
drawmark(Graph * g,int x)331 drawmark(Graph *g, int x)
332 {
333 int c;
334
335 c = (g->colindex+1)&Ncolor;
336 draw(screen, Rect(x, g->r.min.y, x+1, g->r.max.y), cols[c][2], nil, ZP);
337 }
338
339 void
redraw(Graph * g,int vmax)340 redraw(Graph *g, int vmax)
341 {
342 int i, c;
343
344 c = g->colindex;
345 draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
346 for(i=1; i<Dx(g->r); i++)
347 drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
348 drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
349 }
350
351 void
clearmsg(Graph * g)352 clearmsg(Graph *g)
353 {
354 if(g->overtmp != nil)
355 draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
356 g->overflow = 0;
357 }
358
359 void
drawmsg(Graph * g,char * msg)360 drawmsg(Graph *g, char *msg)
361 {
362 if(g->overtmp == nil)
363 return;
364
365 /* save previous contents of screen */
366 draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
367
368 /* draw message */
369 if(strlen(msg) > g->overtmplen)
370 msg[g->overtmplen] = 0;
371 string(screen, g->overtmp->r.min, display->black, ZP, mediumfont, msg);
372 }
373
374 void
clearcursor(Graph * g)375 clearcursor(Graph *g)
376 {
377 int x;
378 long prev;
379
380 if(g->overtmp == nil)
381 return;
382
383 if(g->cursor > 0 && g->cursor < g->ndata){
384 x = g->r.max.x - g->cursor;
385 prev = 0;
386 if(g->cursor > 0)
387 prev = g->data[g->cursor-1];
388 drawdatum(g, x, prev, g->data[g->cursor], g->vmax);
389 g->cursor = -1;
390 }
391 }
392
393 void
drawcursor(Graph * g,int x)394 drawcursor(Graph *g, int x)
395 {
396 if(g->overtmp == nil)
397 return;
398
399 draw(screen, Rect(x, g->r.min.y, x+1, g->r.max.y), cols[g->colindex][2], nil, ZP);
400 }
401
402 void
update1(Graph * g,long v,long vmax,long mark)403 update1(Graph *g, long v, long vmax, long mark)
404 {
405 char buf[Gmsglen];
406
407 /* put back screen value sans message */
408 if(g->overflow || *g->msg){
409 clearmsg(g);
410 g->overflow = 0;
411 }
412
413 draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
414 drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
415 if(mark)
416 drawmark(g, g->r.max.x-1);
417 memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
418 g->data[0] = v;
419 if(v>vmax){
420 g->overflow = 1;
421 sprint(buf, "%ld", v);
422 drawmsg(g, buf);
423 } else if(*g->msg)
424 drawmsg(g, g->msg);
425
426 if(g->cursor >= 0){
427 g->cursor++;
428 if(g->cursor >= g->ndata){
429 g->cursor = -1;
430 if(*g->msg){
431 clearmsg(g);
432 *g->msg = 0;
433 }
434 }
435 }
436
437 }
438
439 void
pinglost(Machine * m,Req *)440 pinglost(Machine *m, Req*)
441 {
442 m->lostmsgs++;
443 }
444
445 void
pingreply(Machine * m,Req * r)446 pingreply(Machine *m, Req *r)
447 {
448 ulong x;
449
450 x = r->time/1000LL;
451 m->rttsum += x;
452 m->rcvdmsgs++;
453 m->rttmsgs++;
454 }
455
456
457 void
pingclean(Machine * m,ushort seq,vlong now,int)458 pingclean(Machine *m, ushort seq, vlong now, int)
459 {
460 Req **l, *r;
461 vlong x, y;
462
463 y = 10LL*1000000000LL;
464 for(l = &m->first; *l; ){
465 r = *l;
466 x = now - r->time;
467 if(x > y || r->seq == seq){
468 *l = r->next;
469 r->time = x;
470 if(r->seq != seq)
471 pinglost(m, r);
472 else
473 pingreply(m, r);
474 free(r);
475 } else
476 l = &(r->next);
477 }
478 }
479
480 /* IPv4 only */
481 void
pingsend(Machine * m)482 pingsend(Machine *m)
483 {
484 int i;
485 char buf[128], err[ERRMAX];
486 Icmphdr *ip;
487 Req *r;
488
489 ip = (Icmphdr *)(buf + IPV4HDR_LEN);
490 memset(buf, 0, sizeof buf);
491 r = malloc(sizeof *r);
492 if(r == nil)
493 return;
494
495 for(i = 32; i < MSGLEN; i++)
496 buf[i] = i;
497 ip->type = EchoRequest;
498 ip->code = 0;
499 ip->seq[0] = m->seq;
500 ip->seq[1] = m->seq>>8;
501 r->seq = m->seq;
502 r->next = nil;
503 lock(m);
504 pingclean(m, -1, nsec(), 0);
505 if(m->first == nil)
506 m->first = r;
507 else
508 m->last->next = r;
509 m->last = r;
510 r->time = nsec();
511 unlock(m);
512 if(write(m->pingfd, buf, MSGLEN) < MSGLEN){
513 errstr(err, sizeof err);
514 if(strstr(err, "unreach")||strstr(err, "exceed"))
515 m->unreachable++;
516 }
517 m->seq++;
518 }
519
520 /* IPv4 only */
521 void
pingrcv(void * arg)522 pingrcv(void *arg)
523 {
524 int i, n, fd;
525 uchar buf[512];
526 ushort x;
527 vlong now;
528 Icmphdr *ip;
529 Ip4hdr *ip4;
530 Machine *m = arg;
531
532 ip4 = (Ip4hdr *)buf;
533 ip = (Icmphdr *)(buf + IPV4HDR_LEN);
534 fd = dup(m->pingfd, -1);
535 for(;;){
536 n = read(fd, buf, sizeof(buf));
537 now = nsec();
538 if(n <= 0)
539 continue;
540 if(n < MSGLEN){
541 print("bad len %d/%d\n", n, MSGLEN);
542 continue;
543 }
544 for(i = 32; i < MSGLEN; i++)
545 if(buf[i] != (i&0xff))
546 continue;
547 x = (ip->seq[1]<<8) | ip->seq[0];
548 if(ip->type != EchoReply || ip->code != 0)
549 continue;
550 lock(m);
551 pingclean(m, x, now, ip4->ttl);
552 unlock(m);
553 }
554 }
555
556 void
initmach(Machine * m,char * name)557 initmach(Machine *m, char *name)
558 {
559 char *p;
560
561 srand(time(0));
562 p = strchr(name, '!');
563 if(p){
564 p++;
565 m->name = estrdup(p+1);
566 }else
567 p = name;
568
569 m->name = estrdup(p);
570 m->nproc = 1;
571 m->pingfd = dial(netmkaddr(m->name, "icmp", "1"), 0, 0, 0);
572 if(m->pingfd < 0)
573 sysfatal("dialing %s: %r", m->name);
574 startproc(pingrcv, m);
575 }
576
577 long
rttscale(long x)578 rttscale(long x)
579 {
580 if(x == 0)
581 return 0;
582 x = 10.0*log10(x) - 20.0;
583 if(x < 0)
584 x = 0;
585 return x;
586 }
587
588 double
rttunscale(long x)589 rttunscale(long x)
590 {
591 double dx;
592
593 x += 20;
594 dx = x;
595 return pow(10.0, dx/10.0);
596 }
597
598 void
rttval(Machine * m,long * v,long * vmax,long * mark)599 rttval(Machine *m, long *v, long *vmax, long *mark)
600 {
601 ulong x;
602
603 if(m->rttmsgs == 0){
604 x = m->lastrtt;
605 } else {
606 x = m->rttsum/m->rttmsgs;
607 m->rttsum = m->rttmsgs = 0;
608 m->lastrtt = x;
609 }
610
611 *v = rttscale(x);
612 *vmax = Rttmax;
613 *mark = 0;
614 }
615
616 void
lostval(Machine * m,long * v,long * vmax,long * mark)617 lostval(Machine *m, long *v, long *vmax, long *mark)
618 {
619 ulong x;
620
621 if(m->rcvdmsgs+m->lostmsgs > 0)
622 x = (m->lostavg>>1) + (((m->lostmsgs*100)/(m->lostmsgs + m->rcvdmsgs))>>1);
623 else
624 x = m->lostavg;
625 m->lostavg = x;
626 m->lostmsgs = m->rcvdmsgs = 0;
627
628 if(m->unreachable){
629 m->unreachable = 0;
630 *mark = 100;
631 } else
632 *mark = 0;
633
634 *v = x;
635 *vmax = 100;
636 }
637
638 jmp_buf catchalarm;
639
640 void
alarmed(void * a,char * s)641 alarmed(void *a, char *s)
642 {
643 if(strcmp(s, "alarm") == 0)
644 notejmp(a, catchalarm, 1);
645 noted(NDFLT);
646 }
647
648 void
usage(void)649 usage(void)
650 {
651 fprint(2, "usage: %s machine [machine...]\n", argv0);
652 exits("usage");
653 }
654
655 void
addgraph(int n)656 addgraph(int n)
657 {
658 Graph *g, *ograph;
659 int i, j;
660 static int nadd;
661
662 if(n > nelem(menu2str))
663 abort();
664 /* avoid two adjacent graphs of same color */
665 if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
666 nadd++;
667 ograph = graph;
668 graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
669 for(i=0; i<nmach; i++)
670 for(j=0; j<ngraph; j++)
671 graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
672 free(ograph);
673 ngraph++;
674 for(i=0; i<nmach; i++){
675 g = &graph[i*ngraph+(ngraph-1)];
676 memset(g, 0, sizeof(Graph));
677 g->label = menu2str[n]+Opwid;
678 g->newvalue = newvaluefn[n];
679 g->update = update1; /* no other update functions yet */
680 g->mach = &mach[i];
681 g->colindex = nadd%Ncolor;
682 }
683 present[n] = 1;
684 nadd++;
685 }
686
687 int
which2index(int which)688 which2index(int which)
689 {
690 int i, n;
691
692 n = -1;
693 for(i=0; i<ngraph; i++){
694 if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
695 n = i;
696 break;
697 }
698 }
699 if(n < 0){
700 fprint(2, "%s: internal error can't drop graph\n", argv0);
701 killall("error");
702 }
703 return n;
704 }
705
706 int
index2which(int index)707 index2which(int index)
708 {
709 int i, n;
710
711 n = -1;
712 for(i=0; i<Nmenu2; i++){
713 if(strcmp(menu2str[i]+Opwid, graph[index].label) == 0){
714 n = i;
715 break;
716 }
717 }
718 if(n < 0){
719 fprint(2, "%s: internal error can't identify graph\n", argv0);
720 killall("error");
721 }
722 return n;
723 }
724
725 void
dropgraph(int which)726 dropgraph(int which)
727 {
728 Graph *ograph;
729 int i, j, n;
730
731 if(which > nelem(menu2str))
732 abort();
733 /* convert n to index in graph table */
734 n = which2index(which);
735 ograph = graph;
736 graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
737 for(i=0; i<nmach; i++){
738 for(j=0; j<n; j++)
739 graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
740 free(ograph[i*ngraph+j].data);
741 freeimage(ograph[i*ngraph+j].overtmp);
742 for(j++; j<ngraph; j++)
743 graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
744 }
745 free(ograph);
746 ngraph--;
747 present[which] = 0;
748 }
749
750 void
addmachine(char * name)751 addmachine(char *name)
752 {
753 if(ngraph > 0){
754 fprint(2, "%s: internal error: ngraph>0 in addmachine()\n", argv0);
755 usage();
756 }
757 if(nmach == NMACH)
758 sysfatal("too many machines");
759 initmach(&mach[nmach++], name);
760 }
761
762
763 void
resize(void)764 resize(void)
765 {
766 int i, j, n, startx, starty, x, y, dx, dy, hashdx, ondata;
767 Graph *g;
768 Rectangle machr, r;
769 long v, vmax, mark;
770 char buf[128];
771
772 draw(screen, screen->r, display->white, nil, ZP);
773
774 /* label left edge */
775 x = screen->r.min.x;
776 y = screen->r.min.y + Labspace+mediumfont->height+Labspace;
777 dy = (screen->r.max.y - y)/ngraph;
778 dx = Labspace+stringwidth(mediumfont, "0")+Labspace;
779 startx = x+dx+1;
780 starty = y;
781 for(i=0; i<ngraph; i++,y+=dy){
782 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
783 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
784 label(Pt(x, y), dy, graph[i].label);
785 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
786 }
787
788 /* label right edge */
789 dx = Labspace+stringwidth(mediumfont, "0.001")+Labspace;
790 hashdx = dx;
791 x = screen->r.max.x - dx;
792 y = screen->r.min.y + Labspace+mediumfont->height+Labspace;
793 for(i=0; i<ngraph; i++,y+=dy){
794 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
795 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
796 hashmarks(Pt(x, y), dy, i);
797 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
798 }
799
800 /* label top edge */
801 dx = (screen->r.max.x - dx - startx)/nmach;
802 for(x=startx, i=0; i<nmach; i++,x+=dx){
803 draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
804 j = dx/stringwidth(mediumfont, "0");
805 n = mach[i].nproc;
806 if(n>1 && j>=1+3+(n>10)+(n>100)){ /* first char of name + (n) */
807 j -= 3+(n>10)+(n>100);
808 if(j <= 0)
809 j = 1;
810 snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].name, n);
811 }else
812 snprint(buf, sizeof buf, "%.*s", j, mach[i].name);
813 string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP,
814 mediumfont, buf);
815 }
816 /* draw last vertical line */
817 draw(screen,
818 Rect(screen->r.max.x-hashdx-1, starty-1, screen->r.max.x-hashdx, screen->r.max.y),
819 display->black, nil, ZP);
820
821 /* create graphs */
822 for(i=0; i<nmach; i++){
823 machr = Rect(startx+i*dx, starty, screen->r.max.x, screen->r.max.y);
824 if(i < nmach-1)
825 machr.max.x = startx+(i+1)*dx - 1;
826 else
827 machr.max.x = screen->r.max.x - hashdx - 1;
828 y = starty;
829 for(j=0; j<ngraph; j++, y+=dy){
830 g = &graph[i*ngraph+j];
831 /* allocate data */
832 ondata = g->ndata;
833 g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
834 g->data = erealloc(g->data, g->ndata*sizeof(long));
835 if(g->ndata > ondata)
836 memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(long));
837 /* set geometry */
838 g->r = machr;
839 g->r.min.y = y;
840 g->r.max.y = y+dy - 1;
841 if(j == ngraph-1)
842 g->r.max.y = screen->r.max.y;
843 draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
844 g->overflow = 0;
845 *g->msg = 0;
846 freeimage(g->overtmp);
847 g->overtmp = nil;
848 g->overtmplen = 0;
849 r = g->r;
850 r.max.y = r.min.y+mediumfont->height;
851 n = (g->r.max.x - r.min.x)/stringwidth(mediumfont, "9");
852 if(n > 4){
853 if(n > Gmsglen)
854 n = Gmsglen;
855 r.max.x = r.min.x+stringwidth(mediumfont, "9")*n;
856 g->overtmplen = n;
857 g->overtmp = allocimage(display, r, screen->chan, 0, -1);
858 }
859 g->newvalue(g->mach, &v, &vmax, &mark);
860 redraw(g, vmax);
861 }
862 }
863
864 flushimage(display, 1);
865 }
866
867 void
eresized(int new)868 eresized(int new)
869 {
870 lockdisplay(display);
871 if(new && getwindow(display, Refnone) < 0) {
872 fprint(2, "%s: can't reattach to window\n", argv0);
873 killall("reattach");
874 }
875 resize();
876 unlockdisplay(display);
877 }
878
879 void
dobutton2(Mouse * m)880 dobutton2(Mouse *m)
881 {
882 int i;
883
884 for(i=0; i<Nmenu2; i++)
885 if(present[i])
886 memmove(menu2str[i], "drop ", Opwid);
887 else
888 memmove(menu2str[i], "add ", Opwid);
889 i = emenuhit(3, m, &menu2);
890 if(i >= 0){
891 if(!present[i])
892 addgraph(i);
893 else if(ngraph > 1)
894 dropgraph(i);
895 resize();
896 }
897 }
898
899 void
dobutton1(Mouse * m)900 dobutton1(Mouse *m)
901 {
902 int i, n, dx, dt;
903 Graph *g;
904 char *e;
905 double f;
906
907 for(i = 0; i < ngraph*nmach; i++){
908 if(ptinrect(m->xy, graph[i].r))
909 break;
910 }
911 if(i == ngraph*nmach)
912 return;
913
914 g = &graph[i];
915 if(g->overtmp == nil)
916 return;
917
918 /* clear any previous message and cursor */
919 if(g->overflow || *g->msg){
920 clearmsg(g);
921 *g->msg = 0;
922 clearcursor(g);
923 }
924
925 dx = g->r.max.x - m->xy.x;
926 g->cursor = dx;
927 dt = dx*pinginterval;
928 e = &g->msg[sizeof(g->msg)];
929 seprint(g->msg, e, "%s", ctime(starttime-dt/1000)+11);
930 g->msg[8] = 0;
931 n = 8;
932
933 switch(index2which(i)){
934 case Mrtt:
935 f = rttunscale(g->data[dx]);
936 seprint(g->msg+n, e, " %3.3g", f/1000000);
937 break;
938 case Mlost:
939 seprint(g->msg+n, e, " %ld%%", g->data[dx]);
940 break;
941 }
942
943 drawmsg(g, g->msg);
944 drawcursor(g, m->xy.x);
945 }
946
947 void
mouseproc(void *)948 mouseproc(void*)
949 {
950 Mouse mouse;
951
952 for(;;){
953 mouse = emouse();
954 if(mouse.buttons == 4){
955 lockdisplay(display);
956 dobutton2(&mouse);
957 unlockdisplay(display);
958 } else if(mouse.buttons == 1){
959 lockdisplay(display);
960 dobutton1(&mouse);
961 unlockdisplay(display);
962 }
963 }
964 }
965
966 void
startproc(void (* f)(void *),void * arg)967 startproc(void (*f)(void*), void *arg)
968 {
969 int pid;
970
971 switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
972 case -1:
973 fprint(2, "%s: fork failed: %r\n", argv0);
974 killall("fork failed");
975 case 0:
976 f(arg);
977 killall("process died");
978 exits(nil);
979 }
980 pids[npid++] = pid;
981 }
982
983 void
main(int argc,char * argv[])984 main(int argc, char *argv[])
985 {
986 int i, j;
987 long v, vmax, mark;
988 char flags[10], *f, *p;
989
990 fmtinstall('V', eipfmt);
991
992 f = flags;
993 pinginterval = 5000; /* 5 seconds */
994 ARGBEGIN{
995 case 'i':
996 p = ARGF();
997 if(p == nil)
998 usage();
999 pinginterval = atoi(p);
1000 break;
1001 default:
1002 if(f - flags >= sizeof(flags)-1)
1003 usage();
1004 *f++ = ARGC();
1005 break;
1006 }ARGEND
1007 *f = 0;
1008
1009 for(i=0; i<argc; i++)
1010 addmachine(argv[i]);
1011
1012 for(f = flags; *f; f++)
1013 switch(*f){
1014 case 'l':
1015 addgraph(Mlost);
1016 break;
1017 case 'r':
1018 addgraph(Mrtt);
1019 break;
1020 }
1021
1022 if(nmach == 0)
1023 usage();
1024
1025 if(ngraph == 0)
1026 addgraph(Mrtt);
1027
1028 for(i=0; i<nmach; i++)
1029 for(j=0; j<ngraph; j++)
1030 graph[i*ngraph+j].mach = &mach[i];
1031
1032 if(initdraw(nil, nil, argv0) < 0){
1033 fprint(2, "%s: initdraw failed: %r\n", argv0);
1034 exits("initdraw");
1035 }
1036 colinit();
1037 einit(Emouse);
1038 notify(nil);
1039 startproc(mouseproc, 0);
1040 display->locking = 1; /* tell library we're using the display lock */
1041
1042 resize();
1043
1044 starttime = time(0);
1045
1046 unlockdisplay(display); /* display is still locked from initdraw() */
1047 for(j = 0; ; j++){
1048 lockdisplay(display);
1049 if(j == nmach){
1050 parity = 1-parity;
1051 j = 0;
1052 for(i=0; i<nmach*ngraph; i++){
1053 graph[i].newvalue(graph[i].mach, &v, &vmax, &mark);
1054 graph[i].update(&graph[i], v, vmax, mark);
1055 }
1056 starttime = time(0);
1057 }
1058 flushimage(display, 1);
1059 unlockdisplay(display);
1060 pingsend(&mach[j%nmach]);
1061 sleep(pinginterval/nmach);
1062 }
1063 }
1064