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