dockapps/wmmon/wmmon.c
Doug Torrance 3ec1a9cde3 wmmon: Fix jiffy counter overflowing long on 32-bit systems.
Based on patch by Pedro Gimeno Fortea for Debian bug #670151 [1].

[1] https://bugs.debian.org/670151
2017-08-12 22:40:32 +01:00

1026 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;
pixels_per_byte = 100;
p = his;
for (j=0; j<num; j++) {
if (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! */
for (j = pixels_per_byte-100; j > 0; j-=100) {
for (k=0; k<num; k++) {
d = (40.0 / pixels_per_byte) * j;
copyXPMArea(2, 91, 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
*/