dockapps/wmmon/wmmon.c
Doug Torrance 2a199f8b19 wmmon: Add support for larger load averages.
Previously, a green hint line was drawn on the CPU window for each
multiple of 1.  This meant as the load average approached 40 (the
height of the window), the window would gradually fill up entirely
with green.

To get around this, we switch to a yellow hint line for each
multiple of 10 once the load passes 10.  And then a red hint line
for each multiple of 100 once the load passes 100.

This fixes Debian bug #894801, reported by Pedro Gimeno
<debian.bts@personal.formauri.es>.
2018-04-11 00:07:03 +01:00

1036 lines
27 KiB
C

/*
Code based on wmppp/wmifs
[Orig WMPPP comments]
This code was mainly put together by looking at the
following programs:
asclock
A neat piece of equip, used to display the date
and time on the screen.
Comes with every AfterStep installation.
Source used:
How do I create a not so solid window?
How do I open a window?
How do I use pixmaps?
------------------------------------------------------------
Authors: Martijn Pieterse (pieterse@xs4all.nl)
Antoine Nulle (warp@xs4all.nl)
This program is distributed under the GPL license.
(as were asclock and pppstats)
----
Changes:
----
17/03/2014 (Pedro Gimeno Fortea)
* Fix jiffy counter overflowing long on 32-bit systems.
17/06/2012 (Rodolfo García Peñas (kix), <kix@kix.es>)
* Code style.
13/3/2012 (Barry Kelly (wbk), <coydog@devio.us>)
* Fixed get_statistics() I/O features to work with newer
/proc/diskstats instead of the old /proc/stat.
* Fixes to graph/meter scaling for I/O. Original code let
the scaling grow out of control due to inappropriate static
data.
* Eliminated rounding down relatively low stats in getWidth()
and DrawStats_io() by using double and float types instead
of ints. We now round up tiny values to prevent the system
appearing idle when it's not.
* Style/readbility edits.
* TODO: Merge in Gentoo and possibly BSD local patches. This
should aid in fixing I/O monitoring on non-Linux systems.
* TODO: Color swapping. User supplies color values in .rc, and
app modifies pixmap in memory on startup. Should be simple.
* TODO: address compiler warnings (GCC has gotten pickier over
the years).
17/10/2009 (Romuald Delavergne, romuald.delavergne@free.fr)
* Support SMP processors in realtime CPU stress meter
15/05/2004 (Simon Law, sfllaw@debian.org)
* Support disabling of mode-cycling
23/10/2003 (Simon Law, sfllaw@debian.org)
* Eliminated exploitable static buffers
* Added -geometry support.
* /proc/meminfo support for Linux 2.6
18/05/1998 (Antoine Nulle, warp@xs4all.nl)
* MEM/SWAP/UPTIME only updated when visible
* Using global file descriptors to reduce file
system overhead, both updates are based on a diff
supplied by Dave Harden (dharden@wisewire.com)
15/05/1998 (Antoine Nulle, warp@xs4all.nl)
* Fixed memory overflow in the MEM gaugebar
* MEM gauge displays now real used mem
(buffered + cached mem removed)
14/05/1998 (Antoine Nulle, warp@xs4all.nl)
* Added -i & -s kludge for selecting startupmode,
tijno, don't hate me for this :)
12/05/1998 (Antoine Nulle, warp@xs4all.nl)
* Finetuned master-xpm, tijno don't worry, no
reprogramming needed this time ;-)
07/05/1998 (Martijn Pieterse, pieterse@xs4all.nl)
* Added disk i/o
03/05/1998 (Antoine Nulle, warp@xs4all.nl)
* Added new master-xpm which contains the gfx
for the upcoming SysInfo part :P
02/05/1998 (Martijn Pieterse, pieterse@xs4all.nl)
* Removed a lot of code, that was put in wmgeneral
23/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
* Added zombie destroying code (aka wait :) )
18/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
* Added CPU-on-screen.
* Added -display command line
15/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
* Fixed a bug in the stats routine
(Top 3 bright pixels were not shown when 100% loaded)
* Changed xpm to a no-title one.
This included the reprogramming of all positions.
warpstah, i hate you! ;)
05/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
* First Working Version
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <libdockapp/wmgeneral.h>
#include <libdockapp/misc.h>
#include "ulllib.h"
#include "wmmon-master.xpm"
#include "wmmon-mask.xbm"
/***********/
/* Defines */
/***********/
#define HISTORY_ENTRIES 55
#define MAX_CPU (10) /* depends on graph height */
#define MAX_STAT_DEVICES (4)
/********************/
/* Global Variables */
/********************/
int stat_current = 0; /* now global */
int mode_cycling = 1; /* Allow mode-cycling */
int cpu_avg_max = 0; /* CPU stress meter with average and max for SMP */
int show_buffers = 0; /* wbk adding per Gentoo -b enhancement. */
FILE *fp_meminfo;
FILE *fp_stat;
FILE *fp_loadavg;
FILE *fp_diskstats; /* wbk new io stats API */
/* functions */
void usage(char*);
void printversion(void);
void DrawStats(int *, int, int, int, int);
void DrawStats_io(int *, int, int, int, int);
void wmmon_routine(int, char **);
int main(int argc, char *argv[])
{
int i;
char *name = argv[0];
/* Parse Command Line */
for (i = 1; i < argc; i++) {
char *arg = argv[i];
if (*arg=='-')
switch (arg[1]) {
case 'd' :
if (strcmp(arg+1, "display")) {
usage(name);
return 1;
}
break;
case 'g' :
if (strcmp(arg+1, "geometry")) {
usage(name);
return 1;
}
case 'l' :
mode_cycling = 0;
break;
case 'c' :
cpu_avg_max = 1;
break;
case 'i' :
stat_current = 1;
break;
case 'b' :
show_buffers = 1;
break;
case 's' :
stat_current = 2;
break;
case 'v' :
printversion();
return 0;
default:
usage(name);
return 1;
}
}
wmmon_routine(argc, argv);
exit(0);
}
/*******************************************************************************\
|* wmmon_routine *|
\*******************************************************************************/
typedef struct {
char name[5]; /* "cpu0..cpuz", eventually.. :) */
int his[HISTORY_ENTRIES];
int hisaddcnt;
long rt_stat;
ullong statlast;
long rt_idle;
ullong idlelast;
/* Processors stats */
long *cpu_stat;
ullong *cpu_last;
long *idle_stat;
ullong *idle_last;
} stat_dev;
stat_dev stat_device[MAX_STAT_DEVICES];
char *left_action, *right_action, *middle_action;
int nb_cpu, cpu_max;
int getNbCPU(void);
unsigned long getWidth(long, long);
int checksysdevs(void);
void get_statistics(char *, long *, ullong *, ullong *, ullong *, ullong *);
void DrawActive(char *);
void update_stat_cpu(stat_dev *, ullong *, ullong *);
void update_stat_io(stat_dev *);
void update_stat_mem(stat_dev *st, stat_dev *st2);
void update_stat_swp(stat_dev *);
void wmmon_routine(int argc, char **argv)
{
rckeys wmmon_keys[] = {
{ "left", &left_action },
{ "right", &right_action },
{ "middle", &middle_action },
{ NULL, NULL }
};
int i, j;
long k;
XEvent Event;
int but_stat = -1;
int stat_online;
long starttime, curtime, nexttime;
ullong istat, idle, *istat2, *idle2;
FILE *fp;
char *conffile = NULL;
int xpm_X = 0, xpm_Y = 0;
long online_time = 0;
long ref_time = 0;
long cnt_time;
fp = fopen("/proc/uptime", "r");
fp_meminfo = fopen("/proc/meminfo", "r");
fp_loadavg = fopen("/proc/loadavg", "r");
fp_stat = fopen("/proc/stat", "r");
fp_diskstats = fopen("/proc/diskstats", "r");
if (fp) {
if (fscanf(fp, "%ld", &online_time) == EOF)
perror("Error! fscanf() of /proc/uptime failed!\n");
ref_time = time(0);
fclose(fp);
}
for (i = 0; i < MAX_STAT_DEVICES; i++) {
for (j = 0; j < HISTORY_ENTRIES; j++)
stat_device[i].his[j] = 0;
stat_device[i].hisaddcnt = 0;
}
/* wbk - I don't fully understand this. Probably just a means of providing
* test cases. ifdef'ing to clear compiler warnings. TODO: remove. */
#ifdef LEFT_ACTION
if (LEFT_ACTION)
left_action = strdup(LEFT_ACTION);
#endif
#ifdef RIGHT_ACTION
if (RIGHT_ACTION)
right_action = strdup(RIGHT_ACTION);
#endif
#ifdef MIDDLE_ACTION
if (MIDDLE_ACTION)
middle_action = strdup(MIDDLE_ACTION);
#endif
/* Scan through the .rc files */
if (asprintf(&conffile, "/etc/wmmonrc") >= 0) {
parse_rcfile(conffile, wmmon_keys);
free(conffile);
}
if (asprintf(&conffile, "%s/.wmmonrc", getenv("HOME")) >= 0) {
parse_rcfile(conffile, wmmon_keys);
free(conffile);
}
if (asprintf(&conffile, "/etc/wmmonrc.fixed") >= 0) {
parse_rcfile(conffile, wmmon_keys);
free(conffile);
}
stat_online = checksysdevs();
nb_cpu = getNbCPU();
stat_device[0].cpu_stat = calloc(nb_cpu, sizeof(long));
stat_device[0].cpu_last = calloc(nb_cpu, sizeof(ullong));
stat_device[0].idle_stat = calloc(nb_cpu, sizeof(long));
stat_device[0].idle_last = calloc(nb_cpu, sizeof(ullong));
if (!stat_device[0].cpu_stat ||
!stat_device[0].cpu_last ||
!stat_device[0].idle_stat ||
!stat_device[0].idle_last) {
fprintf(stderr, "%s: Unable to alloc memory !\n", argv[0]);
exit(1);
}
istat2 = calloc(nb_cpu, sizeof(ullong));
idle2 = calloc(nb_cpu, sizeof(ullong));
if (!istat2 || !idle2) {
fprintf(stderr, "%s: Unable to alloc memory !!\n", argv[0]);
exit(1);
}
openXwindow(argc, argv, wmmon_master_xpm, (char *)wmmon_mask_bits,
wmmon_mask_width, wmmon_mask_height);
/* add mouse region */
AddMouseRegion(0, 12, 13, 58, 57);
AddMouseRegion(1, 5, 5, 24, 14);
starttime = time(0);
nexttime = starttime + 10;
/* Collect information on each panel */
for (i = 0; i < stat_online; i++) {
get_statistics(stat_device[i].name, &k, &istat, &idle, istat2, idle2);
stat_device[i].statlast = istat;
stat_device[i].idlelast = idle;
if (i == 0 && nb_cpu > 1) {
int cpu;
for (cpu = 0; cpu < nb_cpu; cpu++) {
stat_device[i].cpu_last[cpu] = istat2[cpu];
stat_device[i].idle_last[cpu] = idle2[cpu];
}
}
}
/* Set the mask for the current window */
switch (stat_current) {
case 0:
case 1:
xpm_X = 0;
setMaskXY(0, 0);
break;
case 2:
xpm_X = 64;
setMaskXY(-64, 0);
default:
break;
}
/* Draw statistics */
if (stat_current == 0) {
DrawStats(stat_device[stat_current].his,
HISTORY_ENTRIES-1, 40, 5, 58);
} else if (stat_current == 1) {
DrawStats_io(stat_device[stat_current].his,
HISTORY_ENTRIES, 40, 5, 58);
}
DrawActive(stat_device[stat_current].name);
while (1) {
curtime = time(NULL);
waitpid(0, NULL, WNOHANG);
update_stat_cpu(&stat_device[0], istat2, idle2);
update_stat_io(&stat_device[1]);
if(stat_current == 2)
update_stat_mem(&stat_device[2], &stat_device[3]);
if (stat_current < 2) {
i = stat_current;
/* Load ding is 45 pixels hoog */
copyXPMArea(0, 64, 32, 12, 28, 4);
if (i == 0 && nb_cpu > 1) {
if (nb_cpu > MAX_CPU || cpu_avg_max) {
/* show average CPU */
j = getWidth(stat_device[i].rt_stat, stat_device[i].rt_idle);
copyXPMArea(32, 64, j, 6, 28, 4);
/* Show max CPU */
j = getWidth(stat_device[i].cpu_stat[cpu_max],
stat_device[i].idle_stat[cpu_max]);
copyXPMArea(32, 70, j, 6, 28, 10);
} else {
int cpu;
for (cpu = 0; cpu < nb_cpu; cpu++) {
j = getWidth(stat_device[i].cpu_stat[cpu],
stat_device[i].idle_stat[cpu]);
copyXPMArea(32, 65, j,
MAX_CPU / nb_cpu, 28,
5 + (MAX_CPU / nb_cpu) * cpu);
}
}
} else {
j = getWidth(stat_device[i].rt_stat, stat_device[i].rt_idle);
copyXPMArea(32, 64, j, 12, 28, 4);
}
} else {
/* Nu zal ie wel 3 zijn. */
copyXPMArea(0, 64, 32, 12, 28+64, 4);
copyXPMArea(0, 64, 32, 12, 28+64, 18);
j = stat_device[2].rt_idle;
if (j != 0) {
j = (stat_device[2].rt_stat * 100) / j;
}
j = j * 0.32;
if (j > 32) j = 32;
copyXPMArea(32, 64, j, 12, 28+64, 4);
/*--------------------- swap? ------------------*/
j = stat_device[3].rt_idle;
if (j != 0)
j = (stat_device[3].rt_stat * 100) / j;
j = j * 0.32;
if (j > 32) j = 32;
copyXPMArea(32, 64, j, 12, 28+64, 18);
/*----------- online tijd neerzetten! ----------*/
cnt_time = time(0) - ref_time + online_time;
/* cnt_time = uptime in seconden */
/*
secs = 108,47
mins = 89,47
uren = 70,47
digits = 40,78, 6breed, 9hoog
*/
i = cnt_time % 60;
cnt_time /= 60;
copyXPMArea(40 + (i % 10)*7, 78, 6, 9, 115, 47);
copyXPMArea(40 + (i / 10)*7, 78, 6, 9, 108, 47);
i = cnt_time % 60;
cnt_time /= 60;
copyXPMArea(40 + (i % 10)*7, 78, 6, 9, 96, 47);
copyXPMArea(40 + (i / 10)*7, 78, 6, 9, 89, 47);
i = cnt_time % 24;
cnt_time /= 24;
copyXPMArea(40 + (i % 10)*7, 78, 6, 9, 77, 47);
copyXPMArea(40 + (i / 10)*7, 78, 6, 9, 70, 47);
/* De rest is dagen! 5x7*/
i = cnt_time;
copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 88, 35);
i /= 10;
copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 82, 35);
i /= 10;
copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 76, 35);
i /= 10;
copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 70, 35);
}
if (curtime >= nexttime) {
nexttime+=10;
if (curtime > nexttime) /* dont let APM suspends make this crazy */
nexttime = curtime;
for (i=0; i<stat_online; i++) {
stat_dev *sd = stat_device + i;
if (sd->his[HISTORY_ENTRIES-1])
sd->his[HISTORY_ENTRIES-1] /= sd->hisaddcnt;
for (j = 1; j < HISTORY_ENTRIES; j++)
sd->his[j-1] = sd->his[j];
if (i == stat_current) {
if (i == 0)
DrawStats(sd->his, HISTORY_ENTRIES - 1, 40, 5, 58);
else if (i == 1)
DrawStats_io(sd->his, HISTORY_ENTRIES - 1, 40, 5, 58);
}
sd->his[HISTORY_ENTRIES-1] = 0;
sd->hisaddcnt = 0;
}
}
RedrawWindowXY(xpm_X, xpm_Y);
while (XPending(display)) {
XNextEvent(display, &Event);
switch (Event.type) {
case Expose:
RedrawWindowXY(xpm_X, xpm_Y);
break;
case DestroyNotify:
XCloseDisplay(display);
exit(0);
break;
case ButtonPress:
but_stat = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
break;
case ButtonRelease:
i = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
if (but_stat == i && but_stat >= 0) {
switch (but_stat) {
case 0:
switch (Event.xbutton.button) {
case 1:
if (left_action)
execCommand(left_action);
break;
case 2:
if (middle_action)
execCommand(middle_action);
break;
case 3:
if (right_action)
execCommand(right_action);
break;
}
break;
case 1:
if (mode_cycling) {
stat_current++;
if (stat_current == stat_online)
stat_current = 0;
DrawActive(stat_device[stat_current].name);
if (stat_current == 0)
DrawStats(stat_device[stat_current].his,
HISTORY_ENTRIES-1, 40, 5, 58);
if (stat_current == 1)
DrawStats_io(stat_device[stat_current].his,
HISTORY_ENTRIES-1, 40, 5, 58);
if (stat_current == 2) {
xpm_X = 64;
setMaskXY(-64, 0);
} else {
xpm_X = 0;
setMaskXY(0, 0);
}
RedrawWindowXY(xpm_X, xpm_Y);
}
break;
}
}
break;
}
}
usleep(250000L);
}
}
void update_stat_cpu(stat_dev *st, ullong *istat2, ullong *idle2)
{
long k;
ullong istat, idle;
get_statistics(st->name, &k, &istat, &idle, istat2, idle2);
st->rt_idle = ullsub(&idle, &st->idlelast);
st->idlelast = idle;
st->rt_stat = ullsub(&istat, &st->statlast);
st->statlast = istat;
if (nb_cpu > 1) {
int cpu;
unsigned long max, j;
cpu_max = 0; max = 0;
for (cpu = 0; cpu < nb_cpu; cpu++) {
st->idle_stat[cpu] = ullsub(&idle2[cpu], &st->idle_last[cpu]);
st->idle_last[cpu] = idle2[cpu];
st->cpu_stat[cpu] = ullsub(&istat2[cpu], &st->cpu_last[cpu]);
st->cpu_last[cpu] = istat2[cpu];
j = st->cpu_stat[cpu] + st->idle_stat[cpu];
if (j != 0)
j = (st->cpu_stat[cpu] << 7) / j;
if (j > max) {
max = j;
cpu_max = cpu;
}
}
}
st->his[HISTORY_ENTRIES-1] += k;
st->hisaddcnt += 1;
}
void update_stat_io(stat_dev *st)
{
long j, k;
ullong istat, idle;
/* Periodically re-sample. Sometimes we get anomalously high readings;
* this discards them. */
static int stalemax = 300;
static long maxdiskio = 0;
if (--stalemax <= 0) {
maxdiskio = 0;
stalemax = 300;
}
get_statistics(st->name, &k, &istat, &idle, NULL, NULL);
st->rt_idle = ullsub(&idle, &st->idlelast);
st->idlelast = idle;
st->rt_stat = ullsub(&istat, &st->statlast);
st->statlast = istat;
/* remember peak for scaling of upper-right meter. */
j = st->rt_stat;
if (maxdiskio < j)
maxdiskio = j;
/* Calculate scaling factor for upper-right meter. "/ 5" will clip
* the highest peaks, but makes moderate values more visible. We are
* compensating for wild fluctuations which are probably caused by
* kernel I/O buffering.
*/
st->rt_idle = (maxdiskio - j) / 5;
if (j > 0 && st->rt_idle < 1)
st->rt_idle = 1; /* scale up tiny values so they are visible */
st->his[HISTORY_ENTRIES-1] += st->rt_stat;
st->hisaddcnt += 1;
}
void update_stat_mem(stat_dev *st, stat_dev *st2)
{
static char *line = NULL;
static size_t line_size = 0;
unsigned long swapfree;
unsigned long free, shared, buffers, cached;
if (freopen("/proc/meminfo", "r", fp_meminfo) == NULL)
perror("freopen() of /proc/meminfo failed!)\n");
while ((getline(&line, &line_size, fp_meminfo)) > 0) {
/* The original format for the first two lines of /proc/meminfo was
* Mem: total used free shared buffers cached
* Swap: total used free
*
* As of at least 2.5.47 these two lines were removed, so that the
* required information has to come from the rest of the lines.
* On top of that, used is no longer recorded - you have to work
* this out yourself, from total - free.
*
* So, these changes below should work. They should also work with
* older kernels, too, since the new format has been available for
* ages.
*/
if (strstr(line, "MemTotal:"))
sscanf(line, "MemTotal: %ld", &st->rt_idle);
else if (strstr(line, "MemFree:"))
sscanf(line, "MemFree: %lu", &free);
else if (strstr(line, "MemShared:"))
sscanf(line, "MemShared: %lu", &shared);
else if (strstr(line, "Buffers:"))
sscanf(line, "Buffers: %lu", &buffers);
else if (strstr(line, "Cached:"))
sscanf(line, "Cached: %lu", &cached);
else if (strstr(line, "SwapTotal:"))
sscanf(line, "SwapTotal: %ld", &st2->rt_idle);
else if (strstr(line, "SwapFree:"))
sscanf(line, "SwapFree: %lu", &swapfree);
}
/* memory use - rt_stat is the amount used, it seems, and this isn't
* recorded in current version of /proc/meminfo (as of 2.5.47), so we
* calculate it from MemTotal - MemFree
*/
st->rt_stat = st->rt_idle - free;
/* wbk -b flag (from Gentoo patchkit) */
if (!show_buffers)
st->rt_stat -= buffers+cached;
/* As with the amount of memory used, it's not recorded any more, so
* we have to calculate it ourselves.
*/
st2->rt_stat = st2->rt_idle - swapfree;
}
void update_stat_swp(stat_dev *st)
{
static char *line = NULL;
static size_t line_size = 0;
unsigned long swapfree;
fseek(fp_meminfo, 0, SEEK_SET);
while ((getline(&line, &line_size, fp_meminfo)) > 0) {
/* As with update_stat_mem(), the format change to /proc/meminfo has
* forced some changes here. */
if (strstr(line, "SwapTotal:"))
sscanf(line, "SwapTotal: %ld", &st->rt_idle);
else if (strstr(line, "SwapFree:"))
sscanf(line, "SwapFree: %lu", &swapfree);
}
st->rt_stat = st->rt_idle - swapfree;
}
/*******************************************************************************\
|* get_statistics *|
\*******************************************************************************/
void get_statistics(char *devname, long *is, ullong *ds, ullong *idle, ullong *ds2, ullong *idle2)
{
int i;
static char *line = NULL;
static size_t line_size = 0;
char *p;
char *tokens = " \t\n";
float f;
ullong ulltmp;
*is = 0;
ullreset(ds);
ullreset(idle);
if (!strncmp(devname, "cpu", 3)) {
fseek(fp_stat, 0, SEEK_SET);
while ((getline(&line, &line_size, fp_stat)) > 0) {
if (strstr(line, "cpu")) {
int cpu = -1; /* by default, cumul stats => average */
if (!strstr(line, "cpu ")) {
sscanf(line, "cpu%d", &cpu);
ullreset(&ds2[cpu]);
ullreset(&idle2[cpu]);
}
p = strtok(line, tokens);
/* 1..3, 4 == idle, we don't want idle! */
for (i=0; i<3; i++) {
p = strtok(NULL, tokens);
ullparse(&ulltmp, p);
if (cpu == -1)
ulladd(ds, &ulltmp);
else
ulladd(&ds2[cpu], &ulltmp);
}
p = strtok(NULL, tokens);
if (cpu == -1)
ullparse(idle, p);
else
ullparse(&idle2[cpu], p);
}
}
if ((fp_loadavg = freopen("/proc/loadavg", "r", fp_loadavg)) == NULL)
perror("ger_statistics(): freopen(proc/loadavg) failed!\n");
if (fscanf(fp_loadavg, "%f", &f) == EOF)
perror("fscanf() failed to read f\n");
*is = (long) (100 * f);
}
if (!strncmp(devname, "i/o", 3)) {
if (fseek(fp_diskstats, 0, SEEK_SET) == -1)
perror("get_statistics() seek failed\n");
/* wbk 20120308 These are no longer in /proc/stat. /proc/diskstats
* seems to be the closest replacement. Under modern BSD's, /proc is
* now deprecated, so iostat() might be the answer.
* http://www.gossamer-threads.com/lists/linux/kernel/314618
* has good info on this being removed from kernel. Also see
* kernel sources Documentation/iostats.txt
*
* TODO: We will end up with doubled values. We are adding the
* aggregate to the individual partition, due to device selection
* logic. Either grab devices' stats with numbers, or without (sda
* OR sda[1..10]. Could use strstr() return plus offset, but would
* have to be careful with bounds checking since we're in a
* limited buffer. Or just divide by 2 (inefficient). Shouldn't
* matter for graphing (we care about proportions, not numbers). */
while ((getline(&line, &line_size, fp_diskstats)) > 0) {
if (strstr(line, "sd") || strstr(line, "sr")) {
p = strtok(line, tokens);
/* skip 3 tokens, then use fields from
`* linux/Documentation/iostats.txt */
for (i = 1; i <= 6; i++)
p = strtok(NULL, tokens);
ullparse(&ulltmp, p);
ulladd(ds, &ulltmp);
for (i = 7; i <= 10; i++)
p = strtok(NULL, tokens);
ullparse(&ulltmp, p);
ulladd(ds, &ulltmp);
}
}
}
}
/*******************************************************************************\
|* getWidth *|
\*******************************************************************************/
unsigned long getWidth(long actif, long idle)
{
/* wbk - work with a decimal value so we don't round < 1 down to zero. */
double j = 0;
unsigned long r = 0;
j = (actif + idle);
if (j != 0)
j = (actif * 100) / j;
j = j * 0.32;
/* round up very low positive values so they are visible. */
if (actif > 0 && j < 2)
j = 2;
if (j > 32)
j = 32;
r = (unsigned long) j;
return r;
}
/*******************************************************************************\
|* getNbCPU *|
\*******************************************************************************/
int getNbCPU(void)
{
static char *line = NULL;
static size_t line_size = 0;
int cpu = 0;
fseek(fp_stat, 0, SEEK_SET);
while ((getline(&line, &line_size, fp_stat)) > 0) {
if (strstr(line, "cpu") && !strstr(line, "cpu "))
sscanf(line, "cpu%d", &cpu);
}
return cpu+1;
}
/*******************************************************************************\
|* checksysdevs *|
\*******************************************************************************/
int checksysdevs(void) {
strcpy(stat_device[0].name, "cpu0");
strcpy(stat_device[1].name, "i/o");
strcpy(stat_device[2].name, "sys");
return 3;
}
/*******************************************************************************\
|* void DrawActive(char *) *|
\*******************************************************************************/
void DrawActive(char *name)
{
/* Alles op X,77
CPU: 0
I/O: 21
20 Breed, 10 hoog
Destinatie: 5,5
*/
if (name[0] == 'c')
copyXPMArea(0, 77, 19, 10, 5, 5);
else if (name[0] == 'i')
copyXPMArea(19, 77, 19, 10, 5, 5);
}
/*******************************************************************************\
|* DrawStats *|
\*******************************************************************************/
void DrawStats(int *his, int num, int size, int x_left, int y_bottom)
{
int pixels_per_byte, j, k, *p, d, hint_height, hint_color;
pixels_per_byte = 100;
p = his;
for (j=0; j<num; j++) {
while (p[0] > pixels_per_byte)
pixels_per_byte += 100;
p += 1;
}
p = his;
for (k=0; k<num; k++) {
d = (1.0 * p[0] / pixels_per_byte) * size;
for (j=0; j<size; j++) {
if (j < d - 3)
copyXPMArea(2, 88, 1, 1, k+x_left, y_bottom-j);
else if (j < d)
copyXPMArea(2, 89, 1, 1, k+x_left, y_bottom-j);
else
copyXPMArea(2, 90, 1, 1, k+x_left, y_bottom-j);
}
p += 1;
}
/* Nu horizontaal op 100/200/300 etc lijntje trekken! */
if (pixels_per_byte > 10000) {
hint_height = 10000;
hint_color = 93; /* red */
} else if (pixels_per_byte > 1000) {
hint_height = 1000;
hint_color = 92; /* yellow */
} else {
hint_height = 100;
hint_color = 91; /* green */
}
for (j = hint_height; j < pixels_per_byte; j += hint_height) {
d = (40.0 / pixels_per_byte) * j - 1;
for (k=0; k<num; k++) {
copyXPMArea(2, hint_color, 1, 1, k+x_left, y_bottom-d);
}
}
}
/*******************************************************************************\
|* DrawStats_io *|
\*******************************************************************************/
void DrawStats_io(int *his, int num, int size, int x_left, int y_bottom)
{
float pixels_per_byte;
int j, k, *p;
/* wbk - Use a double to avoid rounding values of d < 1 to zero. */
double d = 0;
int border = 3;
/* wbk - this should not be static. No need to track the scale, since
* we always calculate it on the fly anyway. This static variable did
* not get re-initialized when we entered this function, so the scale
* would always grow and never shrink.
*/
/*static int global_io_scale = 1;*/
int io_scale = 1;
p = his;
for (j=0; j<num; j++)
if (p[j] > io_scale) io_scale = p[j];
pixels_per_byte = 1.0 * io_scale / size;
if (pixels_per_byte == 0)
pixels_per_byte = 1;
for (k=0; k<num; k++) {
d = (1.0 * p[0] / pixels_per_byte);
/* graph values too low for graph resolution */
if (d > 0 && d < 1) {
d = 3;
border = 2;
} else {
border = 3;
}
for (j=0; j<size; j++) {
if (j < d - border)
copyXPMArea(2, 88, 1, 1, k+x_left, y_bottom-j);
else if (j < d )
copyXPMArea(2, 89, 1, 1, k+x_left, y_bottom-j);
else
copyXPMArea(2, 90, 1, 1, k+x_left, y_bottom-j);
}
p += 1; /* beware... */
}
}
/*******************************************************************************\
|* usage *|
\*******************************************************************************/
void usage(char *name)
{
printf("Usage: %s [OPTION]...\n", name);
printf("WindowMaker dockapp that displays system information.\n");
printf("\n");
printf(" -display DISPLAY contact the DISPLAY X server\n");
printf(" -geometry GEOMETRY position the clock at GEOMETRY\n");
printf(" -l locked view - cannot cycle modes\n");
printf(" -c show average and max CPU for SMP machine.\n");
printf(" default if there is more than %d processors\n", MAX_CPU);
printf(" -i start in Disk I/O mode\n");
printf(" -s start in System Info mode\n");
printf(" -b include buffers and cache in memory usage\n");
printf(" -h display this help and exit\n");
printf(" -v output version information and exit\n");
}
/*******************************************************************************\
|* printversion *|
\*******************************************************************************/
void printversion(void)
{
printf("WMMon version %s\n", PACKAGE_VERSION);
}
/* vim: sw=4 ts=4 columns=82
*/