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