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 /* IPv4 only */ 481 void 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 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 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 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 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 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 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 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 649 usage(void) 650 { 651 fprint(2, "usage: %s machine [machine...]\n", argv0); 652 exits("usage"); 653 } 654 655 void 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 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 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 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 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 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 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 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 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 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 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 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