diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 4aed2c8219774f5d797760606b8489a92ddc5163 (patch) | |
tree | 3f8c130f7d269626bf6a9447407ef6c35954426a /ksysguard/ksysguardd/Linux | |
download | tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.tar.gz tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'ksysguard/ksysguardd/Linux')
27 files changed, 5181 insertions, 0 deletions
diff --git a/ksysguard/ksysguardd/Linux/Makefile.am b/ksysguard/ksysguardd/Linux/Makefile.am new file mode 100644 index 000000000..928b4b66a --- /dev/null +++ b/ksysguard/ksysguardd/Linux/Makefile.am @@ -0,0 +1,14 @@ +AM_CPPFLAGS = -D_GNU_SOURCE +AM_CFLAGS = -Wall + +# add all supported modules +if supports_i8k +KSGRD_SUPPORTS = -DHAVE_I8K_SUPPORT +endif + + +INCLUDES = -I$(srcdir)/../../CContLib -I$(srcdir)/.. $(KSGRD_SUPPORTS) $(all_includes) + +noinst_LIBRARIES = libksysguardd.a +libksysguardd_a_SOURCES = ProcessList.c Memory.c stat.c netdev.c apm.c acpi.c \ + loadavg.c cpuinfo.c lmsensors.c netstat.c diskstat.c logfile.c i8k.c diff --git a/ksysguard/ksysguardd/Linux/Memory.c b/ksysguard/ksysguardd/Linux/Memory.c new file mode 100644 index 000000000..93c8d9edb --- /dev/null +++ b/ksysguard/ksysguardd/Linux/Memory.c @@ -0,0 +1,293 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 1999 - 2001 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 <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "Command.h" +#include "ksysguardd.h" + +#include "Memory.h" + +#define MEMINFOBUFSIZE (2 * 1024) + +static char MemInfoBuf[ MEMINFOBUFSIZE ]; +static int Dirty = 1; + +static unsigned long Total = 0; +static unsigned long MFree = 0; +static unsigned long Appl = 0; +static unsigned long Used = 0; +static unsigned long Buffers = 0; +static unsigned long Cached = 0; +static unsigned long STotal = 0; +static unsigned long SFree = 0; +static unsigned long SUsed = 0; + +static void scan_one( const char* buff, const char *key, unsigned long int* val ) +{ + int o; + char *b = strstr( buff, key ); + if ( b ) + o = sscanf( b + strlen( key ), ": %lu", val ); +} + +static void processMemInfo() +{ + scan_one( MemInfoBuf, "MemTotal", &Total ); + scan_one( MemInfoBuf, "MemFree", &MFree ); + scan_one( MemInfoBuf, "Buffers", &Buffers ); + scan_one( MemInfoBuf, "Cached", &Cached ); + scan_one( MemInfoBuf, "SwapTotal", &STotal ); + scan_one( MemInfoBuf, "SwapFree", &SFree ); + Used = Total - MFree; + Appl = ( Used - ( Buffers + Cached ) ); + + if ( STotal == 0 ) /* no swap activated */ + SUsed = 0; + else + SUsed = STotal - SFree; + + Dirty = 0; +} + +/* +================================ public part ================================= +*/ + +void initMemory( struct SensorModul* sm ) +{ + /** + Make sure that /proc/meminfo exists and is readable. If not we do + not register any monitors for memory. + */ + if ( updateMemory() < 0 ) + return; + + registerMonitor( "mem/physical/free", "integer", printMFree, printMFreeInfo, sm ); + registerMonitor( "mem/physical/used", "integer", printUsed, printUsedInfo, sm ); + registerMonitor( "mem/physical/application", "integer", printAppl, printApplInfo, sm ); + registerMonitor( "mem/physical/buf", "integer", printBuffers, printBuffersInfo, sm ); + registerMonitor( "mem/physical/cached", "integer", printCached, printCachedInfo, sm ); + registerMonitor( "mem/swap/used", "integer", printSwapUsed, printSwapUsedInfo, sm ); + registerMonitor( "mem/swap/free", "integer", printSwapFree, printSwapFreeInfo, sm ); +} + +void exitMemory( void ) +{ +} + +int updateMemory( void ) +{ + /** + The amount of total and used memory is read from the /proc/meminfo. + It also contains the information about the swap space. + The 'file' looks like this: + + MemTotal: 516560 kB + MemFree: 7812 kB + MemShared: 0 kB + Buffers: 80312 kB + Cached: 236432 kB + SwapCached: 468 kB + Active: 291992 kB + Inactive: 133556 kB + HighTotal: 0 kB + HighFree: 0 kB + LowTotal: 516560 kB + LowFree: 7812 kB + SwapTotal: 899632 kB + SwapFree: 898932 kB + Dirty: 2736 kB + Writeback: 0 kB + Mapped: 155996 kB + Slab: 73920 kB + Committed_AS: 315588 kB + PageTables: 1764 kB + ReverseMaps: 103458 + */ + + int fd; + size_t n; + + if ( ( fd = open( "/proc/meminfo", O_RDONLY ) ) < 0 ) { + print_error( "Cannot open \'/proc/meminfo\'!\n" + "The kernel needs to be compiled with support\n" + "for /proc filesystem enabled!\n" ); + return -1; + } + + if ( ( n = read( fd, MemInfoBuf, MEMINFOBUFSIZE - 1 ) ) == MEMINFOBUFSIZE - 1 ) { + log_error( "Internal buffer too small to read \'/proc/mem\'" ); + close( fd ); + return -1; + } + + close( fd ); + MemInfoBuf[ n ] = '\0'; + Dirty = 1; + + return 0; +} + +void printMFree( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "%ld\n", MFree ); +} + +void printMFreeInfo( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "Free Memory\t0\t%ld\tKB\n", Total ); +} + +void printUsed( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "%ld\n", Used ); +} + +void printUsedInfo( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "Used Memory\t0\t%ld\tKB\n", Total ); +} + +void printAppl( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "%ld\n", Appl ); +} + +void printApplInfo( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "Application Memory\t0\t%ld\tKB\n", Total ); +} + +void printBuffers( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "%ld\n", Buffers ); +} + +void printBuffersInfo( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "Buffer Memory\t0\t%ld\tKB\n", Total ); +} + +void printCached( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "%ld\n", Cached ); +} + +void printCachedInfo( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "Cached Memory\t0\t%ld\tKB\n", Total ); +} + +void printSwapUsed( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "%ld\n", SUsed ); +} + +void printSwapUsedInfo( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "Used Swap Memory\t0\t%ld\tKB\n", STotal ); +} + +void printSwapFree( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "%ld\n", SFree ); +} + +void printSwapFreeInfo( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processMemInfo(); + + fprintf( CurrentClient, "Free Swap Memory\t0\t%ld\tKB\n", STotal ); +} diff --git a/ksysguard/ksysguardd/Linux/Memory.h b/ksysguard/ksysguardd/Linux/Memory.h new file mode 100644 index 000000000..2dbd6f2dc --- /dev/null +++ b/ksysguard/ksysguardd/Linux/Memory.h @@ -0,0 +1,45 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 1999 - 2001 Chris Schlaeger <cs@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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. + +*/ + +#ifndef KSG_MEMORY_H +#define KSG_MEMORY_H + +void initMemory( struct SensorModul* ); +void exitMemory( void ); + +int updateMemory( void ); + +void printMFree( const char* ); +void printMFreeInfo( const char* ); +void printUsed( const char* ); +void printUsedInfo( const char* ); +void printAppl( const char* ); +void printApplInfo( const char* ); +void printBuffers( const char* ); +void printBuffersInfo( const char* ); +void printCached( const char* ); +void printCachedInfo( const char* ); +void printSwapUsed( const char* ); +void printSwapUsedInfo( const char* ); +void printSwapFree( const char* ); +void printSwapFreeInfo( const char* ); + +#endif diff --git a/ksysguard/ksysguardd/Linux/ProcessList.c b/ksysguard/ksysguardd/Linux/ProcessList.c new file mode 100644 index 000000000..b267c7005 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/ProcessList.c @@ -0,0 +1,554 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 1999 - 2001 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 <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <time.h> +#include <unistd.h> + +#include "../../gui/SignalIDs.h" +#include "Command.h" +#include "PWUIDCache.h" +#include "ccont.h" +#include "ksysguardd.h" + +#include "ProcessList.h" + +#define BUFSIZE 1024 +#define TAGSIZE 32 +#define KDEINITLEN strlen( "kdeinit: " ) + +static CONTAINER ProcessList = 0; + +typedef struct { + /** + This flag is set for all found processes at the beginning of the + process list update. Processes that do not have this flag set will + be assumed dead and removed from the list. The flag is cleared after + each list update. + */ + int alive; + + /* The process ID */ + pid_t pid; + + /* The parent process ID */ + pid_t ppid; + + /* The real user ID */ + uid_t uid; + + /* The real group ID */ + gid_t gid; + + /* A character description of the process status */ + char status[ 16 ]; + + /* The number of the tty the process owns */ + int ttyNo; + + /** + The nice level. The range should be -20 to 20. I'm not sure + whether this is true for all platforms. + */ + int niceLevel; + + /* The scheduling priority. */ + int priority; + + /** + The total amount of memory the process uses. This includes shared and + swapped memory. + */ + unsigned int vmSize; + + /* The amount of physical memory the process currently uses. */ + unsigned int vmRss; + + /** + The number of 1/100 of a second the process has spend in user space. + If a machine has an uptime of 1 1/2 years or longer this is not a + good idea. I never thought that the stability of UNIX could get me + into trouble! ;) + */ + unsigned int userTime; + + /** + The number of 1/100 of a second the process has spend in system space. + If a machine has an uptime of 1 1/2 years or longer this is not a + good idea. I never thought that the stability of UNIX could get me + into trouble! ;) + */ + unsigned int sysTime; + + /* The system time as multime of 100ms */ + int centStamp; + + /* The current CPU load (in %) from user space */ + double userLoad; + + /* The current CPU load (in %) from system space */ + double sysLoad; + + /* The name of the process */ + char name[ 64 ]; + + /* The command used to start the process */ + char cmdline[ 256 ]; + + /* The login name of the user that owns this process */ + char userName[ 32 ]; +} ProcessInfo; + +static unsigned ProcessCount; + +static void validateStr( char* str ) +{ + char* s = str; + + /* All characters that could screw up the communication will be removed. */ + while ( *s ) { + if ( *s == '\t' || *s == '\n' || *s == '\r' ) + *s = ' '; + ++s; + } + + /* Make sure that string contains at least one character (blank). */ + if ( str[ 0 ] == '\0' ) + strcpy( str, " " ); +} + +static int processCmp( void* p1, void* p2 ) +{ + return ( ((ProcessInfo*)p1)->pid - ((ProcessInfo*)p2)->pid ); +} + +static ProcessInfo* findProcessInList( int pid ) +{ + ProcessInfo key; + long idx; + + key.pid = pid; + if ( ( idx = search_ctnr( ProcessList, processCmp, &key ) ) < 0 ) + return 0; + + return get_ctnr( ProcessList, idx ); +} + +static int updateProcess( int pid ) +{ + ProcessInfo* ps; + FILE* fd; + char buf[ BUFSIZE ]; + char tag[ TAGSIZE ]; + char format[ 32 ]; + char tagformat[ 32 ]; + int userTime, sysTime; + const char* uName; + char status; + + if ( ( ps = findProcessInList( pid ) ) == 0 ) { + struct timeval tv; + + ps = (ProcessInfo*)malloc( sizeof( ProcessInfo ) ); + ps->pid = pid; + ps->alive = 0; + + gettimeofday( &tv, 0 ); + ps->centStamp = tv.tv_sec * 100 + tv.tv_usec / 10000; + + push_ctnr( ProcessList, ps ); + bsort_ctnr( ProcessList, processCmp ); + } + + snprintf( buf, BUFSIZE - 1, "/proc/%d/status", pid ); + if ( ( fd = fopen( buf, "r" ) ) == 0 ) { + /* process has terminated in the mean time */ + return -1; + } + + sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 ); + sprintf( tagformat, "%%%ds", (int)sizeof( tag ) - 1 ); + for ( ;; ) { + if ( fscanf( fd, format, buf ) != 1 ) + break; + buf[ sizeof( buf ) - 1 ] = '\0'; + sscanf( buf, tagformat, tag ); + tag[ sizeof( tag ) - 1 ] = '\0'; + if ( strcmp( tag, "Name:" ) == 0 ) { + sscanf( buf, "%*s %63s", ps->name ); + validateStr( ps->name ); + } else if ( strcmp( tag, "Uid:" ) == 0 ) + sscanf( buf, "%*s %d %*d %*d %*d", (int*)&ps->uid ); + } + + if ( fclose( fd ) ) + return -1; + + snprintf( buf, BUFSIZE - 1, "/proc/%d/stat", pid ); + buf[ BUFSIZE - 1 ] = '\0'; + if ( ( fd = fopen( buf, "r" ) ) == 0 ) + return -1; + + if ( fscanf( fd, "%*d %*s %c %d %d %*d %d %*d %*u %*u %*u %*u %*u %d %d" + "%*d %*d %*d %d %*u %*u %*d %u %u", + &status, (int*)&ps->ppid, (int*)&ps->gid, &ps->ttyNo, + &userTime, &sysTime, &ps->niceLevel, &ps->vmSize, + &ps->vmRss) != 9 ) { + fclose( fd ); + return -1; + } + + if ( fclose( fd ) ) + return -1; + + /* status decoding as taken from fs/proc/array.c */ + if ( status == 'R' ) + strcpy( ps->status, "running" ); + else if ( status == 'S' ) + strcpy( ps->status, "sleeping" ); + else if ( status == 'D' ) + strcpy( ps->status, "disk sleep" ); + else if ( status == 'Z' ) + strcpy( ps->status, "zombie" ); + else if ( status == 'T' ) + strcpy( ps->status, "stopped" ); + else if ( status == 'W' ) + strcpy( ps->status, "paging" ); + else + sprintf( ps->status, "Unknown: %c", status ); + + ps->vmRss = ( ps->vmRss + 3 ) * sysconf(_SC_PAGESIZE); + + { + int newCentStamp; + int timeDiff, userDiff, sysDiff; + struct timeval tv; + + gettimeofday( &tv, 0 ); + newCentStamp = tv.tv_sec * 100 + tv.tv_usec / 10000; + + timeDiff = newCentStamp - ps->centStamp; + userDiff = userTime - ps->userTime; + sysDiff = sysTime - ps->sysTime; + + if ( ( timeDiff > 0 ) && ( userDiff >= 0 ) && ( sysDiff >= 0 ) ) { + ps->userLoad = ( (double)userDiff / timeDiff ) * 100.0; + ps->sysLoad = ( (double)sysDiff / timeDiff ) * 100.0; + /** + During startup we get bigger loads since the time diff + cannot be correct. So we force it to 0. + */ + if ( ps->userLoad > 100.0 ) + ps->userLoad = 0.0; + if ( ps->sysLoad > 100.0 ) + ps->sysLoad = 0.0; + } else + ps->sysLoad = ps->userLoad = 0.0; + + ps->centStamp = newCentStamp; + ps->userTime = userTime; + ps->sysTime = sysTime; + } + + snprintf( buf, BUFSIZE - 1, "/proc/%d/cmdline", pid ); + if ( ( fd = fopen( buf, "r" ) ) == 0 ) + return -1; + + ps->cmdline[ 0 ] = '\0'; + sprintf( buf, "%%%d[^\n]", (int)sizeof( ps->cmdline ) - 1 ); + fscanf( fd, buf, ps->cmdline ); + ps->cmdline[ sizeof( ps->cmdline ) - 1 ] = '\0'; + validateStr( ps->cmdline ); + if ( fclose( fd ) ) + return -1; + + /* Ugly hack to "fix" program name for kdeinit launched programs. */ + if ( strcmp( ps->name, "kdeinit" ) == 0 && + strncmp( ps->cmdline, "kdeinit: ", KDEINITLEN ) == 0 && + strcmp( ps->cmdline + KDEINITLEN, "Running..." ) != 0 ) { + size_t len; + char* end = strchr( ps->cmdline + KDEINITLEN, ' ' ); + if ( end ) + len = ( end - ps->cmdline ) - KDEINITLEN; + else + len = strlen( ps->cmdline + KDEINITLEN ); + if ( len > 0 ) { + if ( len > sizeof( ps->name ) - 1 ) + len = sizeof( ps->name ) - 1; + strncpy( ps->name, ps->cmdline + KDEINITLEN, len ); + ps->name[ len ] = '\0'; + } + } + + /* find out user name with the process uid */ + uName = getCachedPWUID( ps->uid ); + strncpy( ps->userName, uName, sizeof( ps->userName ) - 1 ); + ps->userName[ sizeof( ps->userName ) - 1 ] = '\0'; + validateStr( ps->userName ); + + ps->alive = 1; + + return 0; +} + +static void cleanupProcessList( void ) +{ + ProcessInfo* ps; + + ProcessCount = 0; + /** + All processes that do not have the active flag set are assumed dead + and will be removed from the list. The alive flag is cleared. + */ + for ( ps = first_ctnr( ProcessList ); ps; ps = next_ctnr( ProcessList ) ) { + if ( ps->alive ) { + /* Process is still alive. Just clear flag. */ + ps->alive = 0; + ProcessCount++; + } else { + /** + Process has probably died. We remove it from the list and + destruct the data structure. i needs to be decremented so + that after i++ the next list element will be inspected. + */ + free( remove_ctnr( ProcessList ) ); + } + } +} + +int updateProcessList( void ) +{ + DIR* dir; + struct dirent* entry; + + /* read in current process list via the /proc filesystem entry */ + if ( ( dir = opendir( "/proc" ) ) == NULL ) { + print_error( "Cannot open directory \'/proc\'!\n" + "The kernel needs to be compiled with support\n" + "for /proc filesystem enabled!\n" ); + return 0; + } + + while ( ( entry = readdir( dir ) ) ) { + if ( isdigit( entry->d_name[ 0 ] ) ) { + int pid; + + pid = atoi( entry->d_name ); + updateProcess( pid ); + } + } + closedir( dir ); + + cleanupProcessList(); + return 0; +} + +/* +================================ public part ================================= +*/ + +void initProcessList( struct SensorModul* sm ) +{ + initPWUIDCache(); + + ProcessList = new_ctnr(); + + registerMonitor( "pscount", "integer", printProcessCount, printProcessCountInfo, sm ); + registerMonitor( "ps", "table", printProcessList, printProcessListInfo, sm ); + + if ( !RunAsDaemon ) { + registerCommand( "kill", killProcess ); + registerCommand( "setpriority", setPriority ); + } + + updateProcessList(); +} + +void exitProcessList( void ) +{ + removeMonitor( "ps" ); + removeMonitor( "pscount" ); + + if ( !RunAsDaemon ) { + removeCommand( "kill" ); + removeCommand( "setpriority" ); + } + + destr_ctnr( ProcessList, free ); + + exitPWUIDCache(); +} + +void printProcessListInfo( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "Name\tPID\tPPID\tUID\tGID\tStatus\tUser%%\tSystem%%\tNice\tVmSize" + "\tVmRss\tLogin\tCommand\n" ); + fprintf( CurrentClient, "s\td\td\td\td\tS\tf\tf\td\tD\tD\ts\ts\n" ); +} + +void printProcessList( const char* cmd ) +{ + ProcessInfo* ps; + + (void)cmd; + + for ( ps = first_ctnr( ProcessList ); ps; ps = next_ctnr( ProcessList ) ) { + fprintf( CurrentClient, "%s\t%ld\t%ld\t%ld\t%ld\t%s\t%.2f\t%.2f\t%d\t%d\t%d" + "\t%s\t%s\n", ps->name, (long)ps->pid, (long)ps->ppid, + (long)ps->uid, (long)ps->gid, ps->status, ps->userLoad, + ps->sysLoad, ps->niceLevel, ps->vmSize / 1024, ps->vmRss / 1024, + ps->userName, ps->cmdline ); + } + + fprintf( CurrentClient, "\n" ); +} + +void printProcessCount( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "%d\n", ProcessCount ); +} + +void printProcessCountInfo( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "Number of Processes\t0\t0\t\n" ); +} + +void killProcess( const char* cmd ) +{ + int sig, pid; + + sscanf( cmd, "%*s %d %d", &pid, &sig ); + switch( sig ) { + case MENU_ID_SIGABRT: + sig = SIGABRT; + break; + case MENU_ID_SIGALRM: + sig = SIGALRM; + break; + case MENU_ID_SIGCHLD: + sig = SIGCHLD; + break; + case MENU_ID_SIGCONT: + sig = SIGCONT; + break; + case MENU_ID_SIGFPE: + sig = SIGFPE; + break; + case MENU_ID_SIGHUP: + sig = SIGHUP; + break; + case MENU_ID_SIGILL: + sig = SIGILL; + break; + case MENU_ID_SIGINT: + sig = SIGINT; + break; + case MENU_ID_SIGKILL: + sig = SIGKILL; + break; + case MENU_ID_SIGPIPE: + sig = SIGPIPE; + break; + case MENU_ID_SIGQUIT: + sig = SIGQUIT; + break; + case MENU_ID_SIGSEGV: + sig = SIGSEGV; + break; + case MENU_ID_SIGSTOP: + sig = SIGSTOP; + break; + case MENU_ID_SIGTERM: + sig = SIGTERM; + break; + case MENU_ID_SIGTSTP: + sig = SIGTSTP; + break; + case MENU_ID_SIGTTIN: + sig = SIGTTIN; + break; + case MENU_ID_SIGTTOU: + sig = SIGTTOU; + break; + case MENU_ID_SIGUSR1: + sig = SIGUSR1; + break; + case MENU_ID_SIGUSR2: + sig = SIGUSR2; + break; + } + + if ( kill( (pid_t)pid, sig ) ) { + switch ( errno ) { + case EINVAL: + fprintf( CurrentClient, "4\t%d\n", pid ); + break; + case ESRCH: + fprintf( CurrentClient, "3\t%d\n", pid ); + break; + case EPERM: + if(vfork() == 0) { + exit(0);/* Won't execute unless execve fails. Need this for the parent process to continue */ + } + fprintf( CurrentClient, "2\t%d\n", pid ); + break; + default: /* unknown error */ + fprintf( CurrentClient, "1\t%d\n", pid ); + break; + } + } else + fprintf( CurrentClient, "0\t%d\n", pid ); +} + +void setPriority( const char* cmd ) +{ + int pid, prio; + + sscanf( cmd, "%*s %d %d", &pid, &prio ); + if ( setpriority( PRIO_PROCESS, pid, prio ) ) { + switch ( errno ) { + case EINVAL: + fprintf( CurrentClient, "4\n" ); + break; + case ESRCH: + fprintf( CurrentClient, "3\n" ); + break; + case EPERM: + case EACCES: + fprintf( CurrentClient, "2\n" ); + break; + default: /* unknown error */ + fprintf( CurrentClient, "1\n" ); + break; + } + } else + fprintf( CurrentClient, "0\n" ); +} diff --git a/ksysguard/ksysguardd/Linux/ProcessList.h b/ksysguard/ksysguardd/Linux/ProcessList.h new file mode 100644 index 000000000..709994a29 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/ProcessList.h @@ -0,0 +1,38 @@ +/* + 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 the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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. + +*/ + +#ifndef KSG_PROCESSLIST_H +#define KSG_PROCESSLIST_H + +void initProcessList( struct SensorModul* ); +void exitProcessList( void ); + +int updateProcessList( void ); + +void printProcessList( const char* ); +void printProcessListInfo( const char* ); +void printProcessCount( const char* ); +void printProcessCountInfo( const char* ); + +void killProcess( const char* ); +void setPriority( const char* ); + +#endif diff --git a/ksysguard/ksysguardd/Linux/acpi.c b/ksysguard/ksysguardd/Linux/acpi.c new file mode 100644 index 000000000..b3100c363 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/acpi.c @@ -0,0 +1,418 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 2003 Stephan Uhlmann <su@su2.info> + Copyright (c) 2005 Sirtaj Singh Kang <taj@kde.org> -- Battery fixes and Thermal + + 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 <dirent.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "Command.h" +#include "ksysguardd.h" + +#include "acpi.h" + +#define ACPIFILENAMELENGTHMAX 64 +#define ACPIBATTERYNUMMAX 6 +#define ACPIBATTERYINFOBUFSIZE 1024 +#define ACPIBATTERYSTATEBUFSIZE 512 + +static int AcpiBatteryNum = 0; +static char AcpiBatteryNames[ ACPIBATTERYNUMMAX ][ 8 ]; +static int AcpiBatteryCharge[ ACPIBATTERYNUMMAX ]; +static int AcpiBatteryUsage[ ACPIBATTERYNUMMAX ]; + +static int AcpiThermalZones = -1; +static int AcpiFans = -1; +/* +================================ public part ================================= +*/ + +void initAcpi(struct SensorModul* sm) +{ + initAcpiBattery(sm); + initAcpiFan(sm); + initAcpiThermal(sm); +} + +int updateAcpi( void ) +{ + if (AcpiBatteryNum > 0) updateAcpiBattery(); + if (AcpiFans > 0) updateAcpiFan(); + if (AcpiThermalZones > 0) updateAcpiThermal(); + return 0; +} + +void exitAcpi( void ) +{ + AcpiBatteryNum = -1; + AcpiFans = -1; + AcpiThermalZones = -1; +} + + +/************ ACPI Battery **********/ + +void initAcpiBattery( struct SensorModul* sm ) +{ + DIR *d; + struct dirent *de; + char s[ ACPIFILENAMELENGTHMAX ]; + + if ( ( d = opendir( "/proc/acpi/battery" ) ) == NULL ) { + AcpiBatteryNum = -1; + return; + } else { + AcpiBatteryNum = 0; + while ( ( de = readdir( d ) ) ) + if ( ( strcmp( de->d_name, "." ) != 0 ) && ( strcmp( de->d_name, ".." ) != 0 ) ) { + strncpy( AcpiBatteryNames[ AcpiBatteryNum ], de->d_name, 8 ); + snprintf( s, sizeof( s ), "acpi/battery/%d/batterycharge", AcpiBatteryNum ); + registerMonitor( s, "integer", printAcpiBatFill, printAcpiBatFillInfo, sm ); + snprintf( s, sizeof( s ), "acpi/battery/%d/batteryusage", AcpiBatteryNum ); + registerMonitor( s, "integer", printAcpiBatUsage, printAcpiBatUsageInfo, sm); + AcpiBatteryCharge[ AcpiBatteryNum ] = 0; + AcpiBatteryNum++; + } + } +} + + +int updateAcpiBattery( void ) +{ + int i, fd; + char s[ ACPIFILENAMELENGTHMAX ]; + size_t n; + char AcpiBatInfoBuf[ ACPIBATTERYINFOBUFSIZE ]; + char AcpiBatStateBuf[ ACPIBATTERYSTATEBUFSIZE ]; + char *p; + int AcpiBatCapacity = 1; + int AcpiBatRemainingCapacity = 0; + + if ( AcpiBatteryNum <= 0 ) + return -1; + + for ( i = 0; i < AcpiBatteryNum; i++ ) { + /* get total capacity */ + snprintf( s, sizeof( s ), "/proc/acpi/battery/%s/info", AcpiBatteryNames[ i ] ); + if ( ( fd = open( s, O_RDONLY ) ) < 0 ) { + print_error( "Cannot open file \'%s\'!\n" + "Load the battery ACPI kernel module or\n" + "compile it into your kernel.\n", s ); + return -1; + } + if ( ( n = read( fd, AcpiBatInfoBuf, ACPIBATTERYINFOBUFSIZE - 1 ) ) == + ACPIBATTERYINFOBUFSIZE - 1 ) { + log_error( "Internal buffer too small to read \'%s\'", s ); + close( fd ); + return -1; + } + close( fd ); + p = AcpiBatInfoBuf; + while ( ( p!= NULL ) && ( sscanf( p, "last full capacity: %d ", + &AcpiBatCapacity ) != 1 ) ) { + p = strchr( p, '\n' ); + if ( p ) + p++; + } + /* get remaining capacity */ + snprintf( s, sizeof( s ), "/proc/acpi/battery/%s/state", AcpiBatteryNames[ i ] ); + if ( ( fd = open( s, O_RDONLY ) ) < 0 ) { + print_error( "Cannot open file \'%s\'!\n" + "Load the battery ACPI kernel module or\n" + "compile it into your kernel.\n", s ); + return -1; + } + if ( ( n = read( fd, AcpiBatStateBuf, ACPIBATTERYSTATEBUFSIZE - 1 ) ) == + ACPIBATTERYSTATEBUFSIZE - 1 ) { + log_error( "Internal buffer too small to read \'%s\'", s); + close( fd ); + return -1; + } + close( fd ); + p = AcpiBatStateBuf; + while ( ( p!= NULL ) && ( sscanf( p, "remaining capacity: %d ", + &AcpiBatRemainingCapacity ) != 1 ) ) { + p = strchr( p, '\n' ); + if ( p ) + p++; + } + + /* get current battery usage, (current Current) */ + p = AcpiBatStateBuf; + while ( ( p!= NULL ) && ( sscanf( p, "present rate: %d ", + &AcpiBatteryUsage[i] ) != 1 ) ) { + p = strchr( p, '\n' ); + if ( p ) + p++; + } + + + /* calculate charge rate */ + if ( AcpiBatCapacity > 0 ) + AcpiBatteryCharge[ i ] = AcpiBatRemainingCapacity * 100 / AcpiBatCapacity; + else + AcpiBatteryCharge[ i ] = 0; + } + + return 0; +} + +void printAcpiBatFill( const char* cmd ) +{ + int i; + + sscanf( cmd + 13, "%d", &i ); + fprintf( CurrentClient, "%d\n", AcpiBatteryCharge[ i ] ); +} + +void printAcpiBatFillInfo( const char* cmd ) +{ + int i; + + sscanf( cmd + 13, "%d", &i ); + fprintf( CurrentClient, "Battery %d charge\t0\t100\t%%\n", i ); +} + +void printAcpiBatUsage( const char* cmd) +{ + int i; + + sscanf( cmd + 13, "%d", &i ); + fprintf(CurrentClient, "%d\n", AcpiBatteryUsage[ i ] ); +} + +void printAcpiBatUsageInfo( const char* cmd) +{ + + int i; + + sscanf(cmd+13, "%d", &i); + + fprintf(CurrentClient, "Battery %d usage\t0\t2500\tmA\n", i ); +} + +/************** ACPI Thermal *****************/ + +#define THERMAL_ZONE_DIR "/proc/acpi/thermal_zone" +#define TEMPERATURE_FILE "temperature" +#define TEMPERATURE_FILE_MAXLEN 255 + + +/*static char **zone_names = NULL;*/ + +/** Find the thermal zone name from the command. + * Assumes the command is of the form acpi/thermal_zone/<zone name>/... + * @p startidx is set to the start of the zone name. May be set to an + * undefined value if zone name is not found. + * @return length of found name, or 0 if nothing found. + */ +static int extract_zone_name(char **startidx, const char *cmd) +{ + char *idx = NULL; + idx = strchr(cmd, '/'); + if (idx == NULL) return 0; + idx = strchr(idx+1, '/'); + if (idx == NULL) return 0; + *startidx = idx+1; + idx = strchr(*startidx, '/'); + if (idx == NULL) return 0; + return idx - *startidx; +} + +void initAcpiThermal(struct SensorModul *sm) +{ + + char th_ref[ ACPIFILENAMELENGTHMAX ]; + DIR *d = NULL; + struct dirent *de; + + d = opendir(THERMAL_ZONE_DIR); + if (d == NULL) { +/* print_error( "Directory \'" THERMAL_ZONE_DIR + "\' does not exist or is not readable.\n" + "Load the ACPI thermal kernel module or compile it into your kernel.\n" ); +*/ + AcpiThermalZones = -1; + return; + } + + AcpiThermalZones = 0; + while ( (de = readdir(d)) != NULL ) { + if ( ( strcmp( de->d_name, "." ) == 0 ) + || ( strcmp( de->d_name, ".." ) == 0 ) ) { + continue; + } + + AcpiThermalZones++; + snprintf(th_ref, sizeof(th_ref), + "acpi/thermal_zone/%s/temperature", de->d_name); + registerMonitor(th_ref, "integer", printThermalZoneTemperature, + printThermalZoneTemperatureInfo, sm); + } + + return; +} + +int updateAcpiThermal() +{ + /* TODO: stub */ + return 0; +} + +static int getCurrentTemperature(const char *cmd) +{ + char th_file[ ACPIFILENAMELENGTHMAX ]; + char input_buf[ TEMPERATURE_FILE_MAXLEN ]; + char *zone_name = NULL; + int read_bytes = 0, fd = 0, len_zone_name = 0; + int temperature=0; + + len_zone_name = extract_zone_name(&zone_name, cmd); + if (len_zone_name <= 0) return -1; + + snprintf(th_file, sizeof(th_file), + THERMAL_ZONE_DIR "/%.*s/" TEMPERATURE_FILE, + len_zone_name, zone_name); + + fd = open(th_file, O_RDONLY); + if (fd < 0) { + print_error( "Cannot open file \'%s\'!\n" + "Load the thermal ACPI kernel module or\n" + "compile it into your kernel.\n", th_file ); + return -1; + } + + read_bytes = read( fd, input_buf, sizeof(input_buf) - 1 ); + if ( read_bytes == sizeof(input_buf) - 1 ) { + log_error( "Internal buffer too small to read \'%s\'", th_file ); + close( fd ); + return -1; + } + close(fd); + + sscanf(input_buf, "temperature: %d C", &temperature); + return temperature; +} + +void printThermalZoneTemperature(const char *cmd) { + int temperature = getCurrentTemperature(cmd); + fprintf(CurrentClient, "%d\n", temperature); +} + +void printThermalZoneTemperatureInfo(const char *cmd) +{ + fprintf(CurrentClient, "Current temperature\t0\t0\tC\n"); +} + +/********** ACPI Fan State***************/ + +#define FAN_DIR "/proc/acpi/fan" +#define FAN_STATE_FILE "state" +#define FAN_STATE_FILE_MAXLEN 255 + +void initAcpiFan(struct SensorModul *sm) +{ + + char th_ref[ ACPIFILENAMELENGTHMAX ]; + DIR *d = NULL; + struct dirent *de; + + d = opendir(FAN_DIR); + if (d == NULL) { +/* print_error( "Directory \'" THERMAL_ZONE_DIR + "\' does not exist or is not readable.\n" + "Load the ACPI thermal kernel module or compile it into your kernel.\n" ); +*/ + AcpiFans = -1; + return; + } + + AcpiFans = 0; + while ( (de = readdir(d)) != NULL ) { + if ( ( strcmp( de->d_name, "." ) == 0 ) + || ( strcmp( de->d_name, ".." ) == 0 ) ) { + continue; + } + + AcpiFans++; + snprintf(th_ref, sizeof(th_ref), + "acpi/fan/%s/state", de->d_name); + registerMonitor(th_ref, "integer", printFanState, + printFanStateInfo, sm); + } + + return; +} + +int updateAcpiFan() +{ + /* TODO: stub */ + return 0; +} + +static int getFanState(const char *cmd) +{ + char fan_state_file[ ACPIFILENAMELENGTHMAX ]; + char input_buf[ FAN_STATE_FILE_MAXLEN ]; + char *fan_name = NULL; + int read_bytes = 0, fd = 0, len_fan_name = 0; + char fan_state[4]; + + len_fan_name = extract_zone_name(&fan_name, cmd); + if (len_fan_name <= 0) return -1; + + snprintf(fan_state_file, sizeof(fan_state_file), + FAN_DIR "/%.*s/" FAN_STATE_FILE, + len_fan_name, fan_name); + + fd = open(fan_state_file, O_RDONLY); + if (fd < 0) { + print_error( "Cannot open file \'%s\'!\n" + "Load the fan ACPI kernel module or\n" + "compile it into your kernel.\n", fan_state_file ); + return -1; + } + + read_bytes = read( fd, input_buf, sizeof(input_buf) - 1 ); + if ( read_bytes == sizeof(input_buf) - 1 ) { + log_error( "Internal buffer too small to read \'%s\'", fan_state_file ); + close( fd ); + return -1; + } + close(fd); + + sscanf(input_buf, "status: %2s", fan_state); + return (fan_state[1] == 'n') ? 1 : 0; +} + +void printFanState(const char *cmd) { + int fan_state = getFanState(cmd); + fprintf(CurrentClient, "%d\n", fan_state); +} + +void printFanStateInfo(const char *cmd) +{ + fprintf(CurrentClient, "Fan status\t0\t1\tboolean\n"); +} + + diff --git a/ksysguard/ksysguardd/Linux/acpi.h b/ksysguard/ksysguardd/Linux/acpi.h new file mode 100644 index 000000000..ae01ecad1 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/acpi.h @@ -0,0 +1,45 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 2003 Stephan Uhlmann <su@su2.info> + + 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. +*/ + +#ifndef KSG_ACPI_H +#define KSG_ACPI_H + +void initAcpi( struct SensorModul* ); +void exitAcpi( void ); + +int updateAcpi( void ); + +void initAcpiBattery( struct SensorModul* ); +int updateAcpiBattery(void); +void printAcpiBatFill( const char* ); +void printAcpiBatFillInfo( const char* ); +void printAcpiBatUsage( const char* ); +void printAcpiBatUsageInfo( const char* ); + +void initAcpiThermal( struct SensorModul * ); +int updateAcpiThermal(void); +void printThermalZoneTemperature(const char *cmd); +void printThermalZoneTemperatureInfo(const char *cmd); + +void initAcpiFan( struct SensorModul * ); +int updateAcpiFan(void); +void printFanState(const char *cmd); +void printFanStateInfo(const char *cmd); + +#endif diff --git a/ksysguard/ksysguardd/Linux/apm.c b/ksysguard/ksysguardd/Linux/apm.c new file mode 100644 index 000000000..0c1d00bcb --- /dev/null +++ b/ksysguard/ksysguardd/Linux/apm.c @@ -0,0 +1,126 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 1999 - 2001 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 <fcntl.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "Command.h" +#include "ksysguardd.h" + +#include "apm.h" + +static int ApmOK = 0; +static int BatFill, BatTime; + +#define APMBUFSIZE 128 +static char ApmBuf[ APMBUFSIZE ]; +static int Dirty = 0; + +static void processApm( void ) +{ + sscanf( ApmBuf, "%*f %*f %*x %*x %*x %*x %d%% %d min", + &BatFill, &BatTime ); + Dirty = 0; +} + +/* +================================ public part ================================= +*/ + +void initApm( struct SensorModul* sm ) +{ + if ( updateApm() < 0 ) { + ApmOK = -1; + return; + } else + ApmOK = 1; + + registerMonitor( "apm/batterycharge", "integer", printApmBatFill, printApmBatFillInfo, sm ); + registerMonitor( "apm/remainingtime", "integer", printApmBatTime, printApmBatTimeInfo, sm ); +} + +void exitApm( void ) +{ + ApmOK = -1; +} + +int updateApm( void ) +{ + size_t n; + int fd; + + if ( ApmOK < 0 ) + return -1; + + if ( ( fd = open( "/proc/apm", O_RDONLY ) ) < 0 ) { + if ( ApmOK != 0 ) + print_error( "Cannot open file \'/proc/apm\'!\n" + "The kernel needs to be compiled with support\n" + "for /proc filesystem enabled!\n" ); + return -1; + } + + if ( ( n = read( fd, ApmBuf, APMBUFSIZE - 1 ) ) == APMBUFSIZE - 1 ) { + log_error( "Internal buffer too small to read \'/proc/apm\'" ); + close( fd ); + return -1; + } + + close( fd ); + ApmBuf[ n ] = '\0'; + Dirty = 1; + + return 0; +} + +void printApmBatFill( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processApm(); + + fprintf( CurrentClient, "%d\n", BatFill ); +} + +void printApmBatFillInfo( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "Battery charge\t0\t100\t%%\n" ); +} + +void printApmBatTime( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processApm(); + + fprintf( CurrentClient, "%d\n", BatTime ); +} + +void printApmBatTimeInfo( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "Remaining battery time\t0\t0\tmin\n" ); +} diff --git a/ksysguard/ksysguardd/Linux/apm.h b/ksysguard/ksysguardd/Linux/apm.h new file mode 100644 index 000000000..982c77f7c --- /dev/null +++ b/ksysguard/ksysguardd/Linux/apm.h @@ -0,0 +1,34 @@ +/* + 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. + +*/ + +#ifndef KSG_APM_H +#define KSG_APM_H + +void initApm( struct SensorModul* ); +void exitApm( void ); + +int updateApm( void ); + +void printApmBatFill( const char* ); +void printApmBatFillInfo( const char* ); +void printApmBatTime( const char* ); +void printApmBatTimeInfo( const char* ); + +#endif diff --git a/ksysguard/ksysguardd/Linux/cpuinfo.c b/ksysguard/ksysguardd/Linux/cpuinfo.c new file mode 100644 index 000000000..de5deb80f --- /dev/null +++ b/ksysguard/ksysguardd/Linux/cpuinfo.c @@ -0,0 +1,179 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 2000-2001 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 <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <time.h> + +#include "Command.h" +#include "ksysguardd.h" + +#include "cpuinfo.h" + +static int CpuInfoOK = 0; +static float* Clocks = 0; +static int CPUs = 0; + +#define CPUINFOBUFSIZE (32 * 1024) +static char CpuInfoBuf[ CPUINFOBUFSIZE ]; +static int Dirty = 0; +static struct SensorModul *CpuInfoSM; + +static void processCpuInfo( void ) +{ + char format[ 32 ]; + char tag[ 32 ]; + char value[ 256 ]; + char* cibp = CpuInfoBuf; + int cpuId = 0; + + if ( !CpuInfoOK ) + return; + + sprintf( format, "%%%d[^:]: %%%d[^\n]\n", (int)sizeof( tag ) - 1, + (int)sizeof( value ) - 1 ); + + while ( sscanf( cibp, format, tag, value ) == 2 ) { + char* p; + tag[ sizeof( tag ) - 1 ] = '\0'; + value[ sizeof( value ) - 1 ] = '\0'; + /* remove trailing whitespaces */ + p = tag + strlen( tag ) - 1; + /* remove trailing whitespaces */ + while ( ( *p == ' ' || *p == '\t' ) && p > tag ) + *p-- = '\0'; + + if ( strcmp( tag, "processor" ) == 0 ) { + if ( sscanf( value, "%d", &cpuId ) == 1 ) { + if ( cpuId >= CPUs ) { + char cmdName[ 24 ]; + if ( Clocks ) + free( Clocks ); + CPUs = cpuId + 1; + Clocks = malloc( CPUs * sizeof( float ) ); + snprintf( cmdName, sizeof( cmdName ) - 1, "cpu%d/clock", cpuId ); + registerMonitor( cmdName, "float", printCPUxClock, printCPUxClockInfo, + CpuInfoSM ); + } + } + } else if ( strcmp( tag, "cpu MHz" ) == 0 ) + sscanf( value, "%f", &Clocks[ cpuId ] ); + + /* Move cibp to begining of next line, if there is one. */ + cibp = strchr( cibp, '\n' ); + if ( cibp ) + cibp++; + else + cibp = CpuInfoBuf + strlen( CpuInfoBuf ); + } + + Dirty = 0; +} + +/* +================================ public part ================================= +*/ + +void initCpuInfo( struct SensorModul* sm ) +{ + CpuInfoSM = sm; + + if ( updateCpuInfo() < 0 ) + return; + + processCpuInfo(); +} + +void exitCpuInfo( void ) +{ + CpuInfoOK = -1; + + free( Clocks ); +} + +int updateCpuInfo( void ) +{ + size_t n; + int fd; + + if ( CpuInfoOK < 0 ) + return -1; + + if ( ( fd = open( "/proc/cpuinfo", O_RDONLY ) ) < 0 ) { + if ( CpuInfoOK != 0 ) + print_error( "Cannot open file \'/proc/cpuinfo\'!\n" + "The kernel needs to be compiled with support\n" + "for /proc filesystem enabled!\n" ); + CpuInfoOK = -1; + return -1; + } + + n = 0; + for(;;) { + ssize_t len = read( fd, CpuInfoBuf + n, CPUINFOBUFSIZE - 1 - n ); + if( len < 0 ) { + print_error( "Failed to read file \'/proc/cpuinfo\'!\n" ); + CpuInfoOK = -1; + close( fd ); + return -1; + } + n += len; + if( len == 0 ) /* reading finished */ + break; + if( n == CPUINFOBUFSIZE - 1 ) { + log_error( "Internal buffer too small to read \'/proc/cpuinfo\'" ); + CpuInfoOK = 0; + close( fd ); + return -1; + } + } + + close( fd ); + CpuInfoOK = 1; + CpuInfoBuf[ n ] = '\0'; + Dirty = 1; + + return 0; +} + +void printCPUxClock( const char* cmd ) +{ + int id; + + if ( Dirty ) + processCpuInfo(); + + sscanf( cmd + 3, "%d", &id ); + fprintf( CurrentClient, "%f\n", Clocks[ id ] ); +} + +void printCPUxClockInfo( const char* cmd ) +{ + int id; + + sscanf( cmd + 3, "%d", &id ); + fprintf( CurrentClient, "CPU%d Clock Frequency\t0\t0\tMHz\n", id ); +} diff --git a/ksysguard/ksysguardd/Linux/cpuinfo.h b/ksysguard/ksysguardd/Linux/cpuinfo.h new file mode 100644 index 000000000..f2380cce7 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/cpuinfo.h @@ -0,0 +1,32 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 2000-2001 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. + +*/ + +#ifndef KSG_CPUINFO_H +#define KSG_CPUINFO_H + +void initCpuInfo( struct SensorModul* ); +void exitCpuInfo( void ); + +int updateCpuInfo( void ); + +void printCPUxClock( const char* ); +void printCPUxClockInfo( const char* ); + +#endif diff --git a/ksysguard/ksysguardd/Linux/diskstat.c b/ksysguard/ksysguardd/Linux/diskstat.c new file mode 100644 index 000000000..012ed5a8e --- /dev/null +++ b/ksysguard/ksysguardd/Linux/diskstat.c @@ -0,0 +1,265 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 2001 Tobias Koenig <tokoe@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 <config.h> + +#include <mntent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/vfs.h> +#include <time.h> +#include <unistd.h> + +#include "Command.h" +#include "ccont.h" +#include "diskstat.h" +#include "ksysguardd.h" + +typedef struct { + char device[ 256 ]; + char mntpnt[ 256 ]; + long blocks; + long bfree; + long bused; + int bused_percent; +} DiskInfo; + +static CONTAINER DiskStatList = 0; +static struct SensorModul* DiskStatSM; +char *getMntPnt( const char* cmd ); + +char *getMntPnt( const char* cmd ) +{ + static char device[ 1025 ]; + char* ptr; + + memset( device, 0, sizeof( device ) ); + sscanf( cmd, "partitions%1024s", device ); + + ptr = (char*)rindex( device, '/' ); + *ptr = '\0'; + + return (char*)device; +} + +/* ----------------------------- public part ------------------------------- */ + +void initDiskStat( struct SensorModul* sm ) +{ + char monitor[ 1024 ]; + DiskInfo* disk_info; + + DiskStatList = new_ctnr(); + DiskStatSM = sm; + + if ( updateDiskStat() < 0 ) + return; + + registerMonitor( "partitions/list", "listview", printDiskStat, printDiskStatInfo, sm ); + + for ( disk_info = first_ctnr( DiskStatList ); disk_info; disk_info = next_ctnr( DiskStatList ) ) { + snprintf( monitor, sizeof( monitor ), "partitions%s/usedspace", disk_info->mntpnt ); + registerMonitor( monitor, "integer", printDiskStatUsed, printDiskStatUsedInfo, DiskStatSM ); + snprintf( monitor, sizeof( monitor ), "partitions%s/freespace", disk_info->mntpnt ); + registerMonitor( monitor, "integer", printDiskStatFree, printDiskStatFreeInfo, DiskStatSM ); + snprintf( monitor, sizeof( monitor ), "partitions%s/filllevel", disk_info->mntpnt ); + registerMonitor( monitor, "integer", printDiskStatPercent, printDiskStatPercentInfo, DiskStatSM ); + } +} + +void exitDiskStat( void ) +{ + char monitor[ 1024 ]; + DiskInfo* disk_info; + + removeMonitor( "partitions/list" ); + + for ( disk_info = first_ctnr( DiskStatList ); disk_info; disk_info = next_ctnr( DiskStatList ) ) { + snprintf( monitor, sizeof( monitor ), "partitions%s/usedspace", disk_info->mntpnt ); + removeMonitor( monitor ); + snprintf( monitor, sizeof( monitor ), "partitions%s/freespace", disk_info->mntpnt ); + removeMonitor( monitor ); + snprintf( monitor, sizeof( monitor ), "partitions%s/filllevel", disk_info->mntpnt ); + removeMonitor( monitor ); + } + + destr_ctnr( DiskStatList, free ); +} + +void checkDiskStat( void ) +{ + struct stat mtab_info; + static off_t mtab_size = 0; + + stat( "/etc/mtab", &mtab_info ); + if ( !mtab_size ) + mtab_size = mtab_info.st_size; + + if ( mtab_info.st_size != mtab_size ) { + exitDiskStat(); + initDiskStat( DiskStatSM ); + mtab_size = mtab_info.st_size; + } +} + +int updateDiskStat( void ) +{ + DiskInfo *disk_info; + FILE *fh; + struct mntent *mnt_info; + float percent; + int i; + struct statfs fs_info; + + if ( ( fh = setmntent( "/etc/mtab", "r" ) ) == NULL ) { + print_error( "Cannot open \'/etc/mtab\'!\n" ); + return -1; + } + + for ( i = level_ctnr( DiskStatList ); i >= 0; --i ) + free( pop_ctnr( DiskStatList ) ); + + while ( ( mnt_info = getmntent( fh ) ) != NULL ) { + if ( statfs( mnt_info->mnt_dir, &fs_info ) < 0 ) + continue; + + if ( strcmp( mnt_info->mnt_type, "proc" ) && + strcmp( mnt_info->mnt_type, "devfs" ) && + strcmp( mnt_info->mnt_type, "usbfs" ) && + strcmp( mnt_info->mnt_type, "sysfs" ) && + strcmp( mnt_info->mnt_type, "tmpfs" ) && + strcmp( mnt_info->mnt_type, "devpts" ) ) { + if ( fs_info.f_blocks != 0 ) + { + percent = ( ( (float)fs_info.f_blocks - (float)fs_info.f_bfree ) / + (float)fs_info.f_blocks ); + percent = percent * 100; + } + else + { + percent = 0; + } + + if ( ( disk_info = (DiskInfo *)malloc( sizeof( DiskInfo ) ) ) == NULL ) + continue; + + memset( disk_info, 0, sizeof( DiskInfo ) ); + strlcpy( disk_info->device, mnt_info->mnt_fsname, sizeof( disk_info->device ) ); + if ( !strcmp( mnt_info->mnt_dir, "/" ) ) + strlcpy( disk_info->mntpnt, "/root", sizeof( disk_info->mntpnt ) ); + else + strlcpy( disk_info->mntpnt, mnt_info->mnt_dir, sizeof( disk_info->mntpnt ) ); + + disk_info->blocks = fs_info.f_blocks; + disk_info->bfree = fs_info.f_bfree; + disk_info->bused = fs_info.f_blocks - fs_info.f_bfree; + disk_info->bused_percent = (int)percent; + + push_ctnr( DiskStatList, disk_info ); + } + } + + endmntent( fh ); + + return 0; +} + +void printDiskStat( const char* cmd ) +{ + DiskInfo* disk_info; + + (void)cmd; + for ( disk_info = first_ctnr( DiskStatList ); disk_info; disk_info = next_ctnr( DiskStatList ) ) { + fprintf( CurrentClient, "%s\t%ld\t%ld\t%ld\t%d\t%s\n", + disk_info->device, + disk_info->blocks, + disk_info->bused, + disk_info->bfree, + disk_info->bused_percent, + disk_info->mntpnt ); + } + + fprintf( CurrentClient, "\n" ); +} + +void printDiskStatInfo( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "Device\tBlocks\tUsed\tAvailable\tUsed %%\tMountPoint\nM\tD\tD\tD\td\ts\n" ); +} + +void printDiskStatUsed( const char* cmd ) +{ + char *mntpnt = (char*)getMntPnt( cmd ); + DiskInfo* disk_info; + + for ( disk_info = first_ctnr( DiskStatList ); disk_info; disk_info = next_ctnr( DiskStatList ) ) { + if ( !strcmp( mntpnt, disk_info->mntpnt ) ) + fprintf( CurrentClient, "%ld\n", disk_info->bused ); + } + + fprintf( CurrentClient, "\n" ); +} + +void printDiskStatUsedInfo( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "Used Blocks\t0\t-\tBlocks\n" ); +} + +void printDiskStatFree( const char* cmd ) +{ + char *mntpnt = (char*)getMntPnt( cmd ); + DiskInfo* disk_info; + + for ( disk_info = first_ctnr( DiskStatList ); disk_info; disk_info = next_ctnr( DiskStatList ) ) { + if ( !strcmp( mntpnt, disk_info->mntpnt ) ) + fprintf( CurrentClient, "%ld\n", disk_info->bfree ); + } + + fprintf( CurrentClient, "\n" ); +} + +void printDiskStatFreeInfo( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "Free Blocks\t0\t-\tBlocks\n" ); +} + +void printDiskStatPercent( const char* cmd ) +{ + char *mntpnt = (char*)getMntPnt( cmd ); + DiskInfo* disk_info; + + for ( disk_info = first_ctnr( DiskStatList ); disk_info; disk_info = next_ctnr( DiskStatList ) ) { + if ( !strcmp( mntpnt, disk_info->mntpnt ) ) + fprintf( CurrentClient, "%d\n", disk_info->bused_percent ); + } + + fprintf( CurrentClient, "\n" ); +} + +void printDiskStatPercentInfo( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "Used Blocks\t0\t100\t%%\n" ); +} diff --git a/ksysguard/ksysguardd/Linux/diskstat.h b/ksysguard/ksysguardd/Linux/diskstat.h new file mode 100644 index 000000000..6a23e6148 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/diskstat.h @@ -0,0 +1,40 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 2001 Tobias Koenig <tokoe@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. + +*/ + +#ifndef KSG_DISKSTAT_H +#define KSG_DISKSTAT_H + +void initDiskStat( struct SensorModul* ); +void exitDiskStat( void ); + +int updateDiskStat( void ); +void checkDiskStat( void ); + +void printDiskStat( const char* ); +void printDiskStatInfo( const char* ); + +void printDiskStatUsed( const char* ); +void printDiskStatUsedInfo( const char* ); +void printDiskStatFree( const char* ); +void printDiskStatFreeInfo( const char* ); +void printDiskStatPercent( const char* ); +void printDiskStatPercentInfo( const char* ); + +#endif diff --git a/ksysguard/ksysguardd/Linux/i8k.c b/ksysguard/ksysguardd/Linux/i8k.c new file mode 100644 index 000000000..c6bbe7d72 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/i8k.c @@ -0,0 +1,150 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 1999 - 2001 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 <fcntl.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "Command.h" +#include "ksysguardd.h" + +#include "i8k.h" + +#ifdef HAVE_I8K_SUPPORT + +static int I8kOK = 0; +static int cpuTemp, fan0Speed, fan1Speed; + +#define I8KBUFSIZE 128 +static char I8kBuf[ I8KBUFSIZE ]; + +/* +================================ public part ================================= +*/ + +void initI8k( struct SensorModul* sm ) +{ + if ( updateI8k() < 0 ) { + I8kOK = -1; + return; + } else + I8kOK = 1; + + registerMonitor( "dell/cputemp", "integer", printI8kCPUTemperature, + printI8kCPUTemperatureInfo, sm ); + registerMonitor( "dell/fan0", "integer", printI8kFan0Speed, + printI8kFan0SpeedInfo, sm ); + registerMonitor( "dell/fan1", "integer", printI8kFan1Speed, + printI8kFan1SpeedInfo, sm ); +} + +void exitI8k( void ) +{ + I8kOK = -1; +} + +int updateI8k( void ) +{ + size_t n; + int fd; + + if ( I8kOK < 0 ) + return -1; + + if ( ( fd = open( "/proc/i8k", O_RDONLY ) ) < 0 ) { + print_error( "Cannot open file \'/proc/i8k\'!\n" + "The kernel needs to be compiled with support\n" + "for /proc filesystem enabled!\n" ); + return -1; + } + + if ( ( n = read( fd, I8kBuf, I8KBUFSIZE - 1 ) ) == I8KBUFSIZE - 1 ) { + log_error( "Internal buffer too small to read \'/proc/i8k\'" ); + + close( fd ); + return -1; + } + + close( fd ); + I8kBuf[ n ] = '\0'; + + sscanf( I8kBuf, "%*f %*s %*s %d %*d %*d %d %d %*d %*d", + &cpuTemp, &fan0Speed, &fan1Speed ); + + return 0; +} + +void printI8kCPUTemperature( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "%d\n", cpuTemp ); +} + +void printI8kCPUTemperatureInfo( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "CPU Temperature\t0\t0\tC\n" ); +} + +void printI8kFan0Speed( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "%d\n", fan0Speed ); +} + +void printI8kFan0SpeedInfo( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "Left fan\t0\t0\trpm\n" ); +} + +void printI8kFan1Speed( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "%d\n", fan1Speed ); +} + +void printI8kFan1SpeedInfo( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "Right fan\t0\t0\trpm\n" ); +} + +#else /* HAVE_I8K_SUPPORT */ + +/* dummy version for systems that have no i8k support */ + +void initI8k( struct SensorModul* sm ) +{ + (void)sm; +} + +void exitI8k( void ) +{ +} + +int updateI8k( void ) +{ + return 0; +} + +#endif diff --git a/ksysguard/ksysguardd/Linux/i8k.h b/ksysguard/ksysguardd/Linux/i8k.h new file mode 100644 index 000000000..40c2a886d --- /dev/null +++ b/ksysguard/ksysguardd/Linux/i8k.h @@ -0,0 +1,36 @@ +/* + 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. + +*/ + +#ifndef KSG_I8K_H +#define KSG_I8K_H + +void initI8k( struct SensorModul* ); +void exitI8k( void ); + +int updateI8k( void ); + +void printI8kCPUTemperature( const char* ); +void printI8kCPUTemperatureInfo( const char* ); +void printI8kFan0Speed( const char* ); +void printI8kFan0SpeedInfo( const char* ); +void printI8kFan1Speed( const char* ); +void printI8kFan1SpeedInfo( const char* ); + +#endif diff --git a/ksysguard/ksysguardd/Linux/lmsensors.c b/ksysguard/ksysguardd/Linux/lmsensors.c new file mode 100644 index 000000000..37e41d2a1 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/lmsensors.c @@ -0,0 +1,309 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 1999 - 2001 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 <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "Command.h" +#include "ccont.h" +#include "ksysguardd.h" + +#include "lmsensors.h" + +#ifdef HAVE_SENSORS_SENSORS_H +#include <sensors/sensors.h> + +#ifndef SENSORS_API_VERSION +#define SENSORS_API_VERSION 0x000 +#endif +#ifndef SENSORS_CHIP_NAME_BUS_PCI +#define SENSORS_CHIP_NAME_BUS_PCI -5 +#endif +#ifndef SENSORS_CHIP_NAME_BUS_ISA +#define SENSORS_CHIP_NAME_BUS_ISA -1 +#endif + +#define BUFFER_SIZE_LMSEN 300 +typedef struct +{ + char* fullName; + const sensors_chip_name* scn; +#if SENSORS_API_VERSION & 0x400 + const sensors_feature *sf; + const sensors_subfeature *sfd; +#else + const sensors_feature_data* sfd; +#endif +} LMSENSOR; + +static CONTAINER LmSensors; +static int LmSensorsOk = -1; + +static int sensorCmp( void* s1, void* s2 ) +{ + return strcmp( ((LMSENSOR*)s1)->fullName, ((LMSENSOR*)s2)->fullName ); +} + +static LMSENSOR* findMatchingSensor( const char* name ) +{ + INDEX idx; + LMSENSOR key; + LMSENSOR* s; + + if(name == NULL || name[0] == '\0') return 0; + key.fullName = strdup( name ); + int end = strlen(key.fullName)-1; + if(key.fullName[end] == '?') + key.fullName[end] = '\0'; + if ( ( idx = search_ctnr( LmSensors, sensorCmp, &key ) ) < 0 ) { + free( key.fullName ); + return 0; + } + + free( key.fullName ); + s = get_ctnr( LmSensors, idx ); + + return s; +} + +static const char *chipName(const sensors_chip_name *chip) { + static char buffer[256]; +#if SENSORS_API_VERSION & 0x400 + sensors_snprintf_chip_name(buffer, sizeof(buffer), chip); +#else /* SENSORS_API_VERSION & 0x400 */ + if (chip->bus == SENSORS_CHIP_NAME_BUS_ISA) + snprintf (buffer, sizeof(buffer), "%s-isa-%04x", chip->prefix, chip->addr); + else if (chip->bus == SENSORS_CHIP_NAME_BUS_PCI) + snprintf (buffer, sizeof(buffer), "%s-pci-%04x", chip->prefix, chip->addr); + else + snprintf (buffer, sizeof(buffer), "%s-i2c-%d-%02x", chip->prefix, chip->bus, chip->addr); +#endif /* SENSORS_API_VERSION & 0x400 */ + return buffer; +} + +#if SENSORS_API_VERSION & 0x400 +void initLmSensors( struct SensorModul* sm ) +{ + const sensors_chip_name* scn; + int nr = 0; + + if ( sensors_init( NULL ) ) { + LmSensorsOk = -1; + return; + } + + LmSensors = new_ctnr(); + while ( ( scn = sensors_get_detected_chips( NULL, &nr ) ) != NULL ) { + int nr1 = 0; + const sensors_feature* sf; + + while ( ( sf = sensors_get_features( scn, &nr1 ) ) != 0 ) { + const sensors_subfeature *ssubf; + LMSENSOR *p; + char *s, *label; + + switch( sf->type ) + { + case SENSORS_FEATURE_IN: + ssubf = sensors_get_subfeature( scn, sf, + SENSORS_SUBFEATURE_IN_INPUT ); + break; + + case SENSORS_FEATURE_FAN: + ssubf = sensors_get_subfeature( scn, sf, + SENSORS_SUBFEATURE_FAN_INPUT ); + break; + + case SENSORS_FEATURE_TEMP: + ssubf = sensors_get_subfeature( scn, sf, + SENSORS_SUBFEATURE_TEMP_INPUT ); + break; + default: + ssubf = NULL; + } + + if ( !ssubf ) + continue; + + label = sensors_get_label( scn, sf ); + p = (LMSENSOR*)malloc( sizeof( LMSENSOR ) ); + p->fullName = (char*)malloc( strlen( "lmsensors/" ) + + strlen( scn->prefix ) + 1 + + strlen( label ) + 1 ); + snprintf( p->fullName, BUFFER_SIZE_LMSEN, "lmsensors/%s/%s", scn->prefix, label ); + + /* Make sure that name contains only proper characters. */ + for ( s = p->fullName; *s; s++ ) + if ( *s == ' ' ) + *s = '_'; + + p->scn = scn; + p->sf = sf; + p->sfd = ssubf; + + /* Note a name collision should never happen with the lm_sensors-3x code, + but it does in the case of k8temp, when there are 2 identical labeled + sensors per CPU. This are really 2 distinct sensors measuring the + same thing, but fullName must be unique so we just drop the second + sensor */ + if ( search_ctnr( LmSensors, sensorCmp, p ) < 0 ) { + push_ctnr( LmSensors, p ); + registerMonitor( p->fullName, "float", printLmSensor, printLmSensorInfo, sm ); + } else { + free( p->fullName ); + free( p ); + } + free( label ); + } + } + bsort_ctnr( LmSensors, sensorCmp ); +} +#else /* SENSORS_API_VERSION & 0x400 */ +void initLmSensors( struct SensorModul* sm ) +{ + const sensors_chip_name* scn; + char buffer[BUFFER_SIZE_LMSEN]; + int nr = 0; + + FILE* input; + if ( ( input = fopen( "/etc/sensors.conf", "r" ) ) == NULL ) { + LmSensorsOk = -1; + return; + } + + if ( sensors_init( input ) ) { + LmSensorsOk = -1; + fclose( input ); + return; + } + + fclose( input ); + + LmSensors = new_ctnr(); + while ( ( scn = sensors_get_detected_chips( &nr ) ) != NULL ) { + int nr1, nr2; + const sensors_feature_data* sfd; + nr1 = nr2 = 0; + while ( ( sfd = sensors_get_all_features( *scn, &nr1, &nr2 ) ) != 0 ) { + if ( sfd->mapping == SENSORS_NO_MAPPING && sfd->mode & SENSORS_MODE_R /* readable feature */) { + LMSENSOR* p; + char* label=NULL; + + if(sensors_get_label( *scn, sfd->number, &label ) != 0) + continue; /*error*/ + else + free( label ); + if(sensors_get_ignored( *scn, sfd->number) != 1 ) + continue; /* 1 for not ignored, 0 for ignore, <0 for error */ + double result; + if(sensors_get_feature( *scn, sfd->number, &result) != 0 ) + continue; /* Make sure this feature actually works. 0 for success, <0 for fail */ + + p = (LMSENSOR*)malloc( sizeof( LMSENSOR ) ); + + snprintf( buffer, BUFFER_SIZE_LMSEN, "lmsensors/%s/%s", chipName(scn), sfd->name ); + + p->fullName = strndup(buffer, BUFFER_SIZE_LMSEN); + + p->scn = scn; + p->sfd = sfd; + if ( search_ctnr( LmSensors, sensorCmp, p ) < 0 ) { + push_ctnr( LmSensors, p ); + registerMonitor( p->fullName, "float", printLmSensor, printLmSensorInfo, sm ); + } else { + free( p->fullName ); + free( p ); + } + } + } + } + bsort_ctnr( LmSensors, sensorCmp ); +} +#endif /* SENSORS_API_VERSION & 0x400 */ + +void exitLmSensors( void ) +{ + destr_ctnr( LmSensors, free ); +} + +void printLmSensor( const char* cmd ) +{ + double value; + LMSENSOR* s; + + if ( ( s = findMatchingSensor( cmd ) ) == 0 ) { /* should never happen */ + fprintf( CurrentClient, "0\n" ); + return; + } +#if SENSORS_API_VERSION & 0x400 + sensors_get_value( s->scn, s->sfd->number, &value ); +#else + sensors_get_feature( *(s->scn), s->sfd->number, &value ); +#endif + fprintf( CurrentClient, "%f\n", value ); +} + +void printLmSensorInfo( const char* cmd ) +{ + LMSENSOR* s; + + if ( ( s = findMatchingSensor( cmd ) ) == 0 ) { /* should never happen */ + fprintf( CurrentClient, "0\n" ); + return; + } + + /* TODO: print real name here */ + char *label; +#if SENSORS_API_VERSION & 0x400 + label = sensors_get_label( s->scn, s->sf ); + if (label == NULL) { +#else + if(sensors_get_label( *s->scn, s->sfd->number, &label ) != 0) { /*error*/ +#endif + fprintf( CurrentClient, "0\n" ); + return; + } + if( strncmp(s->sfd->name, "temp", sizeof("temp")-1) == 0) + fprintf( CurrentClient, "%s\t0\t0\t°C\n", label ); + else if( strncmp(s->sfd->name, "fan", sizeof("fan")-1) == 0) + fprintf( CurrentClient, "%s\t0\t0\trpm\n", label ); + else + fprintf( CurrentClient, "%s\t0\t0\tV\n", label ); /* For everything else, say it's in volts. */ +#if SENSORS_API_VERSION & 0x400 + free(label); +#endif +} + +#else /* HAVE_SENSORS_SENSORS_H */ + +/* dummy version for systems that have no lmsensors support */ + +void initLmSensors( struct SensorModul* sm ) +{ + (void)sm; +} + +void exitLmSensors( void ) +{ +} + +#endif diff --git a/ksysguard/ksysguardd/Linux/lmsensors.h b/ksysguard/ksysguardd/Linux/lmsensors.h new file mode 100644 index 000000000..29f514d4a --- /dev/null +++ b/ksysguard/ksysguardd/Linux/lmsensors.h @@ -0,0 +1,30 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 1999 - 2001 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. + +*/ + +#ifndef KSG_LMSENSORS_H +#define KSG_LMSENSORS_H + +void initLmSensors( struct SensorModul* ); +void exitLmSensors( void ); + +void printLmSensor( const char* ); +void printLmSensorInfo( const char* ); + +#endif diff --git a/ksysguard/ksysguardd/Linux/loadavg.c b/ksysguard/ksysguardd/Linux/loadavg.c new file mode 100644 index 000000000..788e32793 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/loadavg.c @@ -0,0 +1,143 @@ +/* + 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 <fcntl.h> +#include <unistd.h> +#include <stdio.h> + +#include "ksysguardd.h" +#include "Command.h" + +#include "loadavg.h" + +static int LoadAvgOK = 0; +static double LoadAvg1, LoadAvg5, LoadAvg15; + +#define LOADAVGBUFSIZE 128 +static char LoadAvgBuf[ LOADAVGBUFSIZE ]; +static int Dirty = 0; + +static void processLoadAvg( void ) +{ + sscanf( LoadAvgBuf, "%lf %lf %lf", &LoadAvg1, &LoadAvg5, &LoadAvg15 ); + Dirty = 0; +} + +/* +================================ public part ================================= +*/ + +void initLoadAvg( struct SensorModul* sm ) +{ + if ( updateLoadAvg() < 0 ) { + LoadAvgOK = -1; + return; + } else + LoadAvgOK = 1; + + registerMonitor( "cpu/loadavg1", "float", printLoadAvg1, printLoadAvg1Info, sm ); + registerMonitor( "cpu/loadavg5", "float", printLoadAvg5, printLoadAvg5Info, sm ); + registerMonitor( "cpu/loadavg15", "float", printLoadAvg15, printLoadAvg15Info, sm ); +} + +void exitLoadAvg( void ) +{ + LoadAvgOK = -1; +} + +int updateLoadAvg( void ) +{ + size_t n; + int fd; + + if ( LoadAvgOK < 0 ) + return -1; + + if ( ( fd = open( "/proc/loadavg", O_RDONLY ) ) < 0 ) { + if ( LoadAvgOK != 0 ) + print_error( "Cannot open file \'/proc/loadavg\'!\n" + "The kernel needs to be compiled with support\n" + "for /proc filesystem enabled!\n" ); + return -1; + } + + if ( ( n = read( fd, LoadAvgBuf, LOADAVGBUFSIZE - 1 ) ) == LOADAVGBUFSIZE - 1 ) { + log_error( "Internal buffer too small to read \'/proc/loadavg\'" ); + + close( fd ); + return -1; + } + + close( fd ); + LoadAvgBuf[ n ] = '\0'; + Dirty = 1; + + return 0; +} + +void printLoadAvg1( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processLoadAvg(); + + fprintf( CurrentClient, "%f\n", LoadAvg1 ); +} + +void printLoadAvg1Info( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "Load average 1 min\t0\t0\t\n" ); +} + +void printLoadAvg5( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processLoadAvg(); + + fprintf( CurrentClient, "%f\n", LoadAvg5 ); +} + +void printLoadAvg5Info( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "Load average 5 min\t0\t0\t\n" ); +} + +void printLoadAvg15( const char* cmd ) +{ + (void)cmd; + + if ( Dirty ) + processLoadAvg(); + + fprintf( CurrentClient, "%f\n", LoadAvg15 ); +} + +void printLoadAvg15Info( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "Load average 15 min\t0\t0\t\n" ); +} diff --git a/ksysguard/ksysguardd/Linux/loadavg.h b/ksysguard/ksysguardd/Linux/loadavg.h new file mode 100644 index 000000000..31628ac96 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/loadavg.h @@ -0,0 +1,36 @@ +/* + 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. + +*/ + +#ifndef KSG_LOADAVG_H +#define KSG_LOADAVG_H + +void initLoadAvg( struct SensorModul* ); +void exitLoadAvg( void ); + +int updateLoadAvg( void ); + +void printLoadAvg1( const char* ); +void printLoadAvg1Info( const char* ); +void printLoadAvg5( const char* ); +void printLoadAvg5Info( const char* ); +void printLoadAvg15( const char* ); +void printLoadAvg15Info( const char* ); + +#endif diff --git a/ksysguard/ksysguardd/Linux/logfile.c b/ksysguard/ksysguardd/Linux/logfile.c new file mode 100644 index 000000000..58915a207 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/logfile.c @@ -0,0 +1,172 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 2003 Tobias Koenig <tokoe@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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "Command.h" +#include "ccont.h" +#include "conf.h" +#include "ksysguardd.h" + +#include "logfile.h" + +static CONTAINER LogFiles = 0; +static unsigned long counter = 1; + +typedef struct { + char name[ 256 ]; + FILE* fh; + unsigned long id; +} LogFileEntry; + +extern CONTAINER LogFileList; + +/* +================================ public part ================================= +*/ + +void initLogFile( struct SensorModul* sm ) +{ + char monitor[ 1024 ]; + ConfigLogFile *entry; + + registerCommand( "logfile_register", registerLogFile ); + registerCommand( "logfile_unregister", unregisterLogFile ); + registerCommand( "logfile_registered", printRegistered ); + + for ( entry = first_ctnr( LogFileList ); entry; entry = next_ctnr( LogFileList ) ) { + FILE* fp; + /* Register the log file only if we can actually read the file. */ + if ( ( fp = fopen( entry->path, "r" ) ) != NULL ) { + snprintf( monitor, 1024, "logfiles/%s", entry->name ); + registerMonitor( monitor, "logfile", printLogFile, printLogFileInfo, sm ); + fclose( fp ); + } + } + + LogFiles = new_ctnr(); +} + +void exitLogFile( void ) +{ + destr_ctnr( LogFiles, free ); +} + +void printLogFile( const char* cmd ) +{ + char line[ 1024 ]; + unsigned long id; + LogFileEntry *entry; + + sscanf( cmd, "%*s %lu", &id ); + + for ( entry = first_ctnr( LogFiles ); entry; entry = next_ctnr( LogFiles ) ) { + if ( entry->id == id ) { + while ( fgets( line, 1024, entry->fh ) != NULL ) + fprintf( CurrentClient, "%s", line ); + + /* delete the EOF */ + clearerr( entry->fh ); + } + } + + fprintf( CurrentClient, "\n" ); +} + +void printLogFileInfo( const char* cmd ) +{ + (void)cmd; + fprintf( CurrentClient, "LogFile\n" ); +} + +void registerLogFile( const char* cmd ) +{ + char name[ 257 ]; + FILE* file; + LogFileEntry *entry; + int i; + + memset( name, 0, sizeof( name ) ); + sscanf( cmd, "%*s %256s", name ); + + for ( i = 0; i < level_ctnr( LogFileList ); i++ ) { + ConfigLogFile *conf = get_ctnr( LogFileList, i ); + if ( !strcmp( conf->name, name ) ) { + if ( ( file = fopen( conf->path, "r" ) ) == NULL ) { + print_error( "fopen()" ); + fprintf( CurrentClient, "0\n" ); + return; + } + + fseek( file, 0, SEEK_END ); + + if ( ( entry = (LogFileEntry*)malloc( sizeof( LogFileEntry ) ) ) == NULL ) { + print_error( "malloc()" ); + fprintf( CurrentClient, "0\n" ); + return; + } + + entry->fh = file; + strncpy( entry->name, conf->name, 256 ); + entry->id = counter; + + push_ctnr( LogFiles, entry ); + + fprintf( CurrentClient, "%lu\n", counter ); + counter++; + + return; + } + } + + fprintf( CurrentClient, "\n" ); +} + +void unregisterLogFile( const char* cmd ) +{ + unsigned long id; + LogFileEntry *entry; + + sscanf( cmd, "%*s %lu", &id ); + + for ( entry = first_ctnr( LogFiles ); entry; entry = next_ctnr( LogFiles ) ) { + if ( entry->id == id ) { + fclose( entry->fh ); + free( remove_ctnr( LogFiles ) ); + fprintf( CurrentClient, "\n" ); + return; + } + } + + fprintf( CurrentClient, "\n" ); +} + +void printRegistered( const char* cmd ) +{ + LogFileEntry *entry; + + (void)cmd; + for ( entry = first_ctnr( LogFiles ); entry; entry = next_ctnr( LogFiles ) ) + fprintf( CurrentClient, "%s:%lu\n", entry->name, entry->id ); + + fprintf( CurrentClient, "\n" ); +} diff --git a/ksysguard/ksysguardd/Linux/logfile.h b/ksysguard/ksysguardd/Linux/logfile.h new file mode 100644 index 000000000..5eecb2f70 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/logfile.h @@ -0,0 +1,36 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 2003 Tobias Koenig <tokoe@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. + +*/ + +#ifndef KSG_LOGFILE_H +#define KSG_LOGFILE_H + +void initLogFile( struct SensorModul* ); +void exitLogFile( void ); + +void printLogFile( const char* ); +void printLogFileInfo( const char* ); + +void registerLogFile( const char* ); +void unregisterLogFile( const char* ); + +/* debug command */ +void printRegistered( const char* ); + +#endif diff --git a/ksysguard/ksysguardd/Linux/netdev.c b/ksysguard/ksysguardd/Linux/netdev.c new file mode 100644 index 000000000..55e812807 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/netdev.c @@ -0,0 +1,367 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 1999 - 2001 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 <config.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> + +#include "Command.h" +#include "ksysguardd.h" + +#include "netdev.h" + +#define MON_SIZE 128 + +#define CALC( a, b, c, d, e ) \ +{ \ + NetDevs[ i ].a = a - NetDevs[ i ].Old##a; \ + NetDevs[ i ].Old##a = a; \ +} + +#define REGISTERSENSOR( a, b, c, d, e ) \ +{ \ + snprintf( mon, MON_SIZE, "network/interfaces/%s/%s", tag, b ); \ + registerMonitor( mon, "integer", printNetDev##a, printNetDev##a##Info, NetDevSM ); \ +} + +#define UNREGISTERSENSOR( a, b, c, d, e ) \ +{ \ + snprintf( mon, MON_SIZE, "network/interfaces/%s/%s", NetDevs[ i ].name, b ); \ + removeMonitor( mon ); \ +} + +#define DEFMEMBERS( a, b, c, d, e ) \ +unsigned long long Old##a; \ +unsigned long long a; \ +unsigned long a##Scale; + +#define DEFVARS( a, b, c, d, e ) \ +unsigned long long a; + +#define FORALL( a ) \ + a( recBytes, "receiver/data", "Received Data", "kBytes/s", 1024 ) \ + a( recPacks, "receiver/packets", "Received Packets", "1/s", 1 ) \ + a( recErrs, "receiver/errors", "Receiver Errors", "1/s", 1 ) \ + a( recDrop, "receiver/drops", "Receiver Drops", "1/s", 1 ) \ + a( recFifo, "receiver/fifo", "Receiver FIFO Overruns", "1/s", 1 ) \ + a( recFrame, "receiver/frame", "Receiver Frame Errors", "1/s", 1 ) \ + a( recCompressed, "receiver/compressed", "Received Compressed Packets", "1/s", 1 ) \ + a( recMulticast, "receiver/multicast", "Received Multicast Packets", "1/s", 1 ) \ + a( sentBytes, "transmitter/data", "Sent Data", "kBytes/s", 1024 ) \ + a( sentPacks, "transmitter/packets", "Sent Packets", "1/s", 1 ) \ + a( sentErrs, "transmitter/errors", "Transmitter Errors", "1/s", 1 ) \ + a( sentDrop, "transmitter/drops", "Transmitter Drops", "1/s", 1 ) \ + a( sentFifo, "transmitter/fifo", "Transmitter FIFO overruns", "1/s", 1 ) \ + a( sentColls, "transmitter/collisions", "Transmitter Collisions", "1/s", 1 ) \ + a( sentCarrier, "transmitter/carrier", "Transmitter Carrier losses", "1/s", 1 ) \ + a( sentCompressed, "transmitter/compressed", "Transmitter Compressed Packets", "1/s", 1 ) + +#define SETZERO( a, b, c, d, e ) \ +a = 0; + +#define SETMEMBERZERO( a, b, c, d, e ) \ +NetDevs[ i ].a = 0; \ +NetDevs[ i ].a##Scale = e; + +#define DECLAREFUNC( a, b, c, d, e ) \ +void printNetDev##a( const char* cmd ); \ +void printNetDev##a##Info( const char* cmd ); + +typedef struct +{ + FORALL( DEFMEMBERS ) + char name[ 32 ]; +} NetDevInfo; + +/* 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* NetDevSM; + +#define NETDEVBUFSIZE 4096 +static char NetDevBuf[ NETDEVBUFSIZE ]; +static int NetDevCnt = 0; +static int Dirty = 0; +static int NetDevOk = 0; +static long OldHash = 0; + +#define MAXNETDEVS 64 +static NetDevInfo NetDevs[ MAXNETDEVS ]; + +void processNetDev( void ); + +FORALL( DECLAREFUNC ) + +static int processNetDev_( void ) +{ + int i; + char format[ 32 ]; + char devFormat[ 16 ]; + char buf[ 1024 ]; + char tag[ 64 ]; + char* netDevBufP = NetDevBuf; + + sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 ); + sprintf( devFormat, "%%%ds", (int)sizeof( tag ) - 1 ); + + /* skip 2 first lines */ + for ( i = 0; i < 2; i++ ) { + sscanf( netDevBufP, format, buf ); + buf[ sizeof( buf ) - 1 ] = '\0'; + netDevBufP += strlen( buf ) + 1; /* move netDevBufP to next line */ + } + + for ( i = 0; sscanf( netDevBufP, format, buf ) == 1; ++i ) { + buf[ sizeof( buf ) - 1 ] = '\0'; + netDevBufP += strlen( buf ) + 1; /* move netDevBufP to next line */ + + if ( sscanf( buf, devFormat, tag ) ) { + char* pos = strchr( tag, ':' ); + if ( pos ) { + FORALL( DEFVARS ); + *pos = '\0'; + FORALL( SETZERO ); + sscanf( buf + 7, "%llu %llu %llu %llu %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu", + &recBytes, &recPacks, &recErrs, &recDrop, &recFifo, + &recFrame, &recCompressed, &recMulticast, + &sentBytes, &sentPacks, &sentErrs, &sentDrop, + &sentFifo, &sentColls, &sentCarrier, &sentCompressed ); + + if ( i >= NetDevCnt || strcmp( NetDevs[ i ].name, tag ) != 0 ) { + /* The network device configuration has changed. We + * need to reconfigure the netdev module. */ + return -1; + } else { + FORALL( CALC ); + } + } + } + } + + if ( i != NetDevCnt ) + return -1; + + /* save exact time inverval between this and the last read of + * /proc/net/dev */ + timeInterval = currSampling.tv_sec - lastSampling.tv_sec + + ( currSampling.tv_usec - lastSampling.tv_usec ) / 1000000.0; + lastSampling = currSampling; + Dirty = 0; + + return 0; +} + +void processNetDev( void ) +{ + int i; + + if ( NetDevCnt == 0 ) + return; + + for ( i = 0; i < 5 && processNetDev_() < 0; ++i ) + checkNetDev(); + + /* If 5 reconfiguration attemts failed, something is very wrong and + * we close the netdev module for further use. */ + if ( i == 5 ) + exitNetDev(); +} + +/* +================================ public part ================================= +*/ + +void initNetDev( struct SensorModul* sm ) +{ + int i; + char format[ 32 ]; + char devFormat[ 16 ]; + char buf[ 1024 ]; + char tag[ 64 ]; + char* netDevBufP = NetDevBuf; + + NetDevSM = sm; + + if ( updateNetDev() < 0 ) + return; + + sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 ); + sprintf( devFormat, "%%%ds", (int)sizeof( tag ) - 1 ); + + /* skip 2 first lines */ + for ( i = 0; i < 2; i++ ) { + sscanf( netDevBufP, format, buf ); + buf[ sizeof( buf ) - 1 ] = '\0'; + netDevBufP += strlen( buf ) + 1; /* move netDevBufP to next line */ + } + + for ( i = 0; sscanf( netDevBufP, format, buf ) == 1; ++i ) { + buf[ sizeof( buf ) - 1 ] = '\0'; + netDevBufP += strlen( buf ) + 1; /* move netDevBufP to next line */ + + if ( sscanf( buf, devFormat, tag ) ) { + char* pos = strchr( tag, ':' ); + if ( pos ) { + char mon[ MON_SIZE ]; + *pos = '\0'; + strlcpy( NetDevs[ i ].name, tag, sizeof( NetDevs[ i ].name ) ); + FORALL( REGISTERSENSOR ); + sscanf( pos + 1, "%llu %llu %llu %llu %llu %llu %llu %llu" + "%llu %llu %llu %llu %llu %llu %llu %llu", + &NetDevs[ i ].recBytes, &NetDevs[ i ].recPacks, + &NetDevs[ i ].recErrs, &NetDevs[ i ].recDrop, + &NetDevs[ i ].recFifo, &NetDevs[ i ].recFrame, + &NetDevs[ i ].recCompressed, &NetDevs[ i ].recMulticast, + &NetDevs[ i ].sentBytes, &NetDevs[ i ].sentPacks, + &NetDevs[ i ].sentErrs, &NetDevs[ i ].sentDrop, + &NetDevs[ i ].sentFifo, &NetDevs[ i ].sentColls, + &NetDevs[ i ].sentCarrier, &NetDevs[ i ].sentCompressed ); + NetDevCnt++; + } + FORALL( SETMEMBERZERO ); + } + } + + /* Call processNetDev to elimitate initial peek values. */ + processNetDev(); +} + +void exitNetDev( void ) +{ + int i; + + for ( i = 0; i < NetDevCnt; ++i ) { + char mon[ MON_SIZE ]; + FORALL( UNREGISTERSENSOR ); + } + NetDevCnt = 0; +} + +int updateNetDev( void ) +{ + /* We read the information about the network interfaces from + /proc/net/dev. The file should look like this: + + Inter-| Receive | Transmit + face | bytes packets errs drop fifo frame compressed multicast| bytes packets errs drop fifo colls carrier compressed + lo:275135772 1437448 0 0 0 0 0 0 275135772 1437448 0 0 0 0 0 0 + eth0:123648812 655251 0 0 0 0 0 0 246847871 889636 0 0 0 0 0 0 Inter-| Receive | Transmit + face | bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed + lo:275135772 1437448 0 0 0 0 0 0 275135772 1437448 0 0 0 0 0 0 + eth0:123648812 655251 0 0 0 0 0 0 246847871 889636 0 0 0 0 0 0 + */ + + size_t n; + int fd; + long hash; + char* p; + + if ( NetDevOk < 0 ) + return 0; + + if ( ( fd = open( "/proc/net/dev", O_RDONLY ) ) < 0 ) { + /* /proc/net/dev may not exist on some machines. */ + NetDevOk = -1; + return 0; + } + + if ( ( n = read( fd, NetDevBuf, NETDEVBUFSIZE - 1 ) ) == NETDEVBUFSIZE - 1 ) { + log_error( "Internal buffer too small to read \'/proc/net/dev\'" ); + NetDevOk = -1; + + close( fd ); + return -1; + } + + gettimeofday( &currSampling, 0 ); + close( fd ); + NetDevOk = 1; + NetDevBuf[ n ] = '\0'; + + /* Calculate hash over the first 7 characters of each line starting + * after the first newline. */ + for ( p = NetDevBuf, hash = 0; *p; ++p ) + if ( *p == '\n' ) + for ( ++p; *p && *p != ':' && *p != '|'; ++p ) + hash = ( ( hash << 6 ) + *p ) % 390389; + + if ( OldHash != 0 && OldHash != hash ) { + print_error( "RECONFIGURE\n" ); + CheckSetupFlag = 1; + } + OldHash = hash; + + Dirty = 1; + + return 0; +} + +void checkNetDev( void ) +{ + /* Values for other network devices are lost, but it is still better + * than not detecting any new devices. TODO: Fix after 2.1 is out. */ + exitNetDev(); + initNetDev( NetDevSM ); +} + +#define PRINTFUNC( a, b, c, d, e ) \ +void printNetDev##a( const char* cmd ) \ +{ \ + int i; \ + char* beg; \ + char* end; \ + char dev[ 64 ]; \ + \ + beg = strchr( cmd, '/' ); \ + beg = strchr( beg + 1, '/' ); \ + end = strchr( beg + 1, '/' ); \ + strncpy( dev, beg + 1, end - beg - 1 ); \ + dev[ end - beg - 1 ] = '\0'; \ + \ + if ( Dirty ) \ + processNetDev(); \ + \ + for ( i = 0; i < MAXNETDEVS; ++i ) \ + if ( strcmp( NetDevs[ i ].name, dev ) == 0) { \ + fprintf( CurrentClient, "%lu\n", (unsigned long) \ + ( NetDevs[ i ].a / ( NetDevs[ i ].a##Scale * timeInterval ) ) ); \ + return; \ + } \ + \ + fprintf( CurrentClient, "0\n" ); \ +} \ + \ +void printNetDev##a##Info( const char* cmd ) \ +{ \ + (void)cmd; \ + fprintf( CurrentClient, "%s\t0\t0\t%s\n", c, d ); \ +} + +FORALL( PRINTFUNC ) diff --git a/ksysguard/ksysguardd/Linux/netdev.h b/ksysguard/ksysguardd/Linux/netdev.h new file mode 100644 index 000000000..d470adfae --- /dev/null +++ b/ksysguard/ksysguardd/Linux/netdev.h @@ -0,0 +1,35 @@ +/* + 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. + +*/ + +#ifndef KSG_NETDEV_H +#define KSG_NETDEV_H + +void initNetDev( struct SensorModul* ); +void exitNetDev( void ); + +int updateNetDev( void ); +void checkNetDev( void ); + +void printNetDevRecBytes( const char* ); +void printNetDevRecBytesInfo( const char* ); +void printNetDevSentBytes( const char* ); +void printNetDevSentBytesInfo( const char* ); + +#endif diff --git a/ksysguard/ksysguardd/Linux/netstat.c b/ksysguard/ksysguardd/Linux/netstat.c new file mode 100644 index 000000000..c8570f617 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/netstat.c @@ -0,0 +1,495 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 2001 Tobias Koenig <tokoe@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 <config.h> + +#include <arpa/inet.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "ksysguardd.h" +#include "Command.h" +#include "ccont.h" +#include "netstat.h" + +static CONTAINER TcpSocketList = 0; +static CONTAINER UdpSocketList = 0; +static CONTAINER UnixSocketList = 0; +static CONTAINER RawSocketList = 0; + +static int num_tcp = 0; +static int num_udp = 0; +static int num_unix = 0; +static int num_raw = 0; + +typedef struct { + char local_addr[128]; + char local_port[128]; + char remote_addr[128]; + char remote_port[128]; + char state[128]; + int uid; +} SocketInfo; + +typedef struct { + int refcount; + char type[128]; + char state[128]; + int inode; + char path[256]; +} UnixInfo; + +char *get_serv_name(int port, const char *proto); +char *get_host_name(int addr); +char *get_proto_name(int number); +int get_num_sockets(FILE *netstat); +void printSocketInfo(SocketInfo* socket_info); + +static time_t TcpUdpRaw_timeStamp = 0; +static time_t Unix_timeStamp = 0; +static time_t NetStat_timeStamp = 0; + +static const char *raw_type[] = +{ + "", + "stream", + "dgram", + "raw", + "rdm", + "seqpacket", + "packet" +}; + +static const char *raw_state[] = +{ + "free", + "unconnected", + "connecting", + "connected", + "disconnecting" +}; + +static const char *conn_state[] = +{ + "", + "established", + "syn_sent", + "syn_recv", + "fin_wait1", + "fin_wait2", + "time_wait", + "close", + "close_wait", + "last_ack", + "listen", + "closing" +}; + +char *get_serv_name(int port, const char *proto) +{ + static char buffer[1024]; + struct servent *service; + + if (port == 0) { + return (char *)"*"; + } + + memset(buffer, 0, sizeof(buffer)); + + if ((service = getservbyport(ntohs(port), proto)) == NULL) { + snprintf(buffer, sizeof(buffer), "%d", port); + } else { + strlcpy(buffer, service->s_name, sizeof(buffer)); + } + + return (char *)buffer; +} + +char *get_host_name(int addr) +{ + static char buffer[1024]; + struct hostent *host; + struct in_addr a_addr; + + if (addr == 0) { + return (char *)"*"; + } + + memset(buffer, 0, sizeof(buffer)); + + if ((host = gethostbyaddr((char *)&addr, 4, AF_INET)) == NULL) { + a_addr.s_addr = addr; + return inet_ntoa(a_addr); + } else { + strlcpy(buffer, host->h_name, sizeof(buffer)); + return (char *)buffer; + } +} + +char *get_proto_name(int number) +{ + static char buffer[1024]; + struct protoent *protocol; + + if (number == 0) { + return (char *)"*"; + } + + memset(buffer, 0, sizeof(buffer)); + + if ((protocol = getprotobynumber(number)) == NULL) { + snprintf(buffer, sizeof(buffer), "%d", number); + } else { + strlcpy(buffer, protocol->p_name, sizeof(buffer)); + } + + return (char *)buffer; +} + +int get_num_sockets(FILE *netstat) +{ + char line[1024]; + int line_count = 0; + + while (fgets(line, 1024, netstat) != NULL) + line_count++; + + return line_count - 1; +} + +void printSocketInfo(SocketInfo* socket_info) +{ + fprintf(CurrentClient, "%s\t%s\t%s\t%s\t%s\t%d\n", + socket_info->local_addr, + socket_info->local_port, + socket_info->remote_addr, + socket_info->remote_port, + socket_info->state, + socket_info->uid); +} + +/* +================================ public part ================================= +*/ + +void +initNetStat(struct SensorModul* sm) +{ + FILE *netstat; + + if ((netstat = fopen("/proc/net/tcp", "r")) != NULL) { + registerMonitor("network/sockets/tcp/count", "integer", printNetStat, printNetStatInfo, sm); + registerMonitor("network/sockets/tcp/list", "listview", printNetStatTcpUdpRaw, printNetStatTcpUdpRawInfo, sm); + fclose(netstat); + } + if ((netstat = fopen("/proc/net/udp", "r")) != NULL) { + registerMonitor("network/sockets/udp/count", "integer", printNetStat, printNetStatInfo, sm); + registerMonitor("network/sockets/udp/list", "listview", printNetStatTcpUdpRaw, printNetStatTcpUdpRawInfo, sm); + fclose(netstat); + } + if ((netstat = fopen("/proc/net/unix", "r")) != NULL) { + registerMonitor("network/sockets/unix/count", "integer", printNetStat, printNetStatInfo, sm); + registerMonitor("network/sockets/unix/list", "listview", printNetStatUnix, printNetStatUnixInfo, sm); + fclose(netstat); + } + if ((netstat = fopen("/proc/net/raw", "r")) != NULL) { + registerMonitor("network/sockets/raw/count", "integer", printNetStat, printNetStatInfo, sm); + registerMonitor("network/sockets/raw/list", "listview", printNetStatTcpUdpRaw, printNetStatTcpUdpRawInfo, sm); + fclose(netstat); + } + + TcpSocketList = new_ctnr(); + UdpSocketList = new_ctnr(); + RawSocketList = new_ctnr(); + UnixSocketList = new_ctnr(); +} + +void +exitNetStat(void) +{ + destr_ctnr(TcpSocketList, free); + destr_ctnr(UdpSocketList, free); + destr_ctnr(RawSocketList, free); + destr_ctnr(UnixSocketList, free); +} + +int +updateNetStat(void) +{ + FILE *netstat; + + if ((netstat = fopen("/proc/net/tcp", "r")) != NULL) { + num_tcp = get_num_sockets(netstat); + fclose(netstat); + } + + if ((netstat = fopen("/proc/net/udp", "r")) != NULL) { + num_udp = get_num_sockets(netstat); + fclose(netstat); + } + + if ((netstat = fopen("/proc/net/unix", "r")) != NULL) { + num_unix = get_num_sockets(netstat); + fclose(netstat); + } + if ((netstat = fopen("/proc/net/raw", "r")) != NULL) { + num_raw = get_num_sockets(netstat); + fclose(netstat); + } + + NetStat_timeStamp = time(0); + return 0; +} + +int +updateNetStatTcpUdpRaw(const char *cmd) +{ + FILE *netstat; + char buffer[1024]; + uint local_addr, local_port; + uint remote_addr, remote_port; + int uid, i; + uint state; + SocketInfo *socket_info; + + if (strstr(cmd, "tcp")) { + snprintf(buffer, sizeof(buffer), "/proc/net/tcp"); + for (i = level_ctnr(TcpSocketList); i >= 0; --i) + free(pop_ctnr(TcpSocketList)); + } + + if (strstr(cmd, "udp")) { + snprintf(buffer, sizeof(buffer), "/proc/net/udp"); + for (i = level_ctnr(UdpSocketList); i >= 0; --i) + free(pop_ctnr(UdpSocketList)); + } + + if (strstr(cmd, "raw")) { + snprintf(buffer, sizeof(buffer), "/proc/net/raw"); + for (i = level_ctnr(RawSocketList); i >= 0; --i) + free(pop_ctnr(RawSocketList)); + } + + if ((netstat = fopen(buffer, "r")) == NULL) { + print_error("Cannot open \'%s\'!\n" + "The kernel needs to be compiled with support\n" + "for /proc filesystem enabled!\n", buffer); + return -1; + } + + fgets(buffer, sizeof(buffer), netstat); + + while (fgets(buffer, sizeof(buffer), netstat) != NULL) { + if (strcmp(buffer, "")) { + sscanf(buffer, "%*d: %x:%x %x:%x %x %*x:%*x %*x:%*x %d", + &local_addr, &local_port, + &remote_addr, &remote_port, + &state, + &uid); + + if ((socket_info = (SocketInfo *)malloc(sizeof(SocketInfo))) == NULL) { + continue; + } + strlcpy(socket_info->local_addr, get_host_name(local_addr), sizeof(socket_info->local_addr)); + strlcpy(socket_info->remote_addr, get_host_name(remote_addr), sizeof(socket_info->remote_addr)); + + if (strstr(cmd, "tcp")) { + strlcpy(socket_info->local_port, get_serv_name(local_port, "tcp"), sizeof(socket_info->local_port)); + strlcpy(socket_info->remote_port, get_serv_name(remote_port, "tcp"), sizeof(socket_info->remote_port)); + strlcpy(socket_info->state, conn_state[state], sizeof(socket_info->state)); + socket_info->uid = uid; + + push_ctnr(TcpSocketList, socket_info); + } + + if (strstr(cmd, "udp")) { + strlcpy(socket_info->local_port, get_serv_name(local_port, "udp"), sizeof(socket_info->local_port)); + strlcpy(socket_info->remote_port, get_serv_name(remote_port, "udp"), sizeof(socket_info->remote_port)); + strlcpy(socket_info->state, conn_state[state], sizeof(socket_info->state)); + socket_info->uid = uid; + + push_ctnr(UdpSocketList, socket_info); + } + + if (strstr(cmd, "raw")) { + strlcpy(socket_info->local_port, get_proto_name(local_port), sizeof(socket_info->local_port)); + strlcpy(socket_info->remote_port, get_proto_name(remote_port), sizeof(socket_info->remote_port)); + snprintf(socket_info->state, sizeof(socket_info->state)-1, "%d", state); + socket_info->uid = uid; + + push_ctnr(RawSocketList, socket_info); + } + } + } + fclose(netstat); + TcpUdpRaw_timeStamp = time(0); + + return 0; +} + +int +updateNetStatUnix(void) +{ + FILE *file; + char buffer[1024]; + char path[256]; + int ref_count, type, state, inode, i; + UnixInfo *unix_info; + + if ((file = fopen("/proc/net/unix", "r")) == NULL) { + print_error("Cannot open \'/proc/net/unix\'!\n" + "The kernel needs to be compiled with support\n" + "for /proc filesystem enabled!\n"); + return -1; + } + + for (i = level_ctnr(UnixSocketList); i >= 0; --i) + free(pop_ctnr(UnixSocketList)); + + fgets(buffer, sizeof(buffer), file); + + while (fgets(buffer, sizeof(buffer), file) != NULL) { + if (strcmp(buffer, "")) { + sscanf(buffer, "%*x: %d %*d %*d %d %d %d %255s", + &ref_count, &type, &state, &inode, path); + + if ((unix_info = (UnixInfo *)malloc(sizeof(UnixInfo))) == NULL) { + continue; + } + + unix_info->refcount = ref_count; + strlcpy(unix_info->type, raw_type[type], sizeof(unix_info->type)); + strlcpy(unix_info->state, raw_state[state], sizeof(unix_info->state)); + unix_info->inode = inode; + strlcpy(unix_info->path, path, sizeof(unix_info->path)); + + push_ctnr(UnixSocketList, unix_info); + } + } + fclose(file); + Unix_timeStamp = time(0); + + return 0; +} + +void +printNetStat(const char* cmd) +{ + if ((time(0) - NetStat_timeStamp) >= UPDATEINTERVAL) + updateNetStat(); + + if (strstr(cmd, "tcp") != NULL) + fprintf(CurrentClient, "%d\n", num_tcp); + if (strstr(cmd, "udp") != NULL) + fprintf(CurrentClient, "%d\n", num_udp); + if (strstr(cmd, "unix") != NULL) + fprintf(CurrentClient, "%d\n", num_unix); + if (strstr(cmd, "raw") != NULL) + fprintf(CurrentClient, "%d\n", num_raw); +} + +void +printNetStatInfo(const char* cmd) +{ + if (strstr(cmd, "tcp") != NULL) + fprintf(CurrentClient, "Number of TCP-Sockets\t0\t0\tSockets\n"); + if (strstr(cmd, "udp") != NULL) + fprintf(CurrentClient, "Number of UDP-Sockets\t0\t0\tSockets\n"); + if (strstr(cmd, "unix") != NULL) + fprintf(CurrentClient, "Number of UnixDomain-Sockets\t0\t0\tSockets\n"); + if (strstr(cmd, "raw") != NULL) + fprintf(CurrentClient, "Number of Raw-Sockets\t0\t0\tSockets\n"); +} + +void +printNetStatTcpUdpRaw(const char *cmd) +{ + SocketInfo* socket_info; + + if (strstr(cmd, "tcp")) { + if ((time(0) - TcpUdpRaw_timeStamp) >= UPDATEINTERVAL) + updateNetStatTcpUdpRaw("tcp"); + + for (socket_info = first_ctnr(TcpSocketList); socket_info; socket_info = next_ctnr(TcpSocketList)) + printSocketInfo(socket_info); + + if (level_ctnr(TcpSocketList) == 0) + fprintf(CurrentClient, "\n"); + } + + if (strstr(cmd, "udp")) { + if ((time(0) - TcpUdpRaw_timeStamp) >= UPDATEINTERVAL) + updateNetStatTcpUdpRaw("udp"); + + for (socket_info = first_ctnr(UdpSocketList); socket_info; socket_info = next_ctnr(UdpSocketList)) + printSocketInfo(socket_info); + + if (level_ctnr(UdpSocketList) == 0) + fprintf(CurrentClient, "\n"); + } + + if (strstr(cmd, "raw")) { + if ((time(0) - TcpUdpRaw_timeStamp) >= UPDATEINTERVAL) + updateNetStatTcpUdpRaw("raw"); + + for (socket_info = first_ctnr(RawSocketList); socket_info; socket_info = next_ctnr(RawSocketList)) + printSocketInfo(socket_info); + + if (level_ctnr(RawSocketList) == 0) + fprintf(CurrentClient, "\n"); + } +} + +void +printNetStatTcpUdpRawInfo(const char *cmd) +{ + (void) cmd; + fprintf(CurrentClient, "Local Address\tPort\tForeign Address\tPort\tState\tUID\ns\ts\ts\ts\ts\td\n"); +} + +void printNetStatUnix(const char *cmd) +{ + UnixInfo* unix_info; + + (void) cmd; + if ((time(0) - Unix_timeStamp) >= UPDATEINTERVAL) + updateNetStatUnix(); + + for (unix_info = first_ctnr(UnixSocketList); unix_info; unix_info = next_ctnr(UnixSocketList)) { + fprintf(CurrentClient, "%d\t%s\t%s\t%d\t%s\n", + unix_info->refcount, + unix_info->type, + unix_info->state, + unix_info->inode, + unix_info->path); + } + + if (level_ctnr(UnixSocketList) == 0) + fprintf(CurrentClient, "\n"); +} + +void printNetStatUnixInfo(const char *cmd) +{ + (void) cmd; + fprintf(CurrentClient, "RefCount\tType\tState\tInode\tPath\nd\ts\ts\td\ts\n"); +} diff --git a/ksysguard/ksysguardd/Linux/netstat.h b/ksysguard/ksysguardd/Linux/netstat.h new file mode 100644 index 000000000..be0c3a850 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/netstat.h @@ -0,0 +1,39 @@ +/* + KSysGuard, the KDE System Guard + + Copyright (c) 2001 Tobias Koenig <tokoe@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. + +*/ + +#ifndef _netstat_h_ +#define _netstat_h_ + +void initNetStat(struct SensorModul* sm); +void exitNetStat(void); + +int updateNetStat(void); +int updateNetStatTcpUdpRaw(const char* cmd); +int updateNetStatUnix(void); + +void printNetStat(const char* cmd); +void printNetStatInfo(const char* cmd); + +void printNetStatTcpUdpRaw(const char *cmd); +void printNetStatTcpUdpRawInfo(const char *cmd); + +void printNetStatUnix(const char *cmd); +void printNetStatUnixInfo(const char *cmd); +#endif 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 ); + } +} diff --git a/ksysguard/ksysguardd/Linux/stat.h b/ksysguard/ksysguardd/Linux/stat.h new file mode 100644 index 000000000..9107d3aa3 --- /dev/null +++ b/ksysguard/ksysguardd/Linux/stat.h @@ -0,0 +1,66 @@ +/* + 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. + +*/ + +#ifndef KSG_STAT_H +#define KSG_STAT_H + +void initStat( struct SensorModul* ); +void exitStat( void ); + +int updateStat( void ); + +void printCPUUser( const char* ); +void printCPUUserInfo( const char* ); +void printCPUNice( const char* ); +void printCPUNiceInfo( const char* ); +void printCPUSys( const char* ); +void printCPUSysInfo( const char* ); +void printCPUIdle( const char* ); +void printCPUIdleInfo( const char* ); +void printCPUxUser( const char* ); +void printCPUxUserInfo( const char* ); +void printCPUxNice( const char* ); +void printCPUxNiceInfo( const char* ); +void printCPUxSys( const char* ); +void printCPUxSysInfo( const char* ); +void printCPUxIdle( const char* ); +void printCPUxIdleInfo( const char* ); +void printDiskTotal( const char* ); +void printDiskTotalInfo( const char* ); +void printDiskRIO( const char* ); +void printDiskRIOInfo( const char* ); +void printDiskWIO( const char* ); +void printDiskWIOInfo( const char* ); +void printDiskRBlk( const char* ); +void printDiskRBlkInfo( const char* ); +void printDiskWBlk( const char* ); +void printDiskWBlkInfo( const char* ); +void printPageIn( const char* ); +void printPageInInfo( const char* ); +void printPageOut( const char* ); +void printPageOutInfo( const char* ); +void printInterruptx( const char* ); +void printInterruptxInfo( const char* ); +void printCtxt( const char* ); +void printCtxtInfo( const char* ); +void printDiskIO( const char* ); +void printDiskIOInfo( const char* ); + +#endif |