summaryrefslogtreecommitdiffstats
path: root/ksysguard/ksysguardd/Linux/stat.c
diff options
context:
space:
mode:
Diffstat (limited to 'ksysguard/ksysguardd/Linux/stat.c')
-rw-r--r--ksysguard/ksysguardd/Linux/stat.c1184
1 files changed, 1184 insertions, 0 deletions
diff --git a/ksysguard/ksysguardd/Linux/stat.c b/ksysguard/ksysguardd/Linux/stat.c
new file mode 100644
index 000000000..0e03e4d53
--- /dev/null
+++ b/ksysguard/ksysguardd/Linux/stat.c
@@ -0,0 +1,1184 @@
+/*
+ KSysGuard, the KDE System Guard
+
+ Copyright (c) 1999, 2000 Chris Schlaeger <cs@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of version 2 of the GNU General Public
+ License as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "Command.h"
+#include "ksysguardd.h"
+
+#include "stat.h"
+
+typedef struct
+{
+ /* A CPU can be loaded with user processes, reniced processes and
+ * system processes. Unused processing time is called idle load.
+ * These variable store the percentage of each load type. */
+ int userLoad;
+ int niceLoad;
+ int sysLoad;
+ int idleLoad;
+
+ /* To calculate the loads we need to remember the tick values for each
+ * load type. */
+ unsigned long userTicks;
+ unsigned long niceTicks;
+ unsigned long sysTicks;
+ unsigned long idleTicks;
+} CPULoadInfo;
+
+typedef struct
+{
+ unsigned long delta;
+ unsigned long old;
+} DiskLoadSample;
+
+typedef struct
+{
+ /* 5 types of samples are taken:
+ total, rio, wio, rBlk, wBlk */
+ DiskLoadSample s[ 5 ];
+} DiskLoadInfo;
+
+typedef struct DiskIOInfo
+{
+ int major;
+ int minor;
+ int alive;
+ DiskLoadSample total;
+ DiskLoadSample rio;
+ DiskLoadSample wio;
+ DiskLoadSample rblk;
+ DiskLoadSample wblk;
+ struct DiskIOInfo* next;
+} DiskIOInfo;
+
+#define STATBUFSIZE (32 * 1024)
+
+static char StatBuf[ STATBUFSIZE ];
+static char VmStatBuf[ STATBUFSIZE ];
+static char IOStatBuf[ STATBUFSIZE ]; /* Buffer for /proc/diskstats */
+static int Dirty = 0;
+
+/* We have observed deviations of up to 5% in the accuracy of the timer
+ * interrupts. So we try to measure the interrupt interval and use this
+ * value to calculate timing dependant values. */
+static float timeInterval = 0;
+static struct timeval lastSampling;
+static struct timeval currSampling;
+static struct SensorModul* StatSM;
+
+static CPULoadInfo CPULoad;
+static CPULoadInfo* SMPLoad = 0;
+static unsigned CPUCount = 0;
+static DiskLoadInfo* DiskLoad = 0;
+static unsigned DiskCount = 0;
+static DiskIOInfo* DiskIO = 0;
+static unsigned long PageIn = 0;
+static unsigned long OldPageIn = 0;
+static unsigned long PageOut = 0;
+static unsigned long OldPageOut = 0;
+static unsigned long Ctxt = 0;
+static unsigned long OldCtxt = 0;
+static unsigned int NumOfInts = 0;
+static unsigned long* OldIntr = 0;
+static unsigned long* Intr = 0;
+
+static int initStatDisk( char* tag, char* buf, const char* label, const char* shortLabel,
+ int idx, cmdExecutor ex, cmdExecutor iq );
+static void updateCPULoad( const char* line, CPULoadInfo* load );
+static int processDisk( char* tag, char* buf, const char* label, int idx );
+static void processStat( void );
+static int processDiskIO( const char* buf );
+static int process26DiskIO( const char* buf );
+static void cleanupDiskList( void );
+
+static int initStatDisk( char* tag, char* buf, const char* label,
+ const char* shortLabel, int idx, cmdExecutor ex, cmdExecutor iq )
+{
+ char sensorName[ 128 ];
+
+ gettimeofday( &lastSampling, 0 );
+
+ if ( strcmp( label, tag ) == 0 ) {
+ unsigned int i;
+ buf = buf + strlen( label ) + 1;
+
+ for ( i = 0; i < DiskCount; ++i ) {
+ sscanf( buf, "%lu", &DiskLoad[ i ].s[ idx ].old );
+ while ( *buf && isblank( *buf++ ) );
+ while ( *buf && isdigit( *buf++ ) );
+ sprintf( sensorName, "disk/disk%d/%s", i, shortLabel );
+ registerMonitor( sensorName, "integer", ex, iq, StatSM );
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void updateCPULoad( const char* line, CPULoadInfo* load )
+{
+ unsigned long currUserTicks, currSysTicks, currNiceTicks, currIdleTicks;
+ unsigned long totalTicks;
+
+ sscanf( line, "%*s %lu %lu %lu %lu", &currUserTicks, &currNiceTicks,
+ &currSysTicks, &currIdleTicks );
+
+ totalTicks = ( currUserTicks - load->userTicks ) +
+ ( currSysTicks - load->sysTicks ) +
+ ( currNiceTicks - load->niceTicks ) +
+ ( currIdleTicks - load->idleTicks );
+
+ if ( totalTicks > 10 ) {
+ load->userLoad = ( 100 * ( currUserTicks - load->userTicks ) ) / totalTicks;
+ load->sysLoad = ( 100 * ( currSysTicks - load->sysTicks ) ) / totalTicks;
+ load->niceLoad = ( 100 * ( currNiceTicks - load->niceTicks ) ) / totalTicks;
+ load->idleLoad = ( 100 - ( load->userLoad + load->sysLoad + load->niceLoad ) );
+ } else
+ load->userLoad = load->sysLoad = load->niceLoad = load->idleLoad = 0;
+
+ load->userTicks = currUserTicks;
+ load->sysTicks = currSysTicks;
+ load->niceTicks = currNiceTicks;
+ load->idleTicks = currIdleTicks;
+}
+
+static int processDisk( char* tag, char* buf, const char* label, int idx )
+{
+ if ( strcmp( label, tag ) == 0 ) {
+ unsigned long val;
+ unsigned int i;
+ buf = buf + strlen( label ) + 1;
+
+ for ( i = 0; i < DiskCount; ++i ) {
+ sscanf( buf, "%lu", &val );
+ while ( *buf && isblank( *buf++ ) );
+ while ( *buf && isdigit( *buf++ ) );
+ DiskLoad[ i ].s[ idx ].delta = val - DiskLoad[ i ].s[ idx ].old;
+ DiskLoad[ i ].s[ idx ].old = val;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int processDiskIO( const char* buf )
+{
+ /* Process disk_io lines as provided by 2.4.x kernels.
+ * disk_io: (2,0):(3,3,6,0,0) (3,0):(1413012,511622,12155382,901390,26486215) */
+ int major, minor;
+ unsigned long total, rblk, rio, wblk, wio;
+ DiskIOInfo* ptr = DiskIO;
+ DiskIOInfo* last = 0;
+ char sensorName[ 128 ];
+ const char* p;
+
+ p = buf + strlen( "disk_io: " );
+ while ( p && *p ) {
+ if ( sscanf( p, "(%d,%d):(%lu,%lu,%lu,%lu,%lu)", &major, &minor,
+ &total, &rio, &rblk, &wio, &wblk ) != 7 )
+ return -1;
+
+ last = 0;
+ ptr = DiskIO;
+ while ( ptr ) {
+ if ( ptr->major == major && ptr->minor == minor ) {
+ /* The IO device has already been registered. */
+ ptr->total.delta = total - ptr->total.old;
+ ptr->total.old = total;
+ ptr->rio.delta = rio - ptr->rio.old;
+ ptr->rio.old = rio;
+ ptr->wio.delta = wio - ptr->wio.old;
+ ptr->wio.old = wio;
+ ptr->rblk.delta = rblk - ptr->rblk.old;
+ ptr->rblk.old = rblk;
+ ptr->wblk.delta = wblk - ptr->wblk.old;
+ ptr->wblk.old = wblk;
+ ptr->alive = 1;
+ break;
+ }
+ last = ptr;
+ ptr = ptr->next;
+ }
+
+ if ( !ptr ) {
+ /* The IO device has not been registered yet. We need to add it. */
+ ptr = (DiskIOInfo*)malloc( sizeof( DiskIOInfo ) );
+ ptr->major = major;
+ ptr->minor = minor;
+ ptr->total.delta = 0;
+ ptr->total.old = total;
+ ptr->rio.delta = 0;
+ ptr->rio.old = rio;
+ ptr->wio.delta = 0;
+ ptr->wio.old = wio;
+ ptr->rblk.delta = 0;
+ ptr->rblk.old = rblk;
+ ptr->wblk.delta = 0;
+ ptr->wblk.old = wblk;
+ ptr->alive = 1;
+ ptr->next = 0;
+ if ( last ) {
+ /* Append new entry at end of list. */
+ last->next = ptr;
+ } else {
+ /* List is empty, so we insert the fist element into the list. */
+ DiskIO = ptr;
+ }
+
+ sprintf( sensorName, "disk/%d:%d/total", major, minor );
+ registerMonitor( sensorName, "integer", printDiskIO, printDiskIOInfo, StatSM );
+ sprintf( sensorName, "disk/%d:%d/rio", major, minor );
+ registerMonitor( sensorName, "integer", printDiskIO, printDiskIOInfo, StatSM );
+ sprintf( sensorName, "disk/%d:%d/wio", major, minor );
+ registerMonitor( sensorName, "integer", printDiskIO, printDiskIOInfo, StatSM );
+ sprintf( sensorName, "disk/%d:%d/rblk", major, minor );
+ registerMonitor( sensorName, "integer", printDiskIO, printDiskIOInfo, StatSM );
+ sprintf( sensorName, "disk/%d:%d/wblk", major, minor );
+ registerMonitor( sensorName, "integer", printDiskIO, printDiskIOInfo, StatSM );
+ }
+ /* Move p after the sencond ')'. We can safely assume that
+ * those two ')' exist. */
+ p = strchr( p, ')' ) + 1;
+ p = strchr( p, ')' ) + 1;
+ if ( p && *p )
+ p = strchr( p, '(' );
+ }
+
+ return 0;
+}
+
+static int process26DiskIO( const char* buf )
+{
+ /* Process values from /proc/diskstats (Linux >= 2.6.x) */
+
+ /* For each disk /proc/diskstats includes lines as follows:
+ * 3 0 hda 1314558 74053 26451438 14776742 1971172 4607401 52658448 202855090 0 9597019 217637839
+ * 3 1 hda1 178 360 0 0
+ * 3 2 hda2 354 360 0 0
+ * 3 3 hda3 354 360 0 0
+ * 3 4 hda4 0 0 0 0
+ * 3 5 hda5 529506 9616000 4745856 37966848
+ *
+ * - See Documentation/iostats.txt for details on the changes
+ */
+ int major, minor;
+ char devname[16];
+ unsigned long total,
+ rio, rmrg, rblk, rtim,
+ wio, wmrg, wblk, wtim,
+ ioprog, iotim, iotimw;
+ DiskIOInfo *ptr = DiskIO;
+ DiskIOInfo *last = 0;
+ char sensorName[128];
+
+ switch (sscanf(buf, "%d %d %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+ &major, &minor, devname,
+ &rio, &rmrg, &rblk, &rtim,
+ &wio, &wmrg, &wblk, &wtim,
+ &ioprog, &iotim, &iotimw))
+ {
+ case 7:
+ /* Partition stats entry */
+ /* Adjust read fields rio rmrg rblk rtim -> rio rblk wio wblk */
+ wblk = rtim;
+ wio = rblk;
+ rblk = rmrg;
+
+ total = rio + wio;
+
+ break;
+ case 14:
+ /* Disk stats entry */
+ total = rio + wio;
+
+ break;
+ default:
+ /* Something unexepected */
+ return -1;
+ }
+
+ last = 0;
+ ptr = DiskIO;
+ while (ptr)
+ {
+ if (ptr->major == major && ptr->minor == minor)
+ {
+ /* The IO device has already been registered. */
+ ptr->total.delta = total - ptr->total.old;
+ ptr->total.old = total;
+ ptr->rio.delta = rio - ptr->rio.old;
+ ptr->rio.old = rio;
+ ptr->wio.delta = wio - ptr->wio.old;
+ ptr->wio.old = wio;
+ ptr->rblk.delta = rblk - ptr->rblk.old;
+ ptr->rblk.old = rblk;
+ ptr->wblk.delta = wblk - ptr->wblk.old;
+ ptr->wblk.old = wblk;
+ ptr->alive = 1;
+ break;
+ }
+
+ last = ptr;
+ ptr = ptr->next;
+ }
+
+ if (!ptr)
+ {
+ /* The IO device has not been registered yet. We need to add it. */
+ ptr = (DiskIOInfo*)malloc( sizeof( DiskIOInfo ) );
+ ptr->major = major;
+ ptr->minor = minor;
+ ptr->total.delta = 0;
+ ptr->total.old = total;
+ ptr->rio.delta = 0;
+ ptr->rio.old = rio;
+ ptr->wio.delta = 0;
+ ptr->wio.old = wio;
+ ptr->rblk.delta = 0;
+ ptr->rblk.old = rblk;
+ ptr->wblk.delta = 0;
+ ptr->wblk.old = wblk;
+ ptr->alive = 1;
+ ptr->next = 0;
+ if (last)
+ {
+ /* Append new entry at end of list. */
+ last->next = ptr;
+ }
+ else
+ {
+ /* List is empty, so we insert the fist element into the list. */
+ DiskIO = ptr;
+ }
+
+ sprintf(sensorName, "disk/%d:%d/total", major, minor);
+ registerMonitor(sensorName, "integer", printDiskIO, printDiskIOInfo,
+ StatSM);
+ sprintf(sensorName, "disk/%d:%d/rio", major, minor);
+ registerMonitor(sensorName, "integer", printDiskIO, printDiskIOInfo,
+ StatSM);
+ sprintf(sensorName, "disk/%d:%d/wio", major, minor);
+ registerMonitor(sensorName, "integer", printDiskIO, printDiskIOInfo,
+ StatSM);
+ sprintf(sensorName, "disk/%d:%d/rblk", major, minor);
+ registerMonitor(sensorName, "integer", printDiskIO, printDiskIOInfo,
+ StatSM);
+ sprintf(sensorName, "disk/%d:%d/wblk", major, minor);
+ registerMonitor(sensorName, "integer", printDiskIO, printDiskIOInfo,
+ StatSM);
+ }
+
+ return 0;
+}
+
+static void cleanupDiskList( void )
+{
+ DiskIOInfo* ptr = DiskIO;
+ DiskIOInfo* last = 0;
+
+ while ( ptr ) {
+ if ( ptr->alive == 0 ) {
+ DiskIOInfo* newPtr;
+ char sensorName[ 128 ];
+
+ /* Disk device has disappeared. We have to remove it from
+ * the list and unregister the monitors. */
+ sprintf( sensorName, "disk/%d:%d/total", ptr->major, ptr->minor );
+ removeMonitor( sensorName );
+ sprintf( sensorName, "disk/%d:%d/rio", ptr->major, ptr->minor );
+ removeMonitor( sensorName );
+ sprintf( sensorName, "disk/%d:%d/wio", ptr->major, ptr->minor );
+ removeMonitor( sensorName );
+ sprintf( sensorName, "disk/%d:%d/rblk", ptr->major, ptr->minor );
+ removeMonitor( sensorName );
+ sprintf( sensorName, "disk/%d:%d/wblk", ptr->major, ptr->minor );
+ removeMonitor( sensorName );
+ if ( last ) {
+ last->next = ptr->next;
+ newPtr = ptr->next;
+ } else {
+ DiskIO = ptr->next;
+ newPtr = DiskIO;
+ last = 0;
+ }
+
+ free ( ptr );
+ ptr = newPtr;
+ } else {
+ ptr->alive = 0;
+ last = ptr;
+ ptr = ptr->next;
+ }
+ }
+}
+
+static void processStat( void )
+{
+ char format[ 32 ];
+ char tagFormat[ 16 ];
+ char buf[ 1024 ];
+ char tag[ 32 ];
+ char* statBufP = StatBuf;
+ char* vmstatBufP = VmStatBuf;
+ char* iostatBufP = IOStatBuf;
+
+ sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 );
+ sprintf( tagFormat, "%%%ds", (int)sizeof( tag ) - 1 );
+
+ while ( sscanf( statBufP, format, buf ) == 1 ) {
+ buf[ sizeof( buf ) - 1 ] = '\0';
+ statBufP += strlen( buf ) + 1; /* move statBufP to next line */
+ sscanf( buf, tagFormat, tag );
+
+ if ( strcmp( "cpu", tag ) == 0 ) {
+ /* Total CPU load */
+ updateCPULoad( buf, &CPULoad );
+ } else if ( strncmp( "cpu", tag, 3 ) == 0 ) {
+ /* Load for each SMP CPU */
+ int id;
+ sscanf( tag + 3, "%d", &id );
+ updateCPULoad( buf, &SMPLoad[ id ] );
+ } else if ( processDisk( tag, buf, "disk", 0 ) ) {
+ } else if ( processDisk( tag, buf, "disk_rio", 1 ) ) {
+ } else if ( processDisk( tag, buf, "disk_wio", 2 ) ) {
+ } else if ( processDisk( tag, buf, "disk_rblk", 3 ) ) {
+ } else if ( processDisk( tag, buf, "disk_wblk", 4 ) ) {
+ } else if ( strcmp( "disk_io:", tag ) == 0 ) {
+ processDiskIO( buf );
+ } else if ( strcmp( "page", tag ) == 0 ) {
+ unsigned long v1, v2;
+ sscanf( buf + 5, "%lu %lu", &v1, &v2 );
+ PageIn = v1 - OldPageIn;
+ OldPageIn = v1;
+ PageOut = v2 - OldPageOut;
+ OldPageOut = v2;
+ } else if ( strcmp( "intr", tag ) == 0 ) {
+ unsigned int i = 0;
+ char* p = buf + 5;
+
+ for ( i = 0; i < NumOfInts; i++ ) {
+ unsigned long val;
+
+ sscanf( p, "%lu", &val );
+ Intr[ i ] = val - OldIntr[ i ];
+ OldIntr[ i ] = val;
+ while ( *p && *p != ' ' )
+ p++;
+ while ( *p && *p == ' ' )
+ p++;
+ }
+ } else if ( strcmp( "ctxt", tag ) == 0 ) {
+ unsigned long val;
+
+ sscanf( buf + 5, "%lu", &val );
+ Ctxt = val - OldCtxt;
+ OldCtxt = val;
+ }
+ }
+
+ /* Read Linux 2.5.x /proc/vmstat */
+ while ( sscanf( vmstatBufP, format, buf ) == 1 ) {
+ buf[ sizeof( buf ) - 1 ] = '\0';
+ vmstatBufP += strlen( buf ) + 1; /* move vmstatBufP to next line */
+ sscanf( buf, tagFormat, tag );
+
+ if ( strcmp( "pgpgin", tag ) == 0 ) {
+ unsigned long v1;
+ sscanf( buf + 7, "%lu", &v1 );
+ PageIn = v1 - OldPageIn;
+ OldPageIn = v1;
+ } else if ( strcmp( "pgpgout", tag ) == 0 ) {
+ unsigned long v1;
+ sscanf( buf + 7, "%lu", &v1 );
+ PageOut = v1 - OldPageOut;
+ OldPageOut = v1;
+ }
+ }
+
+ /* Process values from /proc/diskstats (Linux >= 2.6.x) */
+ while (sscanf(iostatBufP, format, buf) == 1)
+ {
+ buf[sizeof(buf) - 1] = '\0';
+ iostatBufP += strlen(buf) + 1; /* move IOstatBufP to next line */
+
+ process26DiskIO(buf);
+ }
+
+ /* save exact time inverval between this and the last read of /proc/stat */
+ timeInterval = currSampling.tv_sec - lastSampling.tv_sec +
+ ( currSampling.tv_usec - lastSampling.tv_usec ) / 1000000.0;
+ lastSampling = currSampling;
+
+ cleanupDiskList();
+
+ Dirty = 0;
+}
+
+/*
+================================ public part =================================
+*/
+
+void initStat( struct SensorModul* sm )
+{
+ /* The CPU load is calculated from the values in /proc/stat. The cpu
+ * entry contains 4 counters. These counters count the number of ticks
+ * the system has spend on user processes, system processes, nice
+ * processes and idle time.
+ *
+ * SMP systems will have cpu1 to cpuN lines right after the cpu info. The
+ * format is identical to cpu and reports the information for each cpu.
+ * Linux kernels <= 2.0 do not provide this information!
+ *
+ * The /proc/stat file looks like this:
+ *
+ * cpu 1586 4 808 36274
+ * disk 7797 0 0 0
+ * disk_rio 6889 0 0 0
+ * disk_wio 908 0 0 0
+ * disk_rblk 13775 0 0 0
+ * disk_wblk 1816 0 0 0
+ * page 27575 1330
+ * swap 1 0
+ * intr 50444 38672 2557 0 0 0 0 2 0 2 0 0 3 1429 1 7778 0
+ * ctxt 54155
+ * btime 917379184
+ * processes 347
+ *
+ * Linux kernel >= 2.4.0 have one or more disk_io: lines instead of
+ * the disk_* lines.
+ *
+ * Linux kernel >= 2.6.x(?) have disk I/O stats in /proc/diskstats
+ * and no disk relevant lines are found in /proc/stat
+ */
+
+ char format[ 32 ];
+ char tagFormat[ 16 ];
+ char buf[ 1024 ];
+ char tag[ 32 ];
+ char* statBufP = StatBuf;
+ char* vmstatBufP = VmStatBuf;
+ char* iostatBufP = IOStatBuf;
+
+ StatSM = sm;
+
+ updateStat();
+
+ sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 );
+ sprintf( tagFormat, "%%%ds", (int)sizeof( tag ) - 1 );
+
+ while ( sscanf( statBufP, format, buf ) == 1 ) {
+ buf[ sizeof( buf ) - 1 ] = '\0';
+ statBufP += strlen( buf ) + 1; /* move statBufP to next line */
+ sscanf( buf, tagFormat, tag );
+
+ if ( strcmp( "cpu", tag ) == 0 ) {
+ /* Total CPU load */
+ registerMonitor( "cpu/user", "integer", printCPUUser, printCPUUserInfo, StatSM );
+ registerMonitor( "cpu/nice", "integer", printCPUNice, printCPUNiceInfo, StatSM );
+ registerMonitor( "cpu/sys", "integer", printCPUSys, printCPUSysInfo, StatSM );
+ registerMonitor( "cpu/idle", "integer", printCPUIdle, printCPUIdleInfo, StatSM );
+ } else if ( strncmp( "cpu", tag, 3 ) == 0 ) {
+ char cmdName[ 24 ];
+ /* Load for each SMP CPU */
+ int id;
+
+ sscanf( tag + 3, "%d", &id );
+ CPUCount++;
+ sprintf( cmdName, "cpu%d/user", id );
+ registerMonitor( cmdName, "integer", printCPUxUser, printCPUxUserInfo, StatSM );
+ sprintf( cmdName, "cpu%d/nice", id );
+ registerMonitor( cmdName, "integer", printCPUxNice, printCPUxNiceInfo, StatSM );
+ sprintf( cmdName, "cpu%d/sys", id );
+ registerMonitor( cmdName, "integer", printCPUxSys, printCPUxSysInfo, StatSM );
+ sprintf( cmdName, "cpu%d/idle", id );
+ registerMonitor( cmdName, "integer", printCPUxIdle, printCPUxIdleInfo, StatSM );
+ } else if ( strcmp( "disk", tag ) == 0 ) {
+ unsigned long val;
+ char* b = buf + 5;
+
+ /* Count the number of registered disks */
+ for ( DiskCount = 0; *b && sscanf( b, "%lu", &val ) == 1; DiskCount++ ) {
+ while ( *b && isblank( *b++ ) );
+ while ( *b && isdigit( *b++ ) );
+ }
+
+ if ( DiskCount > 0 )
+ DiskLoad = (DiskLoadInfo*)malloc( sizeof( DiskLoadInfo ) * DiskCount );
+ initStatDisk( tag, buf, "disk", "disk", 0, printDiskTotal, printDiskTotalInfo );
+ } else if ( initStatDisk( tag, buf, "disk_rio", "rio", 1, printDiskRIO,
+ printDiskRIOInfo ) );
+ else if ( initStatDisk( tag, buf, "disk_wio", "wio", 2, printDiskWIO,
+ printDiskWIOInfo ) );
+ else if ( initStatDisk( tag, buf, "disk_rblk", "rblk", 3, printDiskRBlk,
+ printDiskRBlkInfo ) );
+ else if ( initStatDisk( tag, buf, "disk_wblk", "wblk", 4, printDiskWBlk,
+ printDiskWBlkInfo ) );
+ else if ( strcmp( "disk_io:", tag ) == 0 )
+ processDiskIO( buf );
+ else if ( strcmp( "page", tag ) == 0 ) {
+ sscanf( buf + 5, "%lu %lu", &OldPageIn, &OldPageOut );
+ registerMonitor( "cpu/pageIn", "integer", printPageIn,
+ printPageInInfo, StatSM );
+ registerMonitor( "cpu/pageOut", "integer", printPageOut,
+ printPageOutInfo, StatSM );
+ } else if ( strcmp( "intr", tag ) == 0 ) {
+ unsigned int i;
+ char cmdName[ 32 ];
+ char* p = buf + 5;
+
+ /* Count the number of listed values in the intr line. */
+ NumOfInts = 0;
+ while ( *p )
+ if ( *p++ == ' ' )
+ NumOfInts++;
+
+ /* It looks like anything above 24 is always 0. So let's just
+ * ignore this for the time being. */
+ if ( NumOfInts > 25 )
+ NumOfInts = 25;
+ OldIntr = (unsigned long*)malloc( NumOfInts * sizeof( unsigned long ) );
+ Intr = (unsigned long*)malloc( NumOfInts * sizeof( unsigned long ) );
+ i = 0;
+ p = buf + 5;
+ for ( i = 0; p && i < NumOfInts; i++ ) {
+ sscanf( p, "%lu", &OldIntr[ i ] );
+ while ( *p && *p != ' ' )
+ p++;
+ while ( *p && *p == ' ' )
+ p++;
+ sprintf( cmdName, "cpu/interrupts/int%02d", i );
+ registerMonitor( cmdName, "integer", printInterruptx,
+ printInterruptxInfo, StatSM );
+ }
+ } else if ( strcmp( "ctxt", tag ) == 0 ) {
+ sscanf( buf + 5, "%lu", &OldCtxt );
+ registerMonitor( "cpu/context", "integer", printCtxt,
+ printCtxtInfo, StatSM );
+ }
+ }
+
+ while ( sscanf( vmstatBufP, format, buf ) == 1 ) {
+ buf[ sizeof( buf ) - 1 ] = '\0';
+ vmstatBufP += strlen( buf ) + 1; /* move vmstatBufP to next line */
+ sscanf( buf, tagFormat, tag );
+
+ if ( strcmp( "pgpgin", tag ) == 0 ) {
+ sscanf( buf + 7, "%lu", &OldPageIn );
+ registerMonitor( "cpu/pageIn", "integer", printPageIn,
+ printPageInInfo, StatSM );
+ } else if ( strcmp( "pgpgout", tag ) == 0 ) {
+ sscanf( buf + 7, "%lu", &OldPageOut );
+ registerMonitor( "cpu/pageOut", "integer", printPageOut,
+ printPageOutInfo, StatSM );
+ }
+ }
+
+ /* Process values from /proc/diskstats (Linux >= 2.6.x) */
+ while (sscanf(iostatBufP, format, buf) == 1)
+ {
+ buf[sizeof(buf) - 1] = '\0';
+ iostatBufP += strlen(buf) + 1; /* move IOstatBufP to next line */
+
+ process26DiskIO(buf);
+ }
+
+ if ( CPUCount > 0 )
+ SMPLoad = (CPULoadInfo*)malloc( sizeof( CPULoadInfo ) * CPUCount );
+
+ /* Call processStat to eliminate initial peek values. */
+ processStat();
+}
+
+void exitStat( void )
+{
+ free( DiskLoad );
+ DiskLoad = 0;
+
+ free( SMPLoad );
+ SMPLoad = 0;
+
+ free( OldIntr );
+ OldIntr = 0;
+
+ free( Intr );
+ Intr = 0;
+}
+
+int updateStat( void )
+{
+ size_t n;
+ int fd;
+
+ if ( ( fd = open( "/proc/stat", O_RDONLY ) ) < 0 ) {
+ print_error( "Cannot open file \'/proc/stat\'!\n"
+ "The kernel needs to be compiled with support\n"
+ "for /proc filesystem enabled!\n" );
+ return -1;
+ }
+
+ if ( ( n = read( fd, StatBuf, STATBUFSIZE - 1 ) ) == STATBUFSIZE - 1 ) {
+ log_error( "Internal buffer too small to read \'/proc/stat\'" );
+
+ close( fd );
+ return -1;
+ }
+
+ gettimeofday( &currSampling, 0 );
+ close( fd );
+ StatBuf[ n ] = '\0';
+ Dirty = 1;
+
+ VmStatBuf[ 0 ] = '\0';
+ if ( ( fd = open( "/proc/vmstat", O_RDONLY ) ) < 0 )
+ return 0; /* failure is okay, only exists for Linux >= 2.5.x */
+
+ if ( ( n = read( fd, VmStatBuf, STATBUFSIZE - 1 ) ) == STATBUFSIZE - 1 ) {
+ log_error( "Internal buffer too small to read \'/proc/vmstat\'" );
+
+ close( fd );
+ return -1;
+ }
+
+ close( fd );
+ VmStatBuf[ n ] = '\0';
+
+ /* Linux >= 2.6.x has disk I/O stats in /proc/diskstats */
+ IOStatBuf[ 0 ] = '\0';
+ if ( ( fd = open( "/proc/diskstats", O_RDONLY ) ) < 0 )
+ return 0; /* failure is okay, only exists for Linux >= 2.6.x */
+
+ if ( ( n = read( fd, IOStatBuf, STATBUFSIZE - 1 ) ) == STATBUFSIZE - 1 ) {
+ log_error( "Internal buffer too small to read \'/proc/diskstats\'" );
+
+ close( fd );
+ return -1;
+ }
+
+ close( fd );
+ IOStatBuf[ n ] = '\0';
+
+ return 0;
+}
+
+void printCPUUser( const char* cmd )
+{
+ (void)cmd;
+
+ if ( Dirty )
+ processStat();
+
+ fprintf( CurrentClient, "%d\n", CPULoad.userLoad );
+}
+
+void printCPUUserInfo( const char* cmd )
+{
+ (void)cmd;
+ fprintf( CurrentClient, "CPU User Load\t0\t100\t%%\n" );
+}
+
+void printCPUNice( const char* cmd )
+{
+ (void)cmd;
+
+ if ( Dirty )
+ processStat();
+
+ fprintf( CurrentClient, "%d\n", CPULoad.niceLoad );
+}
+
+void printCPUNiceInfo( const char* cmd )
+{
+ (void)cmd;
+ fprintf( CurrentClient, "CPU Nice Load\t0\t100\t%%\n" );
+}
+
+void printCPUSys( const char* cmd )
+{
+ (void)cmd;
+
+ if ( Dirty )
+ processStat();
+
+ fprintf( CurrentClient, "%d\n", CPULoad.sysLoad );
+}
+
+void printCPUSysInfo( const char* cmd )
+{
+ (void)cmd;
+ fprintf( CurrentClient, "CPU System Load\t0\t100\t%%\n" );
+}
+
+void printCPUIdle( const char* cmd )
+{
+ (void)cmd;
+
+ if ( Dirty )
+ processStat();
+
+ fprintf( CurrentClient, "%d\n", CPULoad.idleLoad );
+}
+
+void printCPUIdleInfo( const char* cmd )
+{
+ (void)cmd;
+ fprintf( CurrentClient, "CPU Idle Load\t0\t100\t%%\n" );
+}
+
+void printCPUxUser( const char* cmd )
+{
+ int id;
+
+ if ( Dirty )
+ processStat();
+
+ sscanf( cmd + 3, "%d", &id );
+ fprintf( CurrentClient, "%d\n", SMPLoad[ id ].userLoad );
+}
+
+void printCPUxUserInfo( const char* cmd )
+{
+ int id;
+
+ sscanf( cmd + 3, "%d", &id );
+ fprintf( CurrentClient, "CPU%d User Load\t0\t100\t%%\n", id );
+}
+
+void printCPUxNice( const char* cmd )
+{
+ int id;
+
+ if ( Dirty )
+ processStat();
+
+ sscanf( cmd + 3, "%d", &id );
+ fprintf( CurrentClient, "%d\n", SMPLoad[ id ].niceLoad );
+}
+
+void printCPUxNiceInfo( const char* cmd )
+{
+ int id;
+
+ sscanf( cmd + 3, "%d", &id );
+ fprintf( CurrentClient, "CPU%d Nice Load\t0\t100\t%%\n", id );
+}
+
+void printCPUxSys( const char* cmd )
+{
+ int id;
+
+ if ( Dirty )
+ processStat();
+
+ sscanf( cmd + 3, "%d", &id );
+ fprintf( CurrentClient, "%d\n", SMPLoad[ id ].sysLoad );
+}
+
+void printCPUxSysInfo( const char* cmd )
+{
+ int id;
+
+ sscanf( cmd + 3, "%d", &id );
+ fprintf( CurrentClient, "CPU%d System Load\t0\t100\t%%\n", id );
+}
+
+void printCPUxIdle( const char* cmd )
+{
+ int id;
+
+ if ( Dirty )
+ processStat();
+
+ sscanf( cmd + 3, "%d", &id );
+ fprintf( CurrentClient, "%d\n", SMPLoad[ id ].idleLoad );
+}
+
+void printCPUxIdleInfo( const char* cmd )
+{
+ int id;
+
+ sscanf( cmd + 3, "%d", &id );
+ fprintf( CurrentClient, "CPU%d Idle Load\t0\t100\t%%\n", id );
+}
+
+void printDiskTotal( const char* cmd )
+{
+ int id;
+
+ if ( Dirty )
+ processStat();
+
+ sscanf( cmd + 9, "%d", &id );
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( DiskLoad[ id ].s[ 0 ].delta
+ / timeInterval ) );
+}
+
+void printDiskTotalInfo( const char* cmd )
+{
+ int id;
+
+ sscanf( cmd + 9, "%d", &id );
+ fprintf( CurrentClient, "Disk%d Total Load\t0\t0\tkBytes/s\n", id );
+}
+
+void printDiskRIO( const char* cmd )
+{
+ int id;
+
+ if ( Dirty )
+ processStat();
+
+ sscanf( cmd + 9, "%d", &id );
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( DiskLoad[ id ].s[ 1 ].delta
+ / timeInterval ) );
+}
+
+void printDiskRIOInfo( const char* cmd )
+{
+ int id;
+
+ sscanf( cmd + 9, "%d", &id );
+ fprintf( CurrentClient, "Disk%d Read\t0\t0\tkBytes/s\n", id );
+}
+
+void printDiskWIO( const char* cmd )
+{
+ int id;
+
+ if ( Dirty )
+ processStat();
+
+ sscanf( cmd + 9, "%d", &id );
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( DiskLoad[ id ].s[ 2 ].delta
+ / timeInterval ) );
+}
+
+void printDiskWIOInfo( const char* cmd )
+{
+ int id;
+
+ sscanf( cmd + 9, "%d", &id );
+ fprintf( CurrentClient, "Disk%d Write\t0\t0\tkBytes/s\n", id );
+}
+
+void printDiskRBlk( const char* cmd )
+{
+ int id;
+
+ if ( Dirty )
+ processStat();
+
+ sscanf( cmd + 9, "%d", &id );
+ /* a block is 512 bytes or 1/2 kBytes */
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( DiskLoad[ id ].s[ 3 ].delta
+ / timeInterval * 2 ) );
+}
+
+void printDiskRBlkInfo( const char* cmd )
+{
+ int id;
+
+ sscanf( cmd + 9, "%d", &id );
+ fprintf( CurrentClient, "Disk%d Read Data\t0\t0\tkBytes/s\n", id );
+}
+
+void printDiskWBlk( const char* cmd )
+{
+ int id;
+
+ if ( Dirty )
+ processStat();
+
+ sscanf( cmd + 9, "%d", &id );
+ /* a block is 512 bytes or 1/2 kBytes */
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( DiskLoad[ id ].s[ 4 ].delta
+ / timeInterval * 2 ) );
+}
+
+void printDiskWBlkInfo( const char* cmd )
+{
+ int id;
+
+ sscanf( cmd + 9, "%d", &id );
+ fprintf( CurrentClient, "Disk%d Write Data\t0\t0\tkBytes/s\n", id );
+}
+
+void printPageIn( const char* cmd )
+{
+ (void)cmd;
+
+ if ( Dirty )
+ processStat();
+
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( PageIn / timeInterval ) );
+}
+
+void printPageInInfo( const char* cmd )
+{
+ (void)cmd;
+ fprintf( CurrentClient, "Paged in Pages\t0\t0\t1/s\n" );
+}
+
+void printPageOut( const char* cmd )
+{
+ (void)cmd;
+
+ if ( Dirty )
+ processStat();
+
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( PageOut / timeInterval ) );
+}
+
+void printPageOutInfo( const char* cmd )
+{
+ (void)cmd;
+ fprintf( CurrentClient, "Paged out Pages\t0\t0\t1/s\n" );
+}
+
+void printInterruptx( const char* cmd )
+{
+ int id;
+
+ if ( Dirty )
+ processStat();
+
+ sscanf( cmd + strlen( "cpu/interrupts/int" ), "%d", &id );
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( Intr[ id ] / timeInterval ) );
+}
+
+void printInterruptxInfo( const char* cmd )
+{
+ int id;
+
+ sscanf( cmd + strlen( "cpu/interrupt/int" ), "%d", &id );
+ fprintf( CurrentClient, "Interrupt %d\t0\t0\t1/s\n", id );
+}
+
+void printCtxt( const char* cmd )
+{
+ (void)cmd;
+
+ if ( Dirty )
+ processStat();
+
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( Ctxt / timeInterval ) );
+}
+
+void printCtxtInfo( const char* cmd )
+{
+ (void)cmd;
+ fprintf( CurrentClient, "Context switches\t0\t0\t1/s\n" );
+}
+
+void printDiskIO( const char* cmd )
+{
+ int major, minor;
+ char name[ 17 ];
+ DiskIOInfo* ptr;
+
+ sscanf( cmd, "disk/%d:%d/%16s", &major, &minor, name );
+
+ if ( Dirty )
+ processStat();
+
+ ptr = DiskIO;
+ while ( ptr && ( ptr->major != major || ptr->minor != minor ) )
+ ptr = ptr->next;
+
+ if ( !ptr ) {
+ print_error( "RECONFIGURE" );
+ fprintf( CurrentClient, "0\n" );
+
+ log_error( "Disk device disappeared" );
+ return;
+ }
+
+ if ( strcmp( name, "total" ) == 0 )
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( ptr->total.delta
+ / timeInterval ) );
+ else if ( strcmp( name, "rio" ) == 0 )
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( ptr->rio.delta
+ / timeInterval ) );
+ else if ( strcmp( name, "wio" ) == 0 )
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( ptr->wio.delta
+ / timeInterval ) );
+ else if ( strcmp( name, "rblk" ) == 0 )
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( ptr->rblk.delta
+ / ( timeInterval * 2 ) ) );
+ else if ( strcmp( name, "wblk" ) == 0 )
+ fprintf( CurrentClient, "%lu\n", (unsigned long)( ptr->wblk.delta
+ / ( timeInterval * 2 ) ) );
+ else {
+ fprintf( CurrentClient, "0\n" );
+ log_error( "Unknown disk device property \'%s\'", name );
+ }
+}
+
+void printDiskIOInfo( const char* cmd )
+{
+ int major, minor;
+ char name[ 17 ];
+ DiskIOInfo* ptr = DiskIO;
+
+ sscanf( cmd, "disk/%d:%d/%16s", &major, &minor, name );
+
+ while ( ptr && ( ptr->major != major || ptr->minor != minor ) )
+ ptr = ptr->next;
+
+ if ( !ptr ) {
+ /* Disk device has disappeared. Print a dummy answer. */
+ fprintf( CurrentClient, "Dummy\t0\t0\t\n" );
+ return;
+ }
+
+ /* remove trailing '?' */
+ name[ strlen( name ) - 1 ] = '\0';
+
+ if ( strcmp( name, "total" ) == 0 )
+ fprintf( CurrentClient, "Total accesses device %d, %d\t0\t0\t1/s\n",
+ major, minor );
+ else if ( strcmp( name, "rio" ) == 0 )
+ fprintf( CurrentClient, "Read data device %d, %d\t0\t0\t1/s\n",
+ major, minor );
+ else if ( strcmp( name, "wio" ) == 0 )
+ fprintf( CurrentClient, "Write data device %d, %d\t0\t0\t1/s\n",
+ major, minor );
+ else if ( strcmp( name, "rblk" ) == 0 )
+ fprintf( CurrentClient, "Read accesses device %d, %d\t0\t0\tkBytes/s\n",
+ major, minor );
+ else if ( strcmp( name, "wblk" ) == 0 )
+ fprintf( CurrentClient, "Write accesses device %d, %d\t0\t0\tkBytes/s\n",
+ major, minor );
+ else {
+ fprintf( CurrentClient, "Dummy\t0\t0\t\n" );
+ log_error( "Request for unknown device property \'%s\'", name );
+ }
+}