Blob


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 <thread.h>
8 #include <mouse.h>
9 #include <keyboard.h>
11 typedef struct Graph Graph;
12 typedef struct Machine Machine;
14 enum
15 {
16 Ncolor = 6,
17 Ysqueeze = 2, /* vertical squeezing of label text */
18 Labspace = 2, /* room around label */
19 Dot = 2, /* height of dot */
20 Opwid = 5, /* strlen("add ") or strlen("drop ") */
21 Nlab = 3, /* max number of labels on y axis */
22 Lablen = 16, /* max length of label */
23 Lx = 4, /* label tick length */
25 STACK = 8192,
26 XSTACK = 32768
27 };
29 enum
30 {
31 V80211,
32 Vbattery,
33 Vcontext,
34 Vcpu,
35 Vether,
36 Vethererr,
37 Vetherin,
38 Vetherout,
39 Vfault,
40 Vfork,
41 Vidle,
42 Vintr,
43 Vload,
44 Vmem,
45 Vswap,
46 Vsys,
47 Vsyscall,
48 Vuser,
49 Nvalue
50 };
52 char*
53 labels[Nvalue] =
54 {
55 "802.11",
56 "battery",
57 "context",
58 "cpu",
59 "ether",
60 "ethererr",
61 "etherin",
62 "etherout",
63 "fault",
64 "fork",
65 "idle",
66 "intr",
67 "load",
68 "mem",
69 "swap",
70 "sys",
71 "syscall",
72 "user"
73 };
75 struct Graph
76 {
77 int colindex;
78 Rectangle r;
79 int *data;
80 int ndata;
81 char *label;
82 int value;
83 void (*update)(Graph*, long, ulong);
84 Machine *mach;
85 int overflow;
86 Image *overtmp;
87 ulong vmax;
88 };
90 struct Machine
91 {
92 char *name;
93 int fd;
94 int pid;
95 int dead;
96 int absolute[Nvalue];
97 ulong last[Nvalue];
98 ulong val[Nvalue][2];
99 ulong load;
100 ulong nload;
101 };
103 char *menu2str[Nvalue+1];
104 char xmenu2str[Nvalue+1][40];
106 Menu menu2 = {menu2str, 0};
107 int present[Nvalue];
108 Image *cols[Ncolor][3];
109 Graph *graph;
110 Machine *mach;
111 Font *mediumfont;
112 char *fontname;
113 char *mysysname;
114 char argchars[] = "8bcCeEfiIlmnsw";
115 int pids[1024];
116 int parity; /* toggled to avoid patterns in textured background */
117 int nmach;
118 int ngraph; /* totaly number is ngraph*nmach */
119 double scale = 1.0;
120 int logscale = 0;
121 int ylabels = 0;
122 int oldsystem = 0;
123 int sleeptime = 1000;
124 int changedvmax;
126 Mousectl *mc;
127 Keyboardctl *kc;
129 void
130 killall(char *s)
132 int i;
134 for(i=0; i<nmach; i++)
135 if(mach[i].pid)
136 postnote(PNPROC, mach[i].pid, "kill");
137 threadexitsall(s);
140 void*
141 emalloc(ulong sz)
143 void *v;
144 v = malloc(sz);
145 if(v == nil) {
146 fprint(2, "stats: out of memory allocating %ld: %r\n", sz);
147 killall("mem");
149 memset(v, 0, sz);
150 return v;
153 void*
154 erealloc(void *v, ulong sz)
156 v = realloc(v, sz);
157 if(v == nil) {
158 fprint(2, "stats: out of memory reallocating %ld: %r\n", sz);
159 killall("mem");
161 return v;
164 char*
165 estrdup(char *s)
167 char *t;
168 if((t = strdup(s)) == nil) {
169 fprint(2, "stats: out of memory in strdup(%.10s): %r\n", s);
170 killall("mem");
172 return t;
175 void
176 mkcol(int i, int c0, int c1, int c2)
178 cols[i][0] = allocimagemix(display, c0, DWhite);
179 cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
180 cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
183 void
184 colinit(void)
186 if(fontname)
187 mediumfont = openfont(display, fontname);
188 if(mediumfont == nil)
189 mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font");
190 if(mediumfont == nil)
191 mediumfont = font;
193 /* Peach */
194 mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
195 /* Aqua */
196 mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
197 /* Yellow */
198 mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
199 /* Green */
200 mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
201 /* Blue */
202 mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
203 /* Grey */
204 cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
205 cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
206 cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
209 void
210 label(Point p, int dy, char *text)
212 char *s;
213 Rune r[2];
214 int w, maxw, maxy;
216 p.x += Labspace;
217 maxy = p.y+dy;
218 maxw = 0;
219 r[1] = '\0';
220 for(s=text; *s; ){
221 if(p.y+mediumfont->height-Ysqueeze > maxy)
222 break;
223 w = chartorune(r, s);
224 s += w;
225 w = runestringwidth(mediumfont, r);
226 if(w > maxw)
227 maxw = w;
228 runestring(screen, p, display->black, ZP, mediumfont, r);
229 p.y += mediumfont->height-Ysqueeze;
233 Point
234 paritypt(int x)
236 return Pt(x+parity, 0);
239 Point
240 datapoint(Graph *g, int x, ulong v, ulong vmax)
242 Point p;
243 double y;
245 p.x = x;
246 y = ((double)v)/(vmax*scale);
247 if(logscale){
248 /*
249 * Arrange scale to cover a factor of 1000.
250 * vmax corresponds to the 100 mark.
251 * 10*vmax is the top of the scale.
252 */
253 if(y <= 0.)
254 y = 0;
255 else{
256 y = log10(y);
257 /* 1 now corresponds to the top; -2 to the bottom; rescale */
258 y = (y+2.)/3.;
261 p.y = g->r.max.y - Dy(g->r)*y - Dot;
262 if(p.y < g->r.min.y)
263 p.y = g->r.min.y;
264 if(p.y > g->r.max.y-Dot)
265 p.y = g->r.max.y-Dot;
266 return p;
269 void
270 drawdatum(Graph *g, int x, ulong prev, ulong v, ulong vmax)
272 int c;
273 Point p, q;
275 c = g->colindex;
276 p = datapoint(g, x, v, vmax);
277 q = datapoint(g, x, prev, vmax);
278 if(p.y < q.y){
279 draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
280 draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
281 draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
282 }else{
283 draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
284 draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
285 draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
290 void
291 redraw(Graph *g, int vmax)
293 int i, c;
295 if(vmax != g->vmax){
296 g->vmax = vmax;
297 changedvmax = 1;
299 c = g->colindex;
300 draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
301 for(i=1; i<Dx(g->r); i++)
302 drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
303 drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
304 g->overflow = 0;
307 void
308 update1(Graph *g, long v, ulong vmax)
310 char buf[32];
311 int overflow;
313 if(v < 0)
314 v = 0;
315 if(vmax != g->vmax){
316 g->vmax = vmax;
317 changedvmax = 1;
319 if(g->overflow && g->overtmp!=nil)
320 draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
321 draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
322 drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
323 memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
324 g->data[0] = v;
325 g->overflow = 0;
326 if(logscale)
327 overflow = (v>10*vmax*scale);
328 else
329 overflow = (v>vmax*scale);
330 if(overflow && g->overtmp!=nil){
331 g->overflow = 1;
332 draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
333 sprint(buf, "%ld", v);
334 string(screen, g->overtmp->r.min, display->black, ZP, mediumfont, buf);
338 void
339 usage(void)
341 fprint(2, "usage: stats [-LY] [-F font] [-O] [-S scale] [-W winsize] [-%s] [machine...]\n", argchars);
342 threadexitsall("usage");
345 void
346 addgraph(int n)
348 Graph *g, *ograph;
349 int i, j;
350 static int nadd;
352 if(n > Nvalue)
353 abort();
354 /* avoid two adjacent graphs of same color */
355 if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
356 nadd++;
357 ograph = graph;
358 graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
359 for(i=0; i<nmach; i++)
360 for(j=0; j<ngraph; j++)
361 graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
362 free(ograph);
363 ngraph++;
364 for(i=0; i<nmach; i++){
365 g = &graph[i*ngraph+(ngraph-1)];
366 memset(g, 0, sizeof(Graph));
367 g->value = n;
368 g->label = menu2str[n]+Opwid;
369 g->update = update1; /* no other update functions yet */
370 g->mach = &mach[i];
371 g->colindex = nadd%Ncolor;
373 present[n] = 1;
374 nadd++;
377 void
378 dropgraph(int which)
380 Graph *ograph;
381 int i, j, n;
383 if(which > nelem(menu2str))
384 abort();
385 /* convert n to index in graph table */
386 n = -1;
387 for(i=0; i<ngraph; i++)
388 if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
389 n = i;
390 break;
392 if(n < 0){
393 fprint(2, "stats: internal error can't drop graph\n");
394 killall("error");
396 ograph = graph;
397 graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
398 for(i=0; i<nmach; i++){
399 for(j=0; j<n; j++)
400 graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
401 free(ograph[i*ngraph+j].data);
402 freeimage(ograph[i*ngraph+j].overtmp);
403 for(j++; j<ngraph; j++)
404 graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
406 free(ograph);
407 ngraph--;
408 present[which] = 0;
411 int initmach(Machine*, char*);
413 int
414 addmachine(char *name)
416 if(ngraph > 0){
417 fprint(2, "stats: internal error: ngraph>0 in addmachine()\n");
418 usage();
420 if(mach == nil)
421 nmach = 0; /* a little dance to get us started with local machine by default */
422 mach = erealloc(mach, (nmach+1)*sizeof(Machine));
423 memset(mach+nmach, 0, sizeof(Machine));
424 if (initmach(mach+nmach, name)){
425 nmach++;
426 return 1;
427 } else
428 return 0;
431 void
432 newvalue(Machine *m, int i, ulong *v, ulong *vmax)
434 ulong now;
436 if(m->last[i] == 0)
437 m->last[i] = m->val[i][0];
439 if(i == Vload){
440 /*
441 * Invert the ewma to obtain the 5s load statistics.
442 * Ewma is load' = (1884/2048)*load + (164/2048)*last5s, so we do
443 * last5s = (load' - (1884/2048)*load) / (164/2048).
444 */
445 if(++m->nload%5 == 0){
446 now = m->val[i][0];
447 m->load = (now - (((vlong)m->last[i]*1884)/2048)) * 2048 / 164;
448 m->last[i] = now;
450 *v = m->load;
451 *vmax = m->val[i][1];
452 }else if(m->absolute[i]){
453 *v = m->val[i][0];
454 *vmax = m->val[i][1];
455 }else{
456 now = m->val[i][0];
457 *v = (vlong)((now - m->last[i])*sleeptime)/1000;
458 m->last[i] = now;
459 *vmax = m->val[i][1];
461 if(*vmax == 0)
462 *vmax = 1;
465 void
466 labelstrs(Graph *g, char strs[Nlab][Lablen], int *np)
468 int j;
469 ulong vmax;
471 vmax = g->vmax;
472 if(logscale){
473 for(j=1; j<=2; j++)
474 sprint(strs[j-1], "%g", scale*pow(10., j)*(double)vmax/100.);
475 *np = 2;
476 }else{
477 for(j=1; j<=3; j++)
478 sprint(strs[j-1], "%g", scale*(double)j*(double)vmax/4.0);
479 *np = 3;
483 int
484 labelwidth(void)
486 int i, j, n, w, maxw;
487 char strs[Nlab][Lablen];
489 maxw = 0;
490 for(i=0; i<ngraph; i++){
491 /* choose value for rightmost graph */
492 labelstrs(&graph[ngraph*(nmach-1)+i], strs, &n);
493 for(j=0; j<n; j++){
494 w = stringwidth(mediumfont, strs[j]);
495 if(w > maxw)
496 maxw = w;
499 return maxw;
502 void
503 resize(void)
505 int i, j, k, n, startx, starty, x, y, dx, dy, ly, ondata, maxx, wid, nlab;
506 Graph *g;
507 Rectangle machr, r;
508 ulong v, vmax;
509 char buf[128], labs[Nlab][Lablen];
511 draw(screen, screen->r, display->white, nil, ZP);
513 /* label left edge */
514 x = screen->r.min.x;
515 y = screen->r.min.y + Labspace+mediumfont->height+Labspace;
516 dy = (screen->r.max.y - y)/ngraph;
517 dx = Labspace+stringwidth(mediumfont, "0")+Labspace;
518 startx = x+dx+1;
519 starty = y;
520 for(i=0; i<ngraph; i++,y+=dy){
521 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
522 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
523 label(Pt(x, y), dy, graph[i].label);
524 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
527 /* label top edge */
528 dx = (screen->r.max.x - startx)/nmach;
529 for(x=startx, i=0; i<nmach; i++,x+=dx){
530 draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
531 j = dx/stringwidth(mediumfont, "0");
532 /* n = mach[i].nproc; */
533 n = 1;
534 if(n>1 && j>=1+3+(n>10)+(n>100)){ /* first char of name + (n) */
535 j -= 3+(n>10)+(n>100);
536 if(j <= 0)
537 j = 1;
538 snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].name, n);
539 }else
540 snprint(buf, sizeof buf, "%.*s", j, mach[i].name);
541 string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, mediumfont, buf);
544 maxx = screen->r.max.x;
546 /* label right, if requested */
547 if(ylabels && dy>Nlab*(mediumfont->height+1)){
548 wid = labelwidth();
549 if(wid < (maxx-startx)-30){
550 /* else there's not enough room */
551 maxx -= 1+Lx+wid;
552 draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), display->black, nil, ZP);
553 y = starty;
554 for(j=0; j<ngraph; j++, y+=dy){
555 /* choose value for rightmost graph */
556 g = &graph[ngraph*(nmach-1)+j];
557 labelstrs(g, labs, &nlab);
558 r = Rect(maxx+1, y, screen->r.max.x, y+dy-1);
559 if(j == ngraph-1)
560 r.max.y = screen->r.max.y;
561 draw(screen, r, cols[g->colindex][0], nil, paritypt(r.min.x));
562 for(k=0; k<nlab; k++){
563 ly = y + (dy*(nlab-k)/(nlab+1));
564 draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP);
565 ly -= mediumfont->height/2;
566 string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, mediumfont, labs[k]);
572 /* create graphs */
573 for(i=0; i<nmach; i++){
574 machr = Rect(startx+i*dx, starty, maxx, screen->r.max.y);
575 if(i < nmach-1)
576 machr.max.x = startx+(i+1)*dx - 1;
577 y = starty;
578 for(j=0; j<ngraph; j++, y+=dy){
579 g = &graph[i*ngraph+j];
580 /* allocate data */
581 ondata = g->ndata;
582 g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
583 g->data = erealloc(g->data, g->ndata*sizeof(ulong));
584 if(g->ndata > ondata)
585 memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(ulong));
586 /* set geometry */
587 g->r = machr;
588 g->r.min.y = y;
589 g->r.max.y = y+dy - 1;
590 if(j == ngraph-1)
591 g->r.max.y = screen->r.max.y;
592 draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
593 g->overflow = 0;
594 r = g->r;
595 r.max.y = r.min.y+mediumfont->height;
596 r.max.x = r.min.x+stringwidth(mediumfont, "9999999");
597 freeimage(g->overtmp);
598 g->overtmp = nil;
599 if(r.max.x <= g->r.max.x)
600 g->overtmp = allocimage(display, r, screen->chan, 0, -1);
601 newvalue(g->mach, g->value, &v, &vmax);
602 redraw(g, vmax);
606 flushimage(display, 1);
609 void
610 eresized(int new)
612 lockdisplay(display);
613 if(new && getwindow(display, Refnone) < 0) {
614 fprint(2, "stats: can't reattach to window\n");
615 killall("reattach");
617 resize();
618 unlockdisplay(display);
621 void
622 mousethread(void *v)
624 Mouse m;
625 int i;
627 USED(v);
629 while(readmouse(mc) == 0){
630 m = mc->m;
631 if(m.buttons == 4){
632 for(i=0; i<Nvalue; i++)
633 if(present[i])
634 memmove(menu2str[i], "drop ", Opwid);
635 else
636 memmove(menu2str[i], "add ", Opwid);
637 lockdisplay(display);
638 i = menuhit(3, mc, &menu2, nil);
639 if(i >= 0){
640 if(!present[i])
641 addgraph(i);
642 else if(ngraph > 1)
643 dropgraph(i);
644 resize();
646 unlockdisplay(display);
651 void
652 resizethread(void *v)
654 USED(v);
656 while(recv(mc->resizec, 0) == 1){
657 lockdisplay(display);
658 if(getwindow(display, Refnone) < 0)
659 sysfatal("attach to window: %r");
660 resize();
661 unlockdisplay(display);
665 void
666 keyboardthread(void *v)
668 Rune r;
670 while(recv(kc->c, &r) == 1)
671 if(r == 0x7F || r == 'q')
672 killall("quit");
675 void machproc(void*);
676 void updateproc(void*);
678 void
679 threadmain(int argc, char *argv[])
681 int i, j;
682 char *s;
683 ulong nargs;
684 char args[100];
686 nmach = 1;
687 mysysname = sysname();
688 if(mysysname == nil){
689 fprint(2, "stats: can't find sysname: %r\n");
690 threadexitsall("sysname");
693 nargs = 0;
694 ARGBEGIN{
695 case 'T':
696 s = ARGF();
697 if(s == nil)
698 usage();
699 i = atoi(s);
700 if(i > 0)
701 sleeptime = 1000*i;
702 break;
703 case 'S':
704 s = ARGF();
705 if(s == nil)
706 usage();
707 scale = atof(s);
708 if(scale <= 0.)
709 usage();
710 break;
711 case 'L':
712 logscale++;
713 break;
714 case 'F':
715 fontname = EARGF(usage());
716 break;
717 case 'Y':
718 ylabels++;
719 break;
720 case 'O':
721 oldsystem = 1;
722 break;
723 case 'W':
724 winsize = EARGF(usage());
725 break;
726 default:
727 if(nargs>=sizeof args || strchr(argchars, ARGC())==nil)
728 usage();
729 args[nargs++] = ARGC();
730 }ARGEND
732 for(i=0; i<Nvalue; i++){
733 menu2str[i] = xmenu2str[i];
734 snprint(xmenu2str[i], sizeof xmenu2str[i], "add %s", labels[i]);
737 if(argc == 0){
738 mach = emalloc(nmach*sizeof(Machine));
739 initmach(&mach[0], mysysname);
740 }else{
741 for(i=j=0; i<argc; i++)
742 addmachine(argv[i]);
745 for(i=0; i<nmach; i++)
746 proccreate(machproc, &mach[i], STACK);
748 for(i=0; i<nargs; i++)
749 switch(args[i]){
750 default:
751 fprint(2, "stats: internal error: unknown arg %c\n", args[i]);
752 usage();
753 case '8':
754 addgraph(V80211);
755 break;
756 case 'b':
757 addgraph(Vbattery);
758 break;
759 case 'c':
760 addgraph(Vcontext);
761 break;
762 case 'C':
763 addgraph(Vcpu);
764 break;
765 case 'e':
766 addgraph(Vether);
767 break;
768 case 'E':
769 addgraph(Vetherin);
770 addgraph(Vetherout);
771 break;
772 case 'f':
773 addgraph(Vfault);
774 break;
775 case 'i':
776 addgraph(Vintr);
777 break;
778 case 'I':
779 addgraph(Vload);
780 addgraph(Vidle);
781 break;
782 case 'l':
783 addgraph(Vload);
784 break;
785 case 'm':
786 addgraph(Vmem);
787 break;
788 case 'n':
789 addgraph(Vetherin);
790 addgraph(Vetherout);
791 addgraph(Vethererr);
792 break;
793 case 's':
794 addgraph(Vsyscall);
795 break;
796 case 'w':
797 addgraph(Vswap);
798 break;
801 if(ngraph == 0)
802 addgraph(Vload);
804 for(i=0; i<nmach; i++)
805 for(j=0; j<ngraph; j++)
806 graph[i*ngraph+j].mach = &mach[i];
808 if(initdraw(0, nil, "stats") < 0)
809 sysfatal("initdraw: %r");
810 colinit();
811 if((mc = initmouse(nil, screen)) == nil)
812 sysfatal("initmouse: %r");
813 if((kc = initkeyboard(nil)) == nil)
814 sysfatal("initkeyboard: %r");
816 display->locking = 1;
817 threadcreate(keyboardthread, nil, XSTACK);
818 threadcreate(mousethread, nil, XSTACK);
819 threadcreate(resizethread, nil, XSTACK);
820 proccreate(updateproc, nil, XSTACK);
821 resize();
822 unlockdisplay(display);
825 void
826 updateproc(void *z)
828 int i;
829 ulong v, vmax;
831 USED(z);
832 for(;;){
833 parity = 1-parity;
834 lockdisplay(display);
835 for(i=0; i<nmach*ngraph; i++){
836 newvalue(graph[i].mach, graph[i].value, &v, &vmax);
837 graph[i].update(&graph[i], v, vmax);
839 if(changedvmax){
840 changedvmax = 0;
841 resize();
843 flushimage(display, 1);
844 unlockdisplay(display);
845 sleep(sleeptime);
849 void
850 machproc(void *v)
852 char buf[256], *f[4], *p;
853 int i, n, t;
854 Machine *m;
856 m = v;
857 t = 0;
858 for(;;){
859 n = read(m->fd, buf+t, sizeof buf-t);
860 m->dead = 0;
861 if(n <= 0)
862 break;
863 t += n;
864 while((p = memchr(buf, '\n', t)) != nil){
865 *p++ = 0;
866 n = tokenize(buf, f, nelem(f));
867 if(n >= 3){
868 for(i=0; i<Nvalue; i++){
869 if(strcmp(labels[i], f[0]) == 0){
870 if(*f[1] == '='){
871 m->absolute[i] = 1;
872 f[1]++;
874 m->val[i][0] = strtoul(f[1], 0, 0);
875 m->val[i][1] = strtoul(f[2], 0, 0);
879 t -= (p-buf);
880 memmove(buf, p, t);
883 if(m->fd){
884 close(m->fd);
885 m->fd = -1;
887 if(m->pid){
888 postnote(PNPROC, m->pid, "kill");
889 m->pid = 0;
893 int
894 initmach(Machine *m, char *name)
896 char *args[5], *q;
897 int p[2], kfd[3], pid;
899 m->name = name;
900 if(strcmp(name, mysysname) == 0)
901 name = nil;
903 if(pipe(p) < 0)
904 sysfatal("pipe: %r");
906 memset(args, 0, sizeof args);
907 args[0] = "auxstats";
908 if(name){
909 args[1] = name;
910 if((q = strchr(name, ':')) != nil){
911 *q++ = 0;
912 args[2] = q;
915 kfd[0] = open("/dev/null", OREAD);
916 kfd[1] = p[1];
917 kfd[2] = dup(2, -1);
918 if((pid = threadspawn(kfd, "auxstats", args)) < 0){
919 fprint(2, "spawn: %r\n");
920 close(kfd[0]);
921 close(p[0]);
922 close(p[1]);
923 return 0;
925 m->fd = p[0];
926 m->pid = pid;
927 if((q = strchr(m->name, '.')) != nil)
928 *q = 0;
929 return 1;