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 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* 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* 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* 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 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 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 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 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 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 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 290 paritypt(int x) 291 { 292 return Pt(x+parity, 0); 293 } 294 295 Point 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 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 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 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 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 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 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 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 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 440 pinglost(Machine *m, Req*) 441 { 442 m->lostmsgs++; 443 } 444 445 void 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 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 void 481 pingsend(Machine *m) 482 { 483 char buf[128]; 484 Icmp *ip; 485 int i; 486 Req *r; 487 char err[ERRMAX]; 488 489 ip = (Icmp*)buf; 490 491 r = malloc(sizeof *r); 492 if(r == nil) 493 return; 494 495 for(i = 32; i < 64; 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, ip, 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 void 521 pingrcv(void *arg) 522 { 523 uchar buf[512]; 524 Icmp *ip; 525 ushort x; 526 int i, n, fd; 527 vlong now; 528 Machine *m = arg; 529 530 ip = (Icmp*)buf; 531 fd = dup(m->pingfd, -1); 532 for(;;){ 533 n = read(fd, buf, sizeof(buf)); 534 now = nsec(); 535 if(n <= 0) 536 continue; 537 if(n < MSGLEN){ 538 print("bad len %d/%d\n", n, MSGLEN); 539 continue; 540 } 541 for(i = 32; i < MSGLEN; i++) 542 if(buf[i] != (i&0xff)) 543 continue; 544 x = (ip->seq[1]<<8)|ip->seq[0]; 545 if(ip->type != EchoReply || ip->code != 0) 546 continue; 547 lock(m); 548 pingclean(m, x, now, ip->ttl); 549 unlock(m); 550 } 551 } 552 553 void 554 initmach(Machine *m, char *name) 555 { 556 char *p; 557 558 srand(time(0)); 559 p = strchr(name, '!'); 560 if(p){ 561 p++; 562 m->name = estrdup(p+1); 563 }else 564 p = name; 565 566 m->name = estrdup(p); 567 m->nproc = 1; 568 m->pingfd = dial(netmkaddr(m->name, "icmp", "1"), 0, 0, 0); 569 if(m->pingfd < 0) 570 sysfatal("dialing %s: %r", m->name); 571 startproc(pingrcv, m); 572 } 573 574 long 575 rttscale(long x) 576 { 577 if(x == 0) 578 return 0; 579 x = 10.0*log10(x) - 20.0; 580 if(x < 0) 581 x = 0; 582 return x; 583 } 584 585 double 586 rttunscale(long x) 587 { 588 double dx; 589 590 x += 20; 591 dx = x; 592 return pow(10.0, dx/10.0); 593 } 594 595 void 596 rttval(Machine *m, long *v, long *vmax, long *mark) 597 { 598 ulong x; 599 600 if(m->rttmsgs == 0){ 601 x = m->lastrtt; 602 } else { 603 x = m->rttsum/m->rttmsgs; 604 m->rttsum = m->rttmsgs = 0; 605 m->lastrtt = x; 606 } 607 608 *v = rttscale(x); 609 *vmax = Rttmax; 610 *mark = 0; 611 } 612 613 void 614 lostval(Machine *m, long *v, long *vmax, long *mark) 615 { 616 ulong x; 617 618 if(m->rcvdmsgs+m->lostmsgs > 0) 619 x = (m->lostavg>>1) + (((m->lostmsgs*100)/(m->lostmsgs + m->rcvdmsgs))>>1); 620 else 621 x = m->lostavg; 622 m->lostavg = x; 623 m->lostmsgs = m->rcvdmsgs = 0; 624 625 if(m->unreachable){ 626 m->unreachable = 0; 627 *mark = 100; 628 } else 629 *mark = 0; 630 631 *v = x; 632 *vmax = 100; 633 } 634 635 jmp_buf catchalarm; 636 637 void 638 alarmed(void *a, char *s) 639 { 640 if(strcmp(s, "alarm") == 0) 641 notejmp(a, catchalarm, 1); 642 noted(NDFLT); 643 } 644 645 void 646 usage(void) 647 { 648 fprint(2, "usage: %s machine [machine...]\n", argv0); 649 exits("usage"); 650 } 651 652 void 653 addgraph(int n) 654 { 655 Graph *g, *ograph; 656 int i, j; 657 static int nadd; 658 659 if(n > nelem(menu2str)) 660 abort(); 661 /* avoid two adjacent graphs of same color */ 662 if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor) 663 nadd++; 664 ograph = graph; 665 graph = emalloc(nmach*(ngraph+1)*sizeof(Graph)); 666 for(i=0; i<nmach; i++) 667 for(j=0; j<ngraph; j++) 668 graph[i*(ngraph+1)+j] = ograph[i*ngraph+j]; 669 free(ograph); 670 ngraph++; 671 for(i=0; i<nmach; i++){ 672 g = &graph[i*ngraph+(ngraph-1)]; 673 memset(g, 0, sizeof(Graph)); 674 g->label = menu2str[n]+Opwid; 675 g->newvalue = newvaluefn[n]; 676 g->update = update1; /* no other update functions yet */ 677 g->mach = &mach[i]; 678 g->colindex = nadd%Ncolor; 679 } 680 present[n] = 1; 681 nadd++; 682 } 683 684 int 685 which2index(int which) 686 { 687 int i, n; 688 689 n = -1; 690 for(i=0; i<ngraph; i++){ 691 if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){ 692 n = i; 693 break; 694 } 695 } 696 if(n < 0){ 697 fprint(2, "%s: internal error can't drop graph\n", argv0); 698 killall("error"); 699 } 700 return n; 701 } 702 703 int 704 index2which(int index) 705 { 706 int i, n; 707 708 n = -1; 709 for(i=0; i<Nmenu2; i++){ 710 if(strcmp(menu2str[i]+Opwid, graph[index].label) == 0){ 711 n = i; 712 break; 713 } 714 } 715 if(n < 0){ 716 fprint(2, "%s: internal error can't identify graph\n", argv0); 717 killall("error"); 718 } 719 return n; 720 } 721 722 void 723 dropgraph(int which) 724 { 725 Graph *ograph; 726 int i, j, n; 727 728 if(which > nelem(menu2str)) 729 abort(); 730 /* convert n to index in graph table */ 731 n = which2index(which); 732 ograph = graph; 733 graph = emalloc(nmach*(ngraph-1)*sizeof(Graph)); 734 for(i=0; i<nmach; i++){ 735 for(j=0; j<n; j++) 736 graph[i*(ngraph-1)+j] = ograph[i*ngraph+j]; 737 free(ograph[i*ngraph+j].data); 738 freeimage(ograph[i*ngraph+j].overtmp); 739 for(j++; j<ngraph; j++) 740 graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j]; 741 } 742 free(ograph); 743 ngraph--; 744 present[which] = 0; 745 } 746 747 void 748 addmachine(char *name) 749 { 750 if(ngraph > 0){ 751 fprint(2, "%s: internal error: ngraph>0 in addmachine()\n", argv0); 752 usage(); 753 } 754 if(nmach == NMACH) 755 sysfatal("too many machines"); 756 initmach(&mach[nmach++], name); 757 } 758 759 760 void 761 resize(void) 762 { 763 int i, j, n, startx, starty, x, y, dx, dy, hashdx, ondata; 764 Graph *g; 765 Rectangle machr, r; 766 long v, vmax, mark; 767 char buf[128]; 768 769 draw(screen, screen->r, display->white, nil, ZP); 770 771 /* label left edge */ 772 x = screen->r.min.x; 773 y = screen->r.min.y + Labspace+mediumfont->height+Labspace; 774 dy = (screen->r.max.y - y)/ngraph; 775 dx = Labspace+stringwidth(mediumfont, "0")+Labspace; 776 startx = x+dx+1; 777 starty = y; 778 for(i=0; i<ngraph; i++,y+=dy){ 779 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP); 780 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x)); 781 label(Pt(x, y), dy, graph[i].label); 782 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP); 783 } 784 785 /* label right edge */ 786 dx = Labspace+stringwidth(mediumfont, "0.001")+Labspace; 787 hashdx = dx; 788 x = screen->r.max.x - dx; 789 y = screen->r.min.y + Labspace+mediumfont->height+Labspace; 790 for(i=0; i<ngraph; i++,y+=dy){ 791 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP); 792 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x)); 793 hashmarks(Pt(x, y), dy, i); 794 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP); 795 } 796 797 /* label top edge */ 798 dx = (screen->r.max.x - dx - startx)/nmach; 799 for(x=startx, i=0; i<nmach; i++,x+=dx){ 800 draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP); 801 j = dx/stringwidth(mediumfont, "0"); 802 n = mach[i].nproc; 803 if(n>1 && j>=1+3+(n>10)+(n>100)){ /* first char of name + (n) */ 804 j -= 3+(n>10)+(n>100); 805 if(j <= 0) 806 j = 1; 807 snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].name, n); 808 }else 809 snprint(buf, sizeof buf, "%.*s", j, mach[i].name); 810 string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, 811 mediumfont, buf); 812 } 813 /* draw last vertical line */ 814 draw(screen, 815 Rect(screen->r.max.x-hashdx-1, starty-1, screen->r.max.x-hashdx, screen->r.max.y), 816 display->black, nil, ZP); 817 818 /* create graphs */ 819 for(i=0; i<nmach; i++){ 820 machr = Rect(startx+i*dx, starty, screen->r.max.x, screen->r.max.y); 821 if(i < nmach-1) 822 machr.max.x = startx+(i+1)*dx - 1; 823 else 824 machr.max.x = screen->r.max.x - hashdx - 1; 825 y = starty; 826 for(j=0; j<ngraph; j++, y+=dy){ 827 g = &graph[i*ngraph+j]; 828 /* allocate data */ 829 ondata = g->ndata; 830 g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */ 831 g->data = erealloc(g->data, g->ndata*sizeof(long)); 832 if(g->ndata > ondata) 833 memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(long)); 834 /* set geometry */ 835 g->r = machr; 836 g->r.min.y = y; 837 g->r.max.y = y+dy - 1; 838 if(j == ngraph-1) 839 g->r.max.y = screen->r.max.y; 840 draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x)); 841 g->overflow = 0; 842 *g->msg = 0; 843 freeimage(g->overtmp); 844 g->overtmp = nil; 845 g->overtmplen = 0; 846 r = g->r; 847 r.max.y = r.min.y+mediumfont->height; 848 n = (g->r.max.x - r.min.x)/stringwidth(mediumfont, "9"); 849 if(n > 4){ 850 if(n > Gmsglen) 851 n = Gmsglen; 852 r.max.x = r.min.x+stringwidth(mediumfont, "9")*n; 853 g->overtmplen = n; 854 g->overtmp = allocimage(display, r, screen->chan, 0, -1); 855 } 856 g->newvalue(g->mach, &v, &vmax, &mark); 857 redraw(g, vmax); 858 } 859 } 860 861 flushimage(display, 1); 862 } 863 864 void 865 eresized(int new) 866 { 867 lockdisplay(display); 868 if(new && getwindow(display, Refnone) < 0) { 869 fprint(2, "%s: can't reattach to window\n", argv0); 870 killall("reattach"); 871 } 872 resize(); 873 unlockdisplay(display); 874 } 875 876 void 877 dobutton2(Mouse *m) 878 { 879 int i; 880 881 for(i=0; i<Nmenu2; i++) 882 if(present[i]) 883 memmove(menu2str[i], "drop ", Opwid); 884 else 885 memmove(menu2str[i], "add ", Opwid); 886 i = emenuhit(3, m, &menu2); 887 if(i >= 0){ 888 if(!present[i]) 889 addgraph(i); 890 else if(ngraph > 1) 891 dropgraph(i); 892 resize(); 893 } 894 } 895 896 void 897 dobutton1(Mouse *m) 898 { 899 int i, n, dx, dt; 900 Graph *g; 901 char *e; 902 double f; 903 904 for(i = 0; i < ngraph*nmach; i++){ 905 if(ptinrect(m->xy, graph[i].r)) 906 break; 907 } 908 if(i == ngraph*nmach) 909 return; 910 911 g = &graph[i]; 912 if(g->overtmp == nil) 913 return; 914 915 /* clear any previous message and cursor */ 916 if(g->overflow || *g->msg){ 917 clearmsg(g); 918 *g->msg = 0; 919 clearcursor(g); 920 } 921 922 dx = g->r.max.x - m->xy.x; 923 g->cursor = dx; 924 dt = dx*pinginterval; 925 e = &g->msg[sizeof(g->msg)]; 926 seprint(g->msg, e, "%s", ctime(starttime-dt/1000)+11); 927 g->msg[8] = 0; 928 n = 8; 929 930 switch(index2which(i)){ 931 case Mrtt: 932 f = rttunscale(g->data[dx]); 933 seprint(g->msg+n, e, " %3.3g", f/1000000); 934 break; 935 case Mlost: 936 seprint(g->msg+n, e, " %ld%%", g->data[dx]); 937 break; 938 } 939 940 drawmsg(g, g->msg); 941 drawcursor(g, m->xy.x); 942 } 943 944 void 945 mouseproc(void*) 946 { 947 Mouse mouse; 948 949 for(;;){ 950 mouse = emouse(); 951 if(mouse.buttons == 4){ 952 lockdisplay(display); 953 dobutton2(&mouse); 954 unlockdisplay(display); 955 } else if(mouse.buttons == 1){ 956 lockdisplay(display); 957 dobutton1(&mouse); 958 unlockdisplay(display); 959 } 960 } 961 } 962 963 void 964 startproc(void (*f)(void*), void *arg) 965 { 966 int pid; 967 968 switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){ 969 case -1: 970 fprint(2, "%s: fork failed: %r\n", argv0); 971 killall("fork failed"); 972 case 0: 973 f(arg); 974 killall("process died"); 975 exits(nil); 976 } 977 pids[npid++] = pid; 978 } 979 980 void 981 main(int argc, char *argv[]) 982 { 983 int i, j; 984 long v, vmax, mark; 985 char flags[10], *f, *p; 986 987 fmtinstall('V', eipfmt); 988 989 f = flags; 990 pinginterval = 5000; /* 5 seconds */ 991 ARGBEGIN{ 992 case 'i': 993 p = ARGF(); 994 if(p == nil) 995 usage(); 996 pinginterval = atoi(p); 997 break; 998 default: 999 if(f - flags >= sizeof(flags)-1) 1000 usage(); 1001 *f++ = ARGC(); 1002 break; 1003 }ARGEND 1004 *f = 0; 1005 1006 for(i=0; i<argc; i++) 1007 addmachine(argv[i]); 1008 1009 for(f = flags; *f; f++) 1010 switch(*f){ 1011 case 'l': 1012 addgraph(Mlost); 1013 break; 1014 case 'r': 1015 addgraph(Mrtt); 1016 break; 1017 } 1018 1019 if(nmach == 0) 1020 usage(); 1021 1022 if(ngraph == 0) 1023 addgraph(Mrtt); 1024 1025 for(i=0; i<nmach; i++) 1026 for(j=0; j<ngraph; j++) 1027 graph[i*ngraph+j].mach = &mach[i]; 1028 1029 if(initdraw(nil, nil, argv0) < 0){ 1030 fprint(2, "%s: initdraw failed: %r\n", argv0); 1031 exits("initdraw"); 1032 } 1033 colinit(); 1034 einit(Emouse); 1035 notify(nil); 1036 startproc(mouseproc, 0); 1037 display->locking = 1; /* tell library we're using the display lock */ 1038 1039 resize(); 1040 1041 starttime = time(0); 1042 1043 unlockdisplay(display); /* display is still locked from initdraw() */ 1044 for(j = 0; ; j++){ 1045 lockdisplay(display); 1046 if(j == nmach){ 1047 parity = 1-parity; 1048 j = 0; 1049 for(i=0; i<nmach*ngraph; i++){ 1050 graph[i].newvalue(graph[i].mach, &v, &vmax, &mark); 1051 graph[i].update(&graph[i], v, vmax, mark); 1052 } 1053 starttime = time(0); 1054 } 1055 flushimage(display, 1); 1056 unlockdisplay(display); 1057 pingsend(&mach[j%nmach]); 1058 sleep(pinginterval/nmach); 1059 } 1060 } 1061