10750d6d69be208ff1b994381bfe3d876bb693d5
[dockapps.git] / wmmon / wmmon / wmmon.c
blob10750d6d69be208ff1b994381bfe3d876bb693d5
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>
104 #include <sys/param.h>
105 #include <sys/types.h>
107 #include <X11/Xlib.h>
108 #include <X11/xpm.h>
109 #include <X11/extensions/shape.h>
111 #include "../wmgeneral/wmgeneral.h"
112 #include "../wmgeneral/misc.h"
114 #include "wmmon-master.xpm"
115 #include "wmmon-mask.xbm"
117 /***********/
118 /* Defines */
119 /***********/
120 #define WMMON_VERSION "1.2.b2"
121 #define HISTORY_ENTRIES 55
122 #define HISTORY_ENTRIES 55
123 #define MAX_CPU (10) /* depends on graph height */
124 #define MAX_STAT_DEVICES (4)
126 /********************/
127 /* Global Variables */
128 /********************/
129 int stat_current = 0; /* now global */
130 int mode_cycling = 1; /* Allow mode-cycling */
131 int cpu_avg_max = 0; /* CPU stress meter with average and max for SMP */
132 int show_buffers = 0; /* wbk adding per Gentoo -b enhancement. */
134 FILE *fp_meminfo;
135 FILE *fp_stat;
136 FILE *fp_loadavg;
137 FILE *fp_diskstats; /* wbk new io stats API */
139 /* functions */
140 void usage(char*);
141 void printversion(void);
142 void DrawStats(int *, int, int, int, int);
143 void DrawStats_io(int *, int, int, int, int);
144 void wmmon_routine(int, char **);
146 int main(int argc, char *argv[])
148 int i;
149 char *name = argv[0];
151 /* Parse Command Line */
152 for (i = 1; i < argc; i++) {
153 char *arg = argv[i];
155 if (*arg=='-')
156 switch (arg[1]) {
157 case 'd' :
158 if (strcmp(arg+1, "display")) {
159 usage(name);
160 return 1;
162 break;
163 case 'g' :
164 if (strcmp(arg+1, "geometry")) {
165 usage(name);
166 return 1;
168 case 'l' :
169 mode_cycling = 0;
170 break;
171 case 'c' :
172 cpu_avg_max = 1;
173 break;
174 case 'i' :
175 stat_current = 1;
176 break;
177 case 'b' :
178 show_buffers = 1;
179 break;
180 case 's' :
181 stat_current = 2;
182 break;
183 case 'v' :
184 printversion();
185 return 0;
186 default:
187 usage(name);
188 return 1;
192 wmmon_routine(argc, argv);
193 exit(0);
196 /*******************************************************************************\
197 |* wmmon_routine *|
198 \*******************************************************************************/
200 typedef struct {
201 char name[5]; /* "cpu0..cpuz", eventually.. :) */
202 int his[HISTORY_ENTRIES];
203 int hisaddcnt;
204 long rt_stat;
205 long statlast;
206 long rt_idle;
207 long idlelast;
208 /* Processors stats */
209 long *cpu_stat;
210 long *cpu_last;
211 long *idle_stat;
212 long *idle_last;
213 } stat_dev;
215 stat_dev stat_device[MAX_STAT_DEVICES];
217 char *left_action, *right_action, *middle_action;
218 int nb_cpu, cpu_max;
220 int getNbCPU(void);
221 unsigned long getWidth(long, long);
222 int checksysdevs(void);
223 void get_statistics(char *, long *, long *, long *, long *, long *);
224 void DrawActive(char *);
226 void update_stat_cpu(stat_dev *, long *, long *);
227 void update_stat_io(stat_dev *);
228 void update_stat_mem(stat_dev *st, stat_dev *st2);
229 void update_stat_swp(stat_dev *);
231 void wmmon_routine(int argc, char **argv)
233 rckeys wmmon_keys[] = {
234 { "left", &left_action },
235 { "right", &right_action },
236 { "middle", &middle_action },
237 { NULL, NULL }
240 unsigned long i, j;
241 long k;
242 XEvent Event;
243 int but_stat = -1;
245 int stat_online;
247 long starttime, curtime, nexttime;
248 long istat, idle, *istat2, *idle2;
250 FILE *fp;
251 char *conffile = NULL;
253 int xpm_X = 0, xpm_Y = 0;
255 long online_time = 0;
256 long ref_time = 0;
257 long cnt_time;
260 fp = fopen("/proc/uptime", "r");
261 fp_meminfo = fopen("/proc/meminfo", "r");
262 fp_loadavg = fopen("/proc/loadavg", "r");
263 fp_stat = fopen("/proc/stat", "r");
264 fp_diskstats = fopen("/proc/diskstats", "r");
266 if (fp) {
267 if (fscanf(fp, "%ld", &online_time) == EOF)
268 perror("Error! fscanf() of /proc/uptime failed!\n");
269 ref_time = time(0);
270 fclose(fp);
273 for (i = 0; i < MAX_STAT_DEVICES; i++) {
274 for (j = 0; j < HISTORY_ENTRIES; j++)
275 stat_device[i].his[j] = 0;
277 stat_device[i].hisaddcnt = 0;
280 /* wbk - I don't fully understand this. Probably just a means of providing
281 * test cases. ifdef'ing to clear compiler warnings. TODO: remove. */
282 #ifdef LEFT_ACTION
283 if (LEFT_ACTION)
284 left_action = strdup(LEFT_ACTION);
285 #endif
286 #ifdef RIGHT_ACTION
287 if (RIGHT_ACTION)
288 right_action = strdup(RIGHT_ACTION);
289 #endif
290 #ifdef MIDDLE_ACTION
291 if (MIDDLE_ACTION)
292 middle_action = strdup(MIDDLE_ACTION);
293 #endif
295 /* Scan through the .rc files */
296 if (asprintf(&conffile, "/etc/wmmonrc") >= 0) {
297 parse_rcfile(conffile, wmmon_keys);
298 free(conffile);
301 if (asprintf(&conffile, "%s/.wmmonrc", getenv("HOME")) >= 0) {
302 parse_rcfile(conffile, wmmon_keys);
303 free(conffile);
306 if (asprintf(&conffile, "/etc/wmmonrc.fixed") >= 0) {
307 parse_rcfile(conffile, wmmon_keys);
308 free(conffile);
311 stat_online = checksysdevs();
313 nb_cpu = getNbCPU();
314 stat_device[0].cpu_stat = calloc(nb_cpu, sizeof(long));
315 stat_device[0].cpu_last = calloc(nb_cpu, sizeof(long));
316 stat_device[0].idle_stat = calloc(nb_cpu, sizeof(long));
317 stat_device[0].idle_last = calloc(nb_cpu, sizeof(long));
318 if (!stat_device[0].cpu_stat ||
319 !stat_device[0].cpu_last ||
320 !stat_device[0].idle_stat ||
321 !stat_device[0].idle_last) {
322 fprintf(stderr, "%s: Unable to alloc memory !\n", argv[0]);
323 exit(1);
326 istat2 = calloc(nb_cpu, sizeof(long));
327 idle2 = calloc(nb_cpu, sizeof(long));
328 if (!istat2 || !idle2) {
329 fprintf(stderr, "%s: Unable to alloc memory !!\n", argv[0]);
330 exit(1);
333 openXwindow(argc, argv, wmmon_master_xpm, wmmon_mask_bits,
334 wmmon_mask_width, wmmon_mask_height);
336 /* add mouse region */
337 AddMouseRegion(0, 12, 13, 58, 57);
338 AddMouseRegion(1, 5, 5, 24, 14);
340 starttime = time(0);
341 nexttime = starttime + 10;
343 /* Collect information on each panel */
344 for (i = 0; i < stat_online; i++) {
345 get_statistics(stat_device[i].name, &k, &istat, &idle, istat2, idle2);
346 stat_device[i].statlast = istat;
347 stat_device[i].idlelast = idle;
348 if (i == 0 && nb_cpu > 1) {
349 int cpu;
350 for (cpu = 0; cpu < nb_cpu; cpu++) {
351 stat_device[i].cpu_last[cpu] = istat2[cpu];
352 stat_device[i].idle_last[cpu] = idle2[cpu];
357 /* Set the mask for the current window */
358 switch (stat_current) {
359 case 0:
360 case 1:
361 xpm_X = 0;
362 setMaskXY(0, 0);
363 break;
364 case 2:
365 xpm_X = 64;
366 setMaskXY(-64, 0);
367 default:
368 break;
371 /* Draw statistics */
372 if (stat_current == 0) {
373 DrawStats(stat_device[stat_current].his,
374 HISTORY_ENTRIES-1, 40, 5, 58);
375 } else if (stat_current == 1) {
376 DrawStats_io(stat_device[stat_current].his,
377 HISTORY_ENTRIES, 40, 5, 58);
380 DrawActive(stat_device[stat_current].name);
382 while (1) {
383 curtime = time(NULL);
385 waitpid(0, NULL, WNOHANG);
388 update_stat_cpu(&stat_device[0], istat2, idle2);
389 update_stat_io(&stat_device[1]);
391 if(stat_current == 2)
392 update_stat_mem(&stat_device[2], &stat_device[3]);
394 if (stat_current < 2) {
395 i = stat_current;
397 /* Load ding is 45 pixels hoog */
398 copyXPMArea(0, 64, 32, 12, 28, 4);
400 if (i == 0 && nb_cpu > 1) {
401 if (nb_cpu > MAX_CPU || cpu_avg_max) {
402 /* show average CPU */
403 j = getWidth(stat_device[i].rt_stat, stat_device[i].rt_idle);
404 copyXPMArea(32, 64, j, 6, 28, 4);
405 /* Show max CPU */
406 j = getWidth(stat_device[i].cpu_stat[cpu_max],
407 stat_device[i].idle_stat[cpu_max]);
408 copyXPMArea(32, 70, j, 6, 28, 10);
409 } else {
410 int cpu;
411 for (cpu = 0; cpu < nb_cpu; cpu++) {
412 j = getWidth(stat_device[i].cpu_stat[cpu],
413 stat_device[i].idle_stat[cpu]);
414 copyXPMArea(32, 65, j,
415 MAX_CPU / nb_cpu, 28,
416 5 + (MAX_CPU / nb_cpu) * cpu);
419 } else {
420 j = getWidth(stat_device[i].rt_stat, stat_device[i].rt_idle);
421 copyXPMArea(32, 64, j, 12, 28, 4);
423 } else {
424 /* Nu zal ie wel 3 zijn. */
426 copyXPMArea(0, 64, 32, 12, 28+64, 4);
427 copyXPMArea(0, 64, 32, 12, 28+64, 18);
429 j = stat_device[2].rt_idle;
430 if (j != 0) {
431 j = (stat_device[2].rt_stat * 100) / j;
433 j = j * 0.32;
434 if (j > 32) j = 32;
435 copyXPMArea(32, 64, j, 12, 28+64, 4);
437 /*--------------------- swap? ------------------*/
438 j = stat_device[3].rt_idle;
439 if (j != 0)
440 j = (stat_device[3].rt_stat * 100) / j;
442 j = j * 0.32;
443 if (j > 32) j = 32;
444 copyXPMArea(32, 64, j, 12, 28+64, 18);
446 /*----------- online tijd neerzetten! ----------*/
447 cnt_time = time(0) - ref_time + online_time;
449 /* cnt_time = uptime in seconden */
451 secs = 108,47
452 mins = 89,47
453 uren = 70,47
454 digits = 40,78, 6breed, 9hoog
456 i = cnt_time % 60;
457 cnt_time /= 60;
458 copyXPMArea(40 + (i % 10)*7, 78, 6, 9, 115, 47);
459 copyXPMArea(40 + (i / 10)*7, 78, 6, 9, 108, 47);
461 i = cnt_time % 60;
462 cnt_time /= 60;
463 copyXPMArea(40 + (i % 10)*7, 78, 6, 9, 96, 47);
464 copyXPMArea(40 + (i / 10)*7, 78, 6, 9, 89, 47);
466 i = cnt_time % 24;
467 cnt_time /= 24;
468 copyXPMArea(40 + (i % 10)*7, 78, 6, 9, 77, 47);
469 copyXPMArea(40 + (i / 10)*7, 78, 6, 9, 70, 47);
471 /* De rest is dagen! 5x7*/
472 i = cnt_time;
473 copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 88, 35);
474 i /= 10;
475 copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 82, 35);
476 i /= 10;
477 copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 76, 35);
478 i /= 10;
479 copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 70, 35);
482 if (curtime >= nexttime) {
483 nexttime+=10;
485 if (curtime > nexttime) /* dont let APM suspends make this crazy */
486 nexttime = curtime;
488 for (i=0; i<stat_online; i++) {
489 stat_dev *sd = stat_device + i;
491 if (sd->his[HISTORY_ENTRIES-1])
492 sd->his[HISTORY_ENTRIES-1] /= sd->hisaddcnt;
494 for (j = 1; j < HISTORY_ENTRIES; j++)
495 sd->his[j-1] = sd->his[j];
497 if (i == stat_current) {
498 if (i == 0)
499 DrawStats(sd->his, HISTORY_ENTRIES - 1, 40, 5, 58);
500 else if (i == 1)
501 DrawStats_io(sd->his, HISTORY_ENTRIES - 1, 40, 5, 58);
503 sd->his[HISTORY_ENTRIES-1] = 0;
504 sd->hisaddcnt = 0;
508 RedrawWindowXY(xpm_X, xpm_Y);
510 while (XPending(display)) {
511 XNextEvent(display, &Event);
512 switch (Event.type) {
513 case Expose:
514 RedrawWindowXY(xpm_X, xpm_Y);
515 break;
516 case DestroyNotify:
517 XCloseDisplay(display);
518 exit(0);
519 break;
520 case ButtonPress:
521 but_stat = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
522 break;
523 case ButtonRelease:
524 i = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
525 if (but_stat == i && but_stat >= 0 && mode_cycling) {
526 switch (but_stat) {
527 case 0:
528 switch (Event.xbutton.button) {
529 case 1:
530 if (left_action)
531 execCommand(left_action);
532 break;
533 case 2:
534 if (middle_action)
535 execCommand(middle_action);
536 break;
537 case 3:
538 if (right_action)
539 execCommand(right_action);
540 break;
542 case 1:
543 stat_current++;
544 if (stat_current == stat_online)
545 stat_current = 0;
547 DrawActive(stat_device[stat_current].name);
548 if (stat_current == 0)
549 DrawStats(stat_device[stat_current].his,
550 HISTORY_ENTRIES-1, 40, 5, 58);
552 if (stat_current == 1)
553 DrawStats_io(stat_device[stat_current].his,
554 HISTORY_ENTRIES-1, 40, 5, 58);
556 if (stat_current == 2) {
557 xpm_X = 64;
558 setMaskXY(-64, 0);
559 } else {
560 xpm_X = 0;
561 setMaskXY(0, 0);
563 RedrawWindowXY(xpm_X, xpm_Y);
564 break;
567 break;
570 usleep(250000L);
575 void update_stat_cpu(stat_dev *st, long *istat2, long *idle2)
577 long k, istat, idle;
579 get_statistics(st->name, &k, &istat, &idle, istat2, idle2);
581 st->rt_idle = idle - st->idlelast;
582 st->idlelast = idle;
584 st->rt_stat = istat - st->statlast;
585 st->statlast = istat;
587 if (nb_cpu > 1) {
588 int cpu;
589 unsigned long max, j;
590 cpu_max = 0; max = 0;
591 for (cpu = 0; cpu < nb_cpu; cpu++) {
592 st->idle_stat[cpu] = idle2[cpu] - st->idle_last[cpu];
593 st->idle_last[cpu] = idle2[cpu];
595 st->cpu_stat[cpu] = istat2[cpu] - st->cpu_last[cpu];
596 st->cpu_last[cpu] = istat2[cpu];
598 j = st->cpu_stat[cpu] + st->idle_stat[cpu];
600 if (j != 0)
601 j = (st->cpu_stat[cpu] << 7) / j;
603 if (j > max) {
604 max = j;
605 cpu_max = cpu;
610 st->his[HISTORY_ENTRIES-1] += k;
611 st->hisaddcnt += 1;
615 void update_stat_io(stat_dev *st)
617 long j, k, istat, idle;
619 /* Periodically re-sample. Sometimes we get anomalously high readings;
620 * this discards them. */
621 static int stalemax = 300;
622 static long maxdiskio = 0;
623 if (--stalemax <= 0) {
624 maxdiskio = 0;
625 stalemax = 300;
628 get_statistics(st->name, &k, &istat, &idle, NULL, NULL);
630 st->rt_idle = idle - st->idlelast;
631 st->idlelast = idle;
633 st->rt_stat = istat - st->statlast;
634 st->statlast = istat;
636 /* remember peak for scaling of upper-right meter. */
637 j = st->rt_stat;
638 if (maxdiskio < j)
639 maxdiskio = j;
641 /* Calculate scaling factor for upper-right meter. "/ 5" will clip
642 * the highest peaks, but makes moderate values more visible. We are
643 * compensating for wild fluctuations which are probably caused by
644 * kernel I/O buffering.
646 st->rt_idle = (maxdiskio - j) / 5;
647 if (j > 0 && st->rt_idle < 1)
648 st->rt_idle = 1; /* scale up tiny values so they are visible */
650 st->his[HISTORY_ENTRIES-1] += st->rt_stat;
651 st->hisaddcnt += 1;
655 void update_stat_mem(stat_dev *st, stat_dev *st2)
657 static char *line = NULL;
658 static size_t line_size = 0;
660 unsigned long swapfree;
661 unsigned long free, shared, buffers, cached;
663 if (freopen("/proc/meminfo", "r", fp_meminfo) == NULL)
664 perror("freopen() of /proc/meminfo failed!)\n");
666 while ((getline(&line, &line_size, fp_meminfo)) > 0) {
667 /* The original format for the first two lines of /proc/meminfo was
668 * Mem: total used free shared buffers cached
669 * Swap: total used free
671 * As of at least 2.5.47 these two lines were removed, so that the
672 * required information has to come from the rest of the lines.
673 * On top of that, used is no longer recorded - you have to work
674 * this out yourself, from total - free.
676 * So, these changes below should work. They should also work with
677 * older kernels, too, since the new format has been available for
678 * ages.
680 if (strstr(line, "MemTotal:"))
681 sscanf(line, "MemTotal: %ld", &st->rt_idle);
682 else if (strstr(line, "MemFree:"))
683 sscanf(line, "MemFree: %ld", &free);
684 else if (strstr(line, "MemShared:"))
685 sscanf(line, "MemShared: %ld", &shared);
686 else if (strstr(line, "Buffers:"))
687 sscanf(line, "Buffers: %ld", &buffers);
688 else if (strstr(line, "Cached:"))
689 sscanf(line, "Cached: %ld", &cached);
690 else if (strstr(line, "SwapTotal:"))
691 sscanf(line, "SwapTotal: %ld", &st2->rt_idle);
692 else if (strstr(line, "SwapFree:"))
693 sscanf(line, "SwapFree: %ld", &swapfree);
696 /* memory use - rt_stat is the amount used, it seems, and this isn't
697 * recorded in current version of /proc/meminfo (as of 2.5.47), so we
698 * calculate it from MemTotal - MemFree
700 st->rt_stat = st->rt_idle - free;
702 /* wbk -b flag (from Gentoo patchkit) */
703 if (!show_buffers)
704 st->rt_stat -= buffers+cached;
705 /* As with the amount of memory used, it's not recorded any more, so
706 * we have to calculate it ourselves.
708 st2->rt_stat = st2->rt_idle - swapfree;
711 void update_stat_swp(stat_dev *st)
713 static char *line = NULL;
714 static size_t line_size = 0;
715 unsigned long swapfree;
717 fseek(fp_meminfo, 0, SEEK_SET);
718 while ((getline(&line, &line_size, fp_meminfo)) > 0) {
719 /* As with update_stat_mem(), the format change to /proc/meminfo has
720 * forced some changes here. */
721 if (strstr(line, "SwapTotal:"))
722 sscanf(line, "SwapTotal: %ld", &st->rt_idle);
723 else if (strstr(line, "SwapFree:"))
724 sscanf(line, "SwapFree: %ld", &swapfree);
726 st->rt_stat = st->rt_idle - swapfree;
729 /*******************************************************************************\
730 |* get_statistics *|
731 \*******************************************************************************/
732 void get_statistics(char *devname, long *is, long *ds, long *idle, long *ds2, long *idle2)
734 int i;
735 static char *line = NULL;
736 static size_t line_size = 0;
737 char *p;
738 char *tokens = " \t\n";
739 float f;
741 *is = 0;
742 *ds = 0;
743 *idle = 0;
745 if (!strncmp(devname, "cpu", 3)) {
746 fseek(fp_stat, 0, SEEK_SET);
747 while ((getline(&line, &line_size, fp_stat)) > 0) {
748 if (strstr(line, "cpu")) {
749 int cpu = -1; /* by default, cumul stats => average */
750 if (!strstr(line, "cpu ")) {
751 sscanf(line, "cpu%d", &cpu);
752 ds2[cpu] = 0;
753 idle2[cpu] = 0;
755 p = strtok(line, tokens);
756 /* 1..3, 4 == idle, we don't want idle! */
757 for (i=0; i<3; i++) {
758 p = strtok(NULL, tokens);
759 if (cpu == -1)
760 *ds += atol(p);
761 else
762 ds2[cpu] += atol(p);
764 p = strtok(NULL, tokens);
765 if (cpu == -1)
766 *idle = atol(p);
767 else
768 idle2[cpu] = atol(p);
771 if ((fp_loadavg = freopen("/proc/loadavg", "r", fp_loadavg)) == NULL)
772 perror("ger_statistics(): freopen(proc/loadavg) failed!\n");
774 if (fscanf(fp_loadavg, "%f", &f) == EOF)
775 perror("fscanf() failed to read f\n");
776 *is = (long) (100 * f);
779 if (!strncmp(devname, "i/o", 3)) {
780 if (fseek(fp_diskstats, 0, SEEK_SET) == -1)
781 perror("get_statistics() seek failed\n");
783 /* wbk 20120308 These are no longer in /proc/stat. /proc/diskstats
784 * seems to be the closest replacement. Under modern BSD's, /proc is
785 * now deprecated, so iostat() might be the answer.
786 * http://www.gossamer-threads.com/lists/linux/kernel/314618
787 * has good info on this being removed from kernel. Also see
788 * kernel sources Documentation/iostats.txt
790 * TODO: We will end up with doubled values. We are adding the
791 * aggregate to the individual partition, due to device selection
792 * logic. Either grab devices' stats with numbers, or without (sda
793 * OR sda[1..10]. Could use strstr() return plus offset, but would
794 * have to be careful with bounds checking since we're in a
795 * limited buffer. Or just divide by 2 (inefficient). Shouldn't
796 * matter for graphing (we care about proportions, not numbers). */
797 while ((getline(&line, &line_size, fp_diskstats)) > 0) {
798 if (strstr(line, "sd") || strstr(line, "sr")) {
799 p = strtok(line, tokens);
800 /* skip 3 tokens, then use fields from
801 `* linux/Documentation/iostats.txt */
802 for (i = 1; i <= 6; i++)
803 p = strtok(NULL, tokens);
805 *ds += atol(p);
806 for (i = 7; i <= 10; i++)
807 p = strtok(NULL, tokens);
809 *ds += atol(p);
816 /*******************************************************************************\
817 |* getWidth *|
818 \*******************************************************************************/
819 unsigned long getWidth(long actif, long idle)
821 /* wbk - work with a decimal value so we don't round < 1 down to zero. */
822 double j = 0;
823 unsigned long r = 0;
825 j = (actif + idle);
826 if (j != 0)
827 j = (actif * 100) / j;
829 j = j * 0.32;
831 /* round up very low positive values so they are visible. */
832 if (actif > 0 && j < 2)
833 j = 2;
835 if (j > 32)
836 j = 32;
838 r = (unsigned long) j;
839 return r;
843 /*******************************************************************************\
844 |* getNbCPU *|
845 \*******************************************************************************/
846 int getNbCPU(void)
848 static char *line = NULL;
849 static size_t line_size = 0;
850 int cpu = 0;
852 fseek(fp_stat, 0, SEEK_SET);
853 while ((getline(&line, &line_size, fp_stat)) > 0) {
854 if (strstr(line, "cpu") && !strstr(line, "cpu "))
855 sscanf(line, "cpu%d", &cpu);
858 return cpu+1;
862 /*******************************************************************************\
863 |* checksysdevs *|
864 \*******************************************************************************/
865 int checksysdevs(void) {
866 strcpy(stat_device[0].name, "cpu0");
867 strcpy(stat_device[1].name, "i/o");
868 strcpy(stat_device[2].name, "sys");
870 return 3;
874 /*******************************************************************************\
875 |* void DrawActive(char *) *|
876 \*******************************************************************************/
877 void DrawActive(char *name)
880 /* Alles op X,77
881 CPU: 0
882 I/O: 21
884 20 Breed, 10 hoog
885 Destinatie: 5,5
888 if (name[0] == 'c')
889 copyXPMArea(0, 77, 19, 10, 5, 5);
890 else if (name[0] == 'i')
891 copyXPMArea(19, 77, 19, 10, 5, 5);
895 /*******************************************************************************\
896 |* DrawStats *|
897 \*******************************************************************************/
898 void DrawStats(int *his, int num, int size, int x_left, int y_bottom)
900 int pixels_per_byte, j, k, *p, d;
902 pixels_per_byte = 100;
903 p = his;
905 for (j=0; j<num; j++) {
906 if (p[0] > pixels_per_byte)
907 pixels_per_byte += 100;
908 p += 1;
911 p = his;
913 for (k=0; k<num; k++) {
914 d = (1.0 * p[0] / pixels_per_byte) * size;
916 for (j=0; j<size; j++) {
917 if (j < d - 3)
918 copyXPMArea(2, 88, 1, 1, k+x_left, y_bottom-j);
919 else if (j < d)
920 copyXPMArea(2, 89, 1, 1, k+x_left, y_bottom-j);
921 else
922 copyXPMArea(2, 90, 1, 1, k+x_left, y_bottom-j);
924 p += 1;
927 /* Nu horizontaal op 100/200/300 etc lijntje trekken! */
928 for (j = pixels_per_byte-100; j > 0; j-=100) {
929 for (k=0; k<num; k++) {
930 d = (40.0 / pixels_per_byte) * j;
932 copyXPMArea(2, 91, 1, 1, k+x_left, y_bottom-d);
938 /*******************************************************************************\
939 |* DrawStats_io *|
940 \*******************************************************************************/
941 void DrawStats_io(int *his, int num, int size, int x_left, int y_bottom)
943 float pixels_per_byte;
944 int j, k, *p;
945 /* wbk - Use a double to avoid rounding values of d < 1 to zero. */
946 double d = 0;
947 int border = 3;
949 /* wbk - this should not be static. No need to track the scale, since
950 * we always calculate it on the fly anyway. This static variable did
951 * not get re-initialized when we entered this function, so the scale
952 * would always grow and never shrink.
954 /*static int global_io_scale = 1;*/
955 int io_scale = 1;
957 p = his;
958 for (j=0; j<num; j++)
959 if (p[j] > io_scale) io_scale = p[j];
961 pixels_per_byte = 1.0 * io_scale / size;
962 if (pixels_per_byte == 0)
963 pixels_per_byte = 1;
965 for (k=0; k<num; k++) {
966 d = (1.0 * p[0] / pixels_per_byte);
968 /* graph values too low for graph resolution */
969 if (d > 0 && d < 1) {
970 d = 3;
971 border = 2;
972 } else {
973 border = 3;
976 for (j=0; j<size; j++) {
977 if (j < d - border)
978 copyXPMArea(2, 88, 1, 1, k+x_left, y_bottom-j);
979 else if (j < d )
980 copyXPMArea(2, 89, 1, 1, k+x_left, y_bottom-j);
981 else
982 copyXPMArea(2, 90, 1, 1, k+x_left, y_bottom-j);
984 p += 1; /* beware... */
989 /*******************************************************************************\
990 |* usage *|
991 \*******************************************************************************/
992 void usage(char *name)
994 printf("Usage: %s [OPTION]...\n", name);
995 printf("WindowMaker dockapp that displays system information.\n");
996 printf("\n");
997 printf(" -display DISPLAY contact the DISPLAY X server\n");
998 printf(" -geometry GEOMETRY position the clock at GEOMETRY\n");
999 printf(" -l locked view - cannot cycle modes\n");
1000 printf(" -c show average and max CPU for SMP machine.\n");
1001 printf(" default if there is more than %d processors\n", MAX_CPU);
1002 printf(" -i start in Disk I/O mode\n");
1003 printf(" -s start in System Info mode\n");
1004 printf(" -b include buffers and cache in memory usage\n");
1005 printf(" -h display this help and exit\n");
1006 printf(" -v output version information and exit\n");
1010 /*******************************************************************************\
1011 |* printversion *|
1012 \*******************************************************************************/
1013 void printversion(void)
1015 printf("WMMon version %s\n", WMMON_VERSION);
1017 /* vim: sw=4 ts=4 columns=82