wmmon: Update includes based on results of include-what-you-use.
[dockapps.git] / wmmon / wmmon.c
blobd4adff0d8d088d353c438973d71a3a7bceacddad
1 /*
2 Code based on wmppp/wmifs
4 [Orig WMPPP comments]
6 This code was mainly put together by looking at the
7 following programs:
9 asclock
10 A neat piece of equip, used to display the date
11 and time on the screen.
12 Comes with every AfterStep installation.
14 Source used:
15 How do I create a not so solid window?
16 How do I open a window?
17 How do I use pixmaps?
19 ------------------------------------------------------------
21 Authors: Martijn Pieterse (pieterse@xs4all.nl)
22 Antoine Nulle (warp@xs4all.nl)
24 This program is distributed under the GPL license.
25 (as were asclock and pppstats)
27 ----
28 Changes:
29 ----
31 17/06/2012 (Rodolfo García Peñas (kix), <kix@kix.es>)
32 * Code style.
33 13/3/2012 (Barry Kelly (wbk), <coydog@devio.us>)
34 * Fixed get_statistics() I/O features to work with newer
35 /proc/diskstats instead of the old /proc/stat.
36 * Fixes to graph/meter scaling for I/O. Original code let
37 the scaling grow out of control due to inappropriate static
38 data.
39 * Eliminated rounding down relatively low stats in getWidth()
40 and DrawStats_io() by using double and float types instead
41 of ints. We now round up tiny values to prevent the system
42 appearing idle when it's not.
43 * Style/readbility edits.
44 * TODO: Merge in Gentoo and possibly BSD local patches. This
45 should aid in fixing I/O monitoring on non-Linux systems.
46 * TODO: Color swapping. User supplies color values in .rc, and
47 app modifies pixmap in memory on startup. Should be simple.
48 * TODO: address compiler warnings (GCC has gotten pickier over
49 the years).
50 17/10/2009 (Romuald Delavergne, romuald.delavergne@free.fr)
51 * Support SMP processors in realtime CPU stress meter
52 15/05/2004 (Simon Law, sfllaw@debian.org)
53 * Support disabling of mode-cycling
54 23/10/2003 (Simon Law, sfllaw@debian.org)
55 * Eliminated exploitable static buffers
56 * Added -geometry support.
57 * /proc/meminfo support for Linux 2.6
58 18/05/1998 (Antoine Nulle, warp@xs4all.nl)
59 * MEM/SWAP/UPTIME only updated when visible
60 * Using global file descriptors to reduce file
61 system overhead, both updates are based on a diff
62 supplied by Dave Harden (dharden@wisewire.com)
63 15/05/1998 (Antoine Nulle, warp@xs4all.nl)
64 * Fixed memory overflow in the MEM gaugebar
65 * MEM gauge displays now real used mem
66 (buffered + cached mem removed)
67 14/05/1998 (Antoine Nulle, warp@xs4all.nl)
68 * Added -i & -s kludge for selecting startupmode,
69 tijno, don't hate me for this :)
70 12/05/1998 (Antoine Nulle, warp@xs4all.nl)
71 * Finetuned master-xpm, tijno don't worry, no
72 reprogramming needed this time ;-)
73 07/05/1998 (Martijn Pieterse, pieterse@xs4all.nl)
74 * Added disk i/o
75 03/05/1998 (Antoine Nulle, warp@xs4all.nl)
76 * Added new master-xpm which contains the gfx
77 for the upcoming SysInfo part :P
78 02/05/1998 (Martijn Pieterse, pieterse@xs4all.nl)
79 * Removed a lot of code, that was put in wmgeneral
80 23/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
81 * Added zombie destroying code (aka wait :) )
82 18/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
83 * Added CPU-on-screen.
84 * Added -display command line
85 15/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
86 * Fixed a bug in the stats routine
87 (Top 3 bright pixels were not shown when 100% loaded)
88 * Changed xpm to a no-title one.
89 This included the reprogramming of all positions.
90 warpstah, i hate you! ;)
91 05/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
92 * First Working Version
95 #define _GNU_SOURCE
96 #include <stdlib.h>
97 #include <stdio.h>
98 #include <time.h>
99 #include <string.h>
100 #include <fcntl.h>
101 #include <unistd.h>
103 #include <sys/wait.h>
105 #include <X11/X.h>
106 #include <X11/Xlib.h>
108 #include <libdockapp/wmgeneral.h>
109 #include <libdockapp/misc.h>
111 #include "wmmon-master.xpm"
112 #include "wmmon-mask.xbm"
114 /***********/
115 /* Defines */
116 /***********/
117 #define HISTORY_ENTRIES 55
118 #define HISTORY_ENTRIES 55
119 #define MAX_CPU (10) /* depends on graph height */
120 #define MAX_STAT_DEVICES (4)
122 /********************/
123 /* Global Variables */
124 /********************/
125 int stat_current = 0; /* now global */
126 int mode_cycling = 1; /* Allow mode-cycling */
127 int cpu_avg_max = 0; /* CPU stress meter with average and max for SMP */
128 int show_buffers = 0; /* wbk adding per Gentoo -b enhancement. */
130 FILE *fp_meminfo;
131 FILE *fp_stat;
132 FILE *fp_loadavg;
133 FILE *fp_diskstats; /* wbk new io stats API */
135 /* functions */
136 void usage(char*);
137 void printversion(void);
138 void DrawStats(int *, int, int, int, int);
139 void DrawStats_io(int *, int, int, int, int);
140 void wmmon_routine(int, char **);
142 int main(int argc, char *argv[])
144 int i;
145 char *name = argv[0];
147 /* Parse Command Line */
148 for (i = 1; i < argc; i++) {
149 char *arg = argv[i];
151 if (*arg=='-')
152 switch (arg[1]) {
153 case 'd' :
154 if (strcmp(arg+1, "display")) {
155 usage(name);
156 return 1;
158 break;
159 case 'g' :
160 if (strcmp(arg+1, "geometry")) {
161 usage(name);
162 return 1;
164 case 'l' :
165 mode_cycling = 0;
166 break;
167 case 'c' :
168 cpu_avg_max = 1;
169 break;
170 case 'i' :
171 stat_current = 1;
172 break;
173 case 'b' :
174 show_buffers = 1;
175 break;
176 case 's' :
177 stat_current = 2;
178 break;
179 case 'v' :
180 printversion();
181 return 0;
182 default:
183 usage(name);
184 return 1;
188 wmmon_routine(argc, argv);
189 exit(0);
192 /*******************************************************************************\
193 |* wmmon_routine *|
194 \*******************************************************************************/
196 typedef struct {
197 char name[5]; /* "cpu0..cpuz", eventually.. :) */
198 int his[HISTORY_ENTRIES];
199 int hisaddcnt;
200 long rt_stat;
201 long statlast;
202 long rt_idle;
203 long idlelast;
204 /* Processors stats */
205 long *cpu_stat;
206 long *cpu_last;
207 long *idle_stat;
208 long *idle_last;
209 } stat_dev;
211 stat_dev stat_device[MAX_STAT_DEVICES];
213 char *left_action, *right_action, *middle_action;
214 int nb_cpu, cpu_max;
216 int getNbCPU(void);
217 unsigned long getWidth(long, long);
218 int checksysdevs(void);
219 void get_statistics(char *, long *, long *, long *, long *, long *);
220 void DrawActive(char *);
222 void update_stat_cpu(stat_dev *, long *, long *);
223 void update_stat_io(stat_dev *);
224 void update_stat_mem(stat_dev *st, stat_dev *st2);
225 void update_stat_swp(stat_dev *);
227 void wmmon_routine(int argc, char **argv)
229 rckeys wmmon_keys[] = {
230 { "left", &left_action },
231 { "right", &right_action },
232 { "middle", &middle_action },
233 { NULL, NULL }
236 unsigned long i, j;
237 long k;
238 XEvent Event;
239 int but_stat = -1;
241 int stat_online;
243 long starttime, curtime, nexttime;
244 long istat, idle, *istat2, *idle2;
246 FILE *fp;
247 char *conffile = NULL;
249 int xpm_X = 0, xpm_Y = 0;
251 long online_time = 0;
252 long ref_time = 0;
253 long cnt_time;
256 fp = fopen("/proc/uptime", "r");
257 fp_meminfo = fopen("/proc/meminfo", "r");
258 fp_loadavg = fopen("/proc/loadavg", "r");
259 fp_stat = fopen("/proc/stat", "r");
260 fp_diskstats = fopen("/proc/diskstats", "r");
262 if (fp) {
263 if (fscanf(fp, "%ld", &online_time) == EOF)
264 perror("Error! fscanf() of /proc/uptime failed!\n");
265 ref_time = time(0);
266 fclose(fp);
269 for (i = 0; i < MAX_STAT_DEVICES; i++) {
270 for (j = 0; j < HISTORY_ENTRIES; j++)
271 stat_device[i].his[j] = 0;
273 stat_device[i].hisaddcnt = 0;
276 /* wbk - I don't fully understand this. Probably just a means of providing
277 * test cases. ifdef'ing to clear compiler warnings. TODO: remove. */
278 #ifdef LEFT_ACTION
279 if (LEFT_ACTION)
280 left_action = strdup(LEFT_ACTION);
281 #endif
282 #ifdef RIGHT_ACTION
283 if (RIGHT_ACTION)
284 right_action = strdup(RIGHT_ACTION);
285 #endif
286 #ifdef MIDDLE_ACTION
287 if (MIDDLE_ACTION)
288 middle_action = strdup(MIDDLE_ACTION);
289 #endif
291 /* Scan through the .rc files */
292 if (asprintf(&conffile, "/etc/wmmonrc") >= 0) {
293 parse_rcfile(conffile, wmmon_keys);
294 free(conffile);
297 if (asprintf(&conffile, "%s/.wmmonrc", getenv("HOME")) >= 0) {
298 parse_rcfile(conffile, wmmon_keys);
299 free(conffile);
302 if (asprintf(&conffile, "/etc/wmmonrc.fixed") >= 0) {
303 parse_rcfile(conffile, wmmon_keys);
304 free(conffile);
307 stat_online = checksysdevs();
309 nb_cpu = getNbCPU();
310 stat_device[0].cpu_stat = calloc(nb_cpu, sizeof(long));
311 stat_device[0].cpu_last = calloc(nb_cpu, sizeof(long));
312 stat_device[0].idle_stat = calloc(nb_cpu, sizeof(long));
313 stat_device[0].idle_last = calloc(nb_cpu, sizeof(long));
314 if (!stat_device[0].cpu_stat ||
315 !stat_device[0].cpu_last ||
316 !stat_device[0].idle_stat ||
317 !stat_device[0].idle_last) {
318 fprintf(stderr, "%s: Unable to alloc memory !\n", argv[0]);
319 exit(1);
322 istat2 = calloc(nb_cpu, sizeof(long));
323 idle2 = calloc(nb_cpu, sizeof(long));
324 if (!istat2 || !idle2) {
325 fprintf(stderr, "%s: Unable to alloc memory !!\n", argv[0]);
326 exit(1);
329 openXwindow(argc, argv, wmmon_master_xpm, wmmon_mask_bits,
330 wmmon_mask_width, wmmon_mask_height);
332 /* add mouse region */
333 AddMouseRegion(0, 12, 13, 58, 57);
334 AddMouseRegion(1, 5, 5, 24, 14);
336 starttime = time(0);
337 nexttime = starttime + 10;
339 /* Collect information on each panel */
340 for (i = 0; i < stat_online; i++) {
341 get_statistics(stat_device[i].name, &k, &istat, &idle, istat2, idle2);
342 stat_device[i].statlast = istat;
343 stat_device[i].idlelast = idle;
344 if (i == 0 && nb_cpu > 1) {
345 int cpu;
346 for (cpu = 0; cpu < nb_cpu; cpu++) {
347 stat_device[i].cpu_last[cpu] = istat2[cpu];
348 stat_device[i].idle_last[cpu] = idle2[cpu];
353 /* Set the mask for the current window */
354 switch (stat_current) {
355 case 0:
356 case 1:
357 xpm_X = 0;
358 setMaskXY(0, 0);
359 break;
360 case 2:
361 xpm_X = 64;
362 setMaskXY(-64, 0);
363 default:
364 break;
367 /* Draw statistics */
368 if (stat_current == 0) {
369 DrawStats(stat_device[stat_current].his,
370 HISTORY_ENTRIES-1, 40, 5, 58);
371 } else if (stat_current == 1) {
372 DrawStats_io(stat_device[stat_current].his,
373 HISTORY_ENTRIES, 40, 5, 58);
376 DrawActive(stat_device[stat_current].name);
378 while (1) {
379 curtime = time(NULL);
381 waitpid(0, NULL, WNOHANG);
384 update_stat_cpu(&stat_device[0], istat2, idle2);
385 update_stat_io(&stat_device[1]);
387 if(stat_current == 2)
388 update_stat_mem(&stat_device[2], &stat_device[3]);
390 if (stat_current < 2) {
391 i = stat_current;
393 /* Load ding is 45 pixels hoog */
394 copyXPMArea(0, 64, 32, 12, 28, 4);
396 if (i == 0 && nb_cpu > 1) {
397 if (nb_cpu > MAX_CPU || cpu_avg_max) {
398 /* show average CPU */
399 j = getWidth(stat_device[i].rt_stat, stat_device[i].rt_idle);
400 copyXPMArea(32, 64, j, 6, 28, 4);
401 /* Show max CPU */
402 j = getWidth(stat_device[i].cpu_stat[cpu_max],
403 stat_device[i].idle_stat[cpu_max]);
404 copyXPMArea(32, 70, j, 6, 28, 10);
405 } else {
406 int cpu;
407 for (cpu = 0; cpu < nb_cpu; cpu++) {
408 j = getWidth(stat_device[i].cpu_stat[cpu],
409 stat_device[i].idle_stat[cpu]);
410 copyXPMArea(32, 65, j,
411 MAX_CPU / nb_cpu, 28,
412 5 + (MAX_CPU / nb_cpu) * cpu);
415 } else {
416 j = getWidth(stat_device[i].rt_stat, stat_device[i].rt_idle);
417 copyXPMArea(32, 64, j, 12, 28, 4);
419 } else {
420 /* Nu zal ie wel 3 zijn. */
422 copyXPMArea(0, 64, 32, 12, 28+64, 4);
423 copyXPMArea(0, 64, 32, 12, 28+64, 18);
425 j = stat_device[2].rt_idle;
426 if (j != 0) {
427 j = (stat_device[2].rt_stat * 100) / j;
429 j = j * 0.32;
430 if (j > 32) j = 32;
431 copyXPMArea(32, 64, j, 12, 28+64, 4);
433 /*--------------------- swap? ------------------*/
434 j = stat_device[3].rt_idle;
435 if (j != 0)
436 j = (stat_device[3].rt_stat * 100) / j;
438 j = j * 0.32;
439 if (j > 32) j = 32;
440 copyXPMArea(32, 64, j, 12, 28+64, 18);
442 /*----------- online tijd neerzetten! ----------*/
443 cnt_time = time(0) - ref_time + online_time;
445 /* cnt_time = uptime in seconden */
447 secs = 108,47
448 mins = 89,47
449 uren = 70,47
450 digits = 40,78, 6breed, 9hoog
452 i = cnt_time % 60;
453 cnt_time /= 60;
454 copyXPMArea(40 + (i % 10)*7, 78, 6, 9, 115, 47);
455 copyXPMArea(40 + (i / 10)*7, 78, 6, 9, 108, 47);
457 i = cnt_time % 60;
458 cnt_time /= 60;
459 copyXPMArea(40 + (i % 10)*7, 78, 6, 9, 96, 47);
460 copyXPMArea(40 + (i / 10)*7, 78, 6, 9, 89, 47);
462 i = cnt_time % 24;
463 cnt_time /= 24;
464 copyXPMArea(40 + (i % 10)*7, 78, 6, 9, 77, 47);
465 copyXPMArea(40 + (i / 10)*7, 78, 6, 9, 70, 47);
467 /* De rest is dagen! 5x7*/
468 i = cnt_time;
469 copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 88, 35);
470 i /= 10;
471 copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 82, 35);
472 i /= 10;
473 copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 76, 35);
474 i /= 10;
475 copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 70, 35);
478 if (curtime >= nexttime) {
479 nexttime+=10;
481 if (curtime > nexttime) /* dont let APM suspends make this crazy */
482 nexttime = curtime;
484 for (i=0; i<stat_online; i++) {
485 stat_dev *sd = stat_device + i;
487 if (sd->his[HISTORY_ENTRIES-1])
488 sd->his[HISTORY_ENTRIES-1] /= sd->hisaddcnt;
490 for (j = 1; j < HISTORY_ENTRIES; j++)
491 sd->his[j-1] = sd->his[j];
493 if (i == stat_current) {
494 if (i == 0)
495 DrawStats(sd->his, HISTORY_ENTRIES - 1, 40, 5, 58);
496 else if (i == 1)
497 DrawStats_io(sd->his, HISTORY_ENTRIES - 1, 40, 5, 58);
499 sd->his[HISTORY_ENTRIES-1] = 0;
500 sd->hisaddcnt = 0;
504 RedrawWindowXY(xpm_X, xpm_Y);
506 while (XPending(display)) {
507 XNextEvent(display, &Event);
508 switch (Event.type) {
509 case Expose:
510 RedrawWindowXY(xpm_X, xpm_Y);
511 break;
512 case DestroyNotify:
513 XCloseDisplay(display);
514 exit(0);
515 break;
516 case ButtonPress:
517 but_stat = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
518 break;
519 case ButtonRelease:
520 i = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
521 if (but_stat == i && but_stat >= 0) {
522 switch (but_stat) {
523 case 0:
524 switch (Event.xbutton.button) {
525 case 1:
526 if (left_action)
527 execCommand(left_action);
528 break;
529 case 2:
530 if (middle_action)
531 execCommand(middle_action);
532 break;
533 case 3:
534 if (right_action)
535 execCommand(right_action);
536 break;
538 break;
539 case 1:
540 if (mode_cycling) {
541 stat_current++;
542 if (stat_current == stat_online)
543 stat_current = 0;
545 DrawActive(stat_device[stat_current].name);
546 if (stat_current == 0)
547 DrawStats(stat_device[stat_current].his,
548 HISTORY_ENTRIES-1, 40, 5, 58);
550 if (stat_current == 1)
551 DrawStats_io(stat_device[stat_current].his,
552 HISTORY_ENTRIES-1, 40, 5, 58);
554 if (stat_current == 2) {
555 xpm_X = 64;
556 setMaskXY(-64, 0);
557 } else {
558 xpm_X = 0;
559 setMaskXY(0, 0);
561 RedrawWindowXY(xpm_X, xpm_Y);
563 break;
566 break;
569 usleep(250000L);
574 void update_stat_cpu(stat_dev *st, long *istat2, long *idle2)
576 long k, istat, idle;
578 get_statistics(st->name, &k, &istat, &idle, istat2, idle2);
580 st->rt_idle = idle - st->idlelast;
581 st->idlelast = idle;
583 st->rt_stat = istat - st->statlast;
584 st->statlast = istat;
586 if (nb_cpu > 1) {
587 int cpu;
588 unsigned long max, j;
589 cpu_max = 0; max = 0;
590 for (cpu = 0; cpu < nb_cpu; cpu++) {
591 st->idle_stat[cpu] = idle2[cpu] - st->idle_last[cpu];
592 st->idle_last[cpu] = idle2[cpu];
594 st->cpu_stat[cpu] = istat2[cpu] - st->cpu_last[cpu];
595 st->cpu_last[cpu] = istat2[cpu];
597 j = st->cpu_stat[cpu] + st->idle_stat[cpu];
599 if (j != 0)
600 j = (st->cpu_stat[cpu] << 7) / j;
602 if (j > max) {
603 max = j;
604 cpu_max = cpu;
609 st->his[HISTORY_ENTRIES-1] += k;
610 st->hisaddcnt += 1;
614 void update_stat_io(stat_dev *st)
616 long j, k, istat, idle;
618 /* Periodically re-sample. Sometimes we get anomalously high readings;
619 * this discards them. */
620 static int stalemax = 300;
621 static long maxdiskio = 0;
622 if (--stalemax <= 0) {
623 maxdiskio = 0;
624 stalemax = 300;
627 get_statistics(st->name, &k, &istat, &idle, NULL, NULL);
629 st->rt_idle = idle - st->idlelast;
630 st->idlelast = idle;
632 st->rt_stat = istat - st->statlast;
633 st->statlast = istat;
635 /* remember peak for scaling of upper-right meter. */
636 j = st->rt_stat;
637 if (maxdiskio < j)
638 maxdiskio = j;
640 /* Calculate scaling factor for upper-right meter. "/ 5" will clip
641 * the highest peaks, but makes moderate values more visible. We are
642 * compensating for wild fluctuations which are probably caused by
643 * kernel I/O buffering.
645 st->rt_idle = (maxdiskio - j) / 5;
646 if (j > 0 && st->rt_idle < 1)
647 st->rt_idle = 1; /* scale up tiny values so they are visible */
649 st->his[HISTORY_ENTRIES-1] += st->rt_stat;
650 st->hisaddcnt += 1;
654 void update_stat_mem(stat_dev *st, stat_dev *st2)
656 static char *line = NULL;
657 static size_t line_size = 0;
659 unsigned long swapfree;
660 unsigned long free, shared, buffers, cached;
662 if (freopen("/proc/meminfo", "r", fp_meminfo) == NULL)
663 perror("freopen() of /proc/meminfo failed!)\n");
665 while ((getline(&line, &line_size, fp_meminfo)) > 0) {
666 /* The original format for the first two lines of /proc/meminfo was
667 * Mem: total used free shared buffers cached
668 * Swap: total used free
670 * As of at least 2.5.47 these two lines were removed, so that the
671 * required information has to come from the rest of the lines.
672 * On top of that, used is no longer recorded - you have to work
673 * this out yourself, from total - free.
675 * So, these changes below should work. They should also work with
676 * older kernels, too, since the new format has been available for
677 * ages.
679 if (strstr(line, "MemTotal:"))
680 sscanf(line, "MemTotal: %ld", &st->rt_idle);
681 else if (strstr(line, "MemFree:"))
682 sscanf(line, "MemFree: %ld", &free);
683 else if (strstr(line, "MemShared:"))
684 sscanf(line, "MemShared: %ld", &shared);
685 else if (strstr(line, "Buffers:"))
686 sscanf(line, "Buffers: %ld", &buffers);
687 else if (strstr(line, "Cached:"))
688 sscanf(line, "Cached: %ld", &cached);
689 else if (strstr(line, "SwapTotal:"))
690 sscanf(line, "SwapTotal: %ld", &st2->rt_idle);
691 else if (strstr(line, "SwapFree:"))
692 sscanf(line, "SwapFree: %ld", &swapfree);
695 /* memory use - rt_stat is the amount used, it seems, and this isn't
696 * recorded in current version of /proc/meminfo (as of 2.5.47), so we
697 * calculate it from MemTotal - MemFree
699 st->rt_stat = st->rt_idle - free;
701 /* wbk -b flag (from Gentoo patchkit) */
702 if (!show_buffers)
703 st->rt_stat -= buffers+cached;
704 /* As with the amount of memory used, it's not recorded any more, so
705 * we have to calculate it ourselves.
707 st2->rt_stat = st2->rt_idle - swapfree;
710 void update_stat_swp(stat_dev *st)
712 static char *line = NULL;
713 static size_t line_size = 0;
714 unsigned long swapfree;
716 fseek(fp_meminfo, 0, SEEK_SET);
717 while ((getline(&line, &line_size, fp_meminfo)) > 0) {
718 /* As with update_stat_mem(), the format change to /proc/meminfo has
719 * forced some changes here. */
720 if (strstr(line, "SwapTotal:"))
721 sscanf(line, "SwapTotal: %ld", &st->rt_idle);
722 else if (strstr(line, "SwapFree:"))
723 sscanf(line, "SwapFree: %ld", &swapfree);
725 st->rt_stat = st->rt_idle - swapfree;
728 /*******************************************************************************\
729 |* get_statistics *|
730 \*******************************************************************************/
731 void get_statistics(char *devname, long *is, long *ds, long *idle, long *ds2, long *idle2)
733 int i;
734 static char *line = NULL;
735 static size_t line_size = 0;
736 char *p;
737 char *tokens = " \t\n";
738 float f;
740 *is = 0;
741 *ds = 0;
742 *idle = 0;
744 if (!strncmp(devname, "cpu", 3)) {
745 fseek(fp_stat, 0, SEEK_SET);
746 while ((getline(&line, &line_size, fp_stat)) > 0) {
747 if (strstr(line, "cpu")) {
748 int cpu = -1; /* by default, cumul stats => average */
749 if (!strstr(line, "cpu ")) {
750 sscanf(line, "cpu%d", &cpu);
751 ds2[cpu] = 0;
752 idle2[cpu] = 0;
754 p = strtok(line, tokens);
755 /* 1..3, 4 == idle, we don't want idle! */
756 for (i=0; i<3; i++) {
757 p = strtok(NULL, tokens);
758 if (cpu == -1)
759 *ds += atol(p);
760 else
761 ds2[cpu] += atol(p);
763 p = strtok(NULL, tokens);
764 if (cpu == -1)
765 *idle = atol(p);
766 else
767 idle2[cpu] = atol(p);
770 if ((fp_loadavg = freopen("/proc/loadavg", "r", fp_loadavg)) == NULL)
771 perror("ger_statistics(): freopen(proc/loadavg) failed!\n");
773 if (fscanf(fp_loadavg, "%f", &f) == EOF)
774 perror("fscanf() failed to read f\n");
775 *is = (long) (100 * f);
778 if (!strncmp(devname, "i/o", 3)) {
779 if (fseek(fp_diskstats, 0, SEEK_SET) == -1)
780 perror("get_statistics() seek failed\n");
782 /* wbk 20120308 These are no longer in /proc/stat. /proc/diskstats
783 * seems to be the closest replacement. Under modern BSD's, /proc is
784 * now deprecated, so iostat() might be the answer.
785 * http://www.gossamer-threads.com/lists/linux/kernel/314618
786 * has good info on this being removed from kernel. Also see
787 * kernel sources Documentation/iostats.txt
789 * TODO: We will end up with doubled values. We are adding the
790 * aggregate to the individual partition, due to device selection
791 * logic. Either grab devices' stats with numbers, or without (sda
792 * OR sda[1..10]. Could use strstr() return plus offset, but would
793 * have to be careful with bounds checking since we're in a
794 * limited buffer. Or just divide by 2 (inefficient). Shouldn't
795 * matter for graphing (we care about proportions, not numbers). */
796 while ((getline(&line, &line_size, fp_diskstats)) > 0) {
797 if (strstr(line, "sd") || strstr(line, "sr")) {
798 p = strtok(line, tokens);
799 /* skip 3 tokens, then use fields from
800 `* linux/Documentation/iostats.txt */
801 for (i = 1; i <= 6; i++)
802 p = strtok(NULL, tokens);
804 *ds += atol(p);
805 for (i = 7; i <= 10; i++)
806 p = strtok(NULL, tokens);
808 *ds += atol(p);
815 /*******************************************************************************\
816 |* getWidth *|
817 \*******************************************************************************/
818 unsigned long getWidth(long actif, long idle)
820 /* wbk - work with a decimal value so we don't round < 1 down to zero. */
821 double j = 0;
822 unsigned long r = 0;
824 j = (actif + idle);
825 if (j != 0)
826 j = (actif * 100) / j;
828 j = j * 0.32;
830 /* round up very low positive values so they are visible. */
831 if (actif > 0 && j < 2)
832 j = 2;
834 if (j > 32)
835 j = 32;
837 r = (unsigned long) j;
838 return r;
842 /*******************************************************************************\
843 |* getNbCPU *|
844 \*******************************************************************************/
845 int getNbCPU(void)
847 static char *line = NULL;
848 static size_t line_size = 0;
849 int cpu = 0;
851 fseek(fp_stat, 0, SEEK_SET);
852 while ((getline(&line, &line_size, fp_stat)) > 0) {
853 if (strstr(line, "cpu") && !strstr(line, "cpu "))
854 sscanf(line, "cpu%d", &cpu);
857 return cpu+1;
861 /*******************************************************************************\
862 |* checksysdevs *|
863 \*******************************************************************************/
864 int checksysdevs(void) {
865 strcpy(stat_device[0].name, "cpu0");
866 strcpy(stat_device[1].name, "i/o");
867 strcpy(stat_device[2].name, "sys");
869 return 3;
873 /*******************************************************************************\
874 |* void DrawActive(char *) *|
875 \*******************************************************************************/
876 void DrawActive(char *name)
879 /* Alles op X,77
880 CPU: 0
881 I/O: 21
883 20 Breed, 10 hoog
884 Destinatie: 5,5
887 if (name[0] == 'c')
888 copyXPMArea(0, 77, 19, 10, 5, 5);
889 else if (name[0] == 'i')
890 copyXPMArea(19, 77, 19, 10, 5, 5);
894 /*******************************************************************************\
895 |* DrawStats *|
896 \*******************************************************************************/
897 void DrawStats(int *his, int num, int size, int x_left, int y_bottom)
899 int pixels_per_byte, j, k, *p, d;
901 pixels_per_byte = 100;
902 p = his;
904 for (j=0; j<num; j++) {
905 if (p[0] > pixels_per_byte)
906 pixels_per_byte += 100;
907 p += 1;
910 p = his;
912 for (k=0; k<num; k++) {
913 d = (1.0 * p[0] / pixels_per_byte) * size;
915 for (j=0; j<size; j++) {
916 if (j < d - 3)
917 copyXPMArea(2, 88, 1, 1, k+x_left, y_bottom-j);
918 else if (j < d)
919 copyXPMArea(2, 89, 1, 1, k+x_left, y_bottom-j);
920 else
921 copyXPMArea(2, 90, 1, 1, k+x_left, y_bottom-j);
923 p += 1;
926 /* Nu horizontaal op 100/200/300 etc lijntje trekken! */
927 for (j = pixels_per_byte-100; j > 0; j-=100) {
928 for (k=0; k<num; k++) {
929 d = (40.0 / pixels_per_byte) * j;
931 copyXPMArea(2, 91, 1, 1, k+x_left, y_bottom-d);
937 /*******************************************************************************\
938 |* DrawStats_io *|
939 \*******************************************************************************/
940 void DrawStats_io(int *his, int num, int size, int x_left, int y_bottom)
942 float pixels_per_byte;
943 int j, k, *p;
944 /* wbk - Use a double to avoid rounding values of d < 1 to zero. */
945 double d = 0;
946 int border = 3;
948 /* wbk - this should not be static. No need to track the scale, since
949 * we always calculate it on the fly anyway. This static variable did
950 * not get re-initialized when we entered this function, so the scale
951 * would always grow and never shrink.
953 /*static int global_io_scale = 1;*/
954 int io_scale = 1;
956 p = his;
957 for (j=0; j<num; j++)
958 if (p[j] > io_scale) io_scale = p[j];
960 pixels_per_byte = 1.0 * io_scale / size;
961 if (pixels_per_byte == 0)
962 pixels_per_byte = 1;
964 for (k=0; k<num; k++) {
965 d = (1.0 * p[0] / pixels_per_byte);
967 /* graph values too low for graph resolution */
968 if (d > 0 && d < 1) {
969 d = 3;
970 border = 2;
971 } else {
972 border = 3;
975 for (j=0; j<size; j++) {
976 if (j < d - border)
977 copyXPMArea(2, 88, 1, 1, k+x_left, y_bottom-j);
978 else if (j < d )
979 copyXPMArea(2, 89, 1, 1, k+x_left, y_bottom-j);
980 else
981 copyXPMArea(2, 90, 1, 1, k+x_left, y_bottom-j);
983 p += 1; /* beware... */
988 /*******************************************************************************\
989 |* usage *|
990 \*******************************************************************************/
991 void usage(char *name)
993 printf("Usage: %s [OPTION]...\n", name);
994 printf("WindowMaker dockapp that displays system information.\n");
995 printf("\n");
996 printf(" -display DISPLAY contact the DISPLAY X server\n");
997 printf(" -geometry GEOMETRY position the clock at GEOMETRY\n");
998 printf(" -l locked view - cannot cycle modes\n");
999 printf(" -c show average and max CPU for SMP machine.\n");
1000 printf(" default if there is more than %d processors\n", MAX_CPU);
1001 printf(" -i start in Disk I/O mode\n");
1002 printf(" -s start in System Info mode\n");
1003 printf(" -b include buffers and cache in memory usage\n");
1004 printf(" -h display this help and exit\n");
1005 printf(" -v output version information and exit\n");
1009 /*******************************************************************************\
1010 |* printversion *|
1011 \*******************************************************************************/
1012 void printversion(void)
1014 printf("WMMon version %s\n", PACKAGE_VERSION);
1016 /* vim: sw=4 ts=4 columns=82