summaryrefslogtreecommitdiffstats
path: root/ksysguard/ksysguardd/ksysguardd.c
diff options
context:
space:
mode:
Diffstat (limited to 'ksysguard/ksysguardd/ksysguardd.c')
-rw-r--r--ksysguard/ksysguardd/ksysguardd.c633
1 files changed, 633 insertions, 0 deletions
diff --git a/ksysguard/ksysguardd/ksysguardd.c b/ksysguard/ksysguardd/ksysguardd.c
new file mode 100644
index 000000000..faea19492
--- /dev/null
+++ b/ksysguard/ksysguardd/ksysguardd.c
@@ -0,0 +1,633 @@
+/*
+ KSysGuard, the KDE System Guard
+
+ Copyright (c) 1999 - 2003 Chris Schlaeger <cs@kde.org>
+ Tobias Koenig <tokoe@kde.org>
+
+ Solaris support by Torsten Kasch <tk@Genetik.Uni-Bielefeld.DE>
+
+ 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 <ctype.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+#include <../version.h>
+#ifdef HAVE_DNSSD
+#include <dns_sd.h>
+#endif
+#include "modules.h"
+
+#include "ksysguardd.h"
+
+#define CMDBUFSIZE 128
+#define MAX_CLIENTS 100
+
+typedef struct {
+ int socket;
+ FILE* out;
+} ClientInfo;
+
+static int ServerSocket;
+static ClientInfo ClientList[ MAX_CLIENTS ];
+static int SocketPort = -1;
+static unsigned char BindToAllInterfaces = 0;
+static int CurrentSocket;
+static const char *LockFile = "/var/run/ksysguardd.pid";
+static const char *ConfigFile = KSYSGUARDDRCFILE;
+#ifdef HAVE_DNSSD
+static int ServiceSocket = -1;
+static DNSServiceRef Ref;
+#endif
+
+void signalHandler( int sig );
+void makeDaemon( void );
+void resetClientList( void );
+int addClient( int client );
+int delClient( int client );
+int createServerSocket( void );
+#ifdef HAVE_DNSSD
+void publish_callback (DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name,
+ const char*, const char*, void *context);
+#endif
+
+/**
+ This variable is set to 1 if a module requests that the daemon should
+ be terminated.
+ */
+int QuitApp = 0;
+
+/**
+ This variable indicates whether we are running as daemon or (1) or if
+ we were have a controlling shell.
+ */
+int RunAsDaemon = 0;
+
+/**
+ This pointer is used by all modules. It contains the file pointer of
+ the currently served client. This is stdout for non-daemon mode.
+ */
+FILE* CurrentClient = 0;
+
+static int processArguments( int argc, char* argv[] )
+{
+ int option;
+
+ opterr = 0;
+ while ( ( option = getopt( argc, argv, "-p:f:dih" ) ) != EOF ) {
+ switch ( tolower( option ) ) {
+ case 'p':
+ SocketPort = atoi( optarg );
+ break;
+ case 'f':
+ ConfigFile = strdup( optarg );
+ break;
+ case 'd':
+ RunAsDaemon = 1;
+ break;
+ case 'i':
+ BindToAllInterfaces = 1;
+ break;
+ case '?':
+ case 'h':
+ default:
+ fprintf(stderr, "Usage: %s [-d] [-i] [-p port]\n", argv[ 0 ] );
+ return -1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void printWelcome( FILE* out )
+{
+ fprintf( out, "ksysguardd %s\n"
+ "(c) 1999, 2000, 2001, 2002 Chris Schlaeger <cs@kde.org> and\n"
+ "(c) 2001 Tobias Koenig <tokoe@kde.org>\n"
+ "This program is part of the KDE Project and licensed under\n"
+ "the GNU GPL version 2. See http://www.kde.org for details.\n",
+ KSYSGUARD_VERSION );
+
+ fflush( out );
+}
+
+static int createLockFile()
+{
+ FILE *file;
+
+ if ( ( file = fopen( LockFile, "w+" ) ) != NULL ) {
+ struct flock lock;
+ lock.l_type = F_WRLCK;
+ lock.l_whence = 0;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ lock.l_pid = -1;
+ if ( fcntl( fileno( file ), F_SETLK, &lock ) < 0 ) {
+ if ( ( errno == EACCES ) || ( errno == EAGAIN ) ) {
+ log_error( "ksysguardd is running already" );
+ fprintf( stderr, "ksysguardd is running already\n" );
+ fclose( file );
+ return -1;
+ }
+ }
+
+ fseek( file, 0, SEEK_SET );
+ fprintf( file, "%d\n", getpid() );
+ fflush( file );
+ ftruncate( fileno( file ), ftell( file ) );
+ } else {
+ log_error( "Cannot create lockfile '%s'", LockFile );
+ fprintf( stderr, "Cannot create lockfile '%s'\n", LockFile );
+ return -2;
+ }
+
+ /**
+ We abandon 'file' here on purpose. It's needed nowhere else, but we
+ have to keep the file open and locked. The kernel will remove the
+ lock when the process terminates and the runlevel scripts has to
+ remove the pid file.
+ */
+ return 0;
+}
+
+void signalHandler( int sig )
+{
+ switch ( sig ) {
+ case SIGQUIT:
+ case SIGTERM:
+#ifdef HAVE_DNSSD
+ if ( ServiceSocket != -1 ) DNSServiceRefDeallocate(Ref);
+#endif
+ exit( 0 );
+ break;
+ }
+}
+
+static void installSignalHandler( void )
+{
+ struct sigaction Action;
+
+ Action.sa_handler = signalHandler;
+ sigemptyset( &Action.sa_mask );
+ /* make sure that interrupted system calls are restarted. */
+ Action.sa_flags = SA_RESTART;
+ sigaction( SIGTERM, &Action, 0 );
+ sigaction( SIGQUIT, &Action, 0 );
+}
+
+static void dropPrivileges( void )
+{
+ struct passwd *pwd;
+
+ if ( ( pwd = getpwnam( "nobody" ) ) != NULL ) {
+ if ( !setgid(pwd->pw_gid) )
+ setuid(pwd->pw_uid);
+ if (!geteuid() && getuid() != pwd->pw_uid)
+ _exit(1);
+ }
+ else {
+ log_error( "User 'nobody' does not exist." );
+ /**
+ We exit here to avoid becoming vulnerable just because
+ user nobody does not exist.
+ */
+ _exit(1);
+ }
+}
+
+void makeDaemon( void )
+{
+ int fd = -1;
+ switch ( fork() ) {
+ case -1:
+ log_error( "fork() failed" );
+ break;
+ case 0:
+ setsid();
+ chdir( "/" );
+ umask( 0 );
+ if ( createLockFile() < 0 )
+ _exit( 1 );
+
+ dropPrivileges();
+ installSignalHandler();
+
+ fd = open("/dev/null", O_RDWR, 0);
+ if (fd != -1) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close (fd);
+ }
+ break;
+ default:
+ exit( 0 );
+ }
+}
+
+static int readCommand( int fd, char* cmdBuf, size_t len )
+{
+ unsigned int i;
+ char c;
+ for ( i = 0; i < len; ++i )
+ {
+ int result = read( fd, &c, 1 );
+ if (result < 0)
+ return -1; /* Error */
+
+ if (result == 0) {
+ if (i == 0)
+ return -1; /* Connection lost */
+
+ break; /* End of data */
+ }
+
+ if (c == '\n')
+ break; /* End of line */
+
+ cmdBuf[ i ] = c;
+ }
+ cmdBuf[i] = '\0';
+
+ return i;
+}
+
+void resetClientList( void )
+{
+ int i;
+
+ for ( i = 0; i < MAX_CLIENTS; i++ ) {
+ ClientList[ i ].socket = -1;
+ ClientList[ i ].out = 0;
+ }
+}
+
+/**
+ addClient adds a new client to the ClientList.
+ */
+int addClient( int client )
+{
+ int i;
+ FILE* out;
+
+ for ( i = 0; i < MAX_CLIENTS; i++ ) {
+ if ( ClientList[ i ].socket == -1 ) {
+ ClientList[ i ].socket = client;
+ if ( ( out = fdopen( client, "w+" ) ) == NULL ) {
+ log_error( "fdopen()" );
+ return -1;
+ }
+ /* We use unbuffered IO */
+ fcntl( fileno( out ), F_SETFL, O_NDELAY );
+ ClientList[ i ].out = out;
+ printWelcome( out );
+ fprintf( out, "ksysguardd> " );
+ fflush( out );
+
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ delClient removes a client from the ClientList.
+ */
+int delClient( int client )
+{
+ int i;
+
+ for ( i = 0; i < MAX_CLIENTS; i++ ) {
+ if ( ClientList[i].socket == client ) {
+ fclose( ClientList[ i ].out );
+ ClientList[ i ].out = 0;
+ close( ClientList[ i ].socket );
+ ClientList[ i ].socket = -1;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+#ifdef HAVE_DNSSD
+void publish_callback (DNSServiceRef ref, DNSServiceFlags f, DNSServiceErrorType errorCode, const char *name,
+ const char* type, const char* domain, void *context)
+{
+ if (errorCode != kDNSServiceErr_NoError) log_error("Publishing DNS-SD service failed with error %i",errorCode);
+}
+#endif
+
+
+int createServerSocket()
+{
+ int i = 1;
+ int newSocket;
+ struct sockaddr_in s_in;
+ struct servent *service;
+
+ if ( ( newSocket = socket( PF_INET, SOCK_STREAM, 0 ) ) < 0 ) {
+ log_error( "socket()" );
+ return -1;
+ }
+
+ setsockopt( newSocket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) );
+
+ /**
+ The -p command line option always overrides the default or the
+ service entry.
+ */
+ if ( SocketPort == -1 ) {
+ if ( ( service = getservbyname( "ksysguardd", "tcp" ) ) == NULL ) {
+ /**
+ No entry in service directory and no command line request,
+ so we take the build-in default (the offical IANA port).
+ */
+ SocketPort = PORT_NUMBER;
+ } else
+ SocketPort = htons( service->s_port );
+ }
+
+ memset( &s_in, 0, sizeof( struct sockaddr_in ) );
+ s_in.sin_family = AF_INET;
+ if ( BindToAllInterfaces )
+ s_in.sin_addr.s_addr = htonl( INADDR_ANY );
+ else
+ s_in.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
+ s_in.sin_port = htons( SocketPort );
+
+ if ( bind( newSocket, (struct sockaddr*)&s_in, sizeof( s_in ) ) < 0 ) {
+ log_error( "Cannot bind to port %d", SocketPort );
+ return -1;
+ }
+
+ if ( listen( newSocket, 5 ) < 0 ) {
+ log_error( "listen()" );
+ return -1;
+ }
+
+#ifdef HAVE_DNSSD
+ if ( BindToAllInterfaces )
+ if (DNSServiceRegister(&Ref, 0, 0, 0, "_ksysguard._tcp", RegisterDomain ?
+ RegisterDomain : "local.",NULL, htons(SocketPort), 0, 0, publish_callback, 0) == kDNSServiceErr_NoError)
+ ServiceSocket = DNSServiceRefSockFD(Ref);
+#endif
+
+ return newSocket;
+}
+
+static int setupSelect( fd_set* fds )
+{
+ int highestFD = ServerSocket;
+ FD_ZERO( fds );
+ /**
+ Fill the filedescriptor array with all relevant descriptors. If we
+ not in daemon mode we only need to watch stdin.
+ */
+ if ( RunAsDaemon ) {
+ int i;
+ FD_SET( ServerSocket, fds );
+#ifdef HAVE_DNSSD
+ if ( ServiceSocket != -1 ) {
+ FD_SET( ServiceSocket, fds );
+ if ( highestFD < ServiceSocket) highestFD = ServiceSocket;
+ }
+#endif
+
+ for ( i = 0; i < MAX_CLIENTS; i++ ) {
+ if ( ClientList[ i ].socket != -1 ) {
+ FD_SET( ClientList[ i ].socket, fds );
+ if ( highestFD < ClientList[ i ].socket )
+ highestFD = ClientList[ i ].socket;
+ }
+ }
+ } else {
+ FD_SET( STDIN_FILENO, fds );
+ if ( highestFD < STDIN_FILENO )
+ highestFD = STDIN_FILENO;
+ }
+
+ return highestFD;
+}
+
+static void checkModules()
+{
+ struct SensorModul *entry;
+
+ for ( entry = SensorModulList; entry->configName != NULL; entry++ )
+ if ( entry->checkCommand != NULL && entry->available )
+ entry->checkCommand();
+}
+
+static void handleTimerEvent( struct timeval* tv, struct timeval* last )
+{
+ struct timeval now;
+ gettimeofday( &now, NULL );
+ /* Check if the last event was really TIMERINTERVAL seconds ago */
+ if ( now.tv_sec - last->tv_sec >= TIMERINTERVAL ) {
+ /* If so, update all sensors and save current time to last. */
+ checkModules();
+ *last = now;
+ }
+ /**
+ Set tv so that the next timer event will be generated in
+ TIMERINTERVAL seconds.
+ */
+ tv->tv_usec = last->tv_usec - now.tv_usec;
+ if ( tv->tv_usec < 0 ) {
+ tv->tv_usec += 1000000;
+ tv->tv_sec = last->tv_sec + TIMERINTERVAL - 1 - now.tv_sec;
+ } else
+ tv->tv_sec = last->tv_sec + TIMERINTERVAL - now.tv_sec;
+}
+
+static void handleSocketTraffic( int socketNo, const fd_set* fds )
+{
+ char cmdBuf[ CMDBUFSIZE ];
+
+ if ( RunAsDaemon ) {
+ int i;
+
+ if ( FD_ISSET( socketNo, fds ) ) {
+ int clientsocket;
+ struct sockaddr addr;
+ kde_socklen_t addr_len = sizeof( struct sockaddr );
+
+ /* a new connection */
+ if ( ( clientsocket = accept( socketNo, &addr, &addr_len ) ) < 0 ) {
+ log_error( "accept()" );
+ exit( 1 );
+ } else
+ addClient( clientsocket );
+ }
+
+#ifdef HAVE_DNSSD
+ if ( ServiceSocket != -1 && FD_ISSET( ServiceSocket, fds )) DNSServiceProcessResult(Ref);
+#endif
+
+ for ( i = 0; i < MAX_CLIENTS; i++ ) {
+ if ( ClientList[ i ].socket != -1 ) {
+ CurrentSocket = ClientList[ i ].socket;
+ if ( FD_ISSET( ClientList[ i ].socket, fds ) ) {
+ ssize_t cnt;
+ if ( ( cnt = readCommand( CurrentSocket, cmdBuf, sizeof( cmdBuf ) - 1 ) ) <= 0 )
+ delClient( CurrentSocket );
+ else {
+ cmdBuf[ cnt ] = '\0';
+ if ( strncmp( cmdBuf, "quit", 4 ) == 0 )
+ delClient( CurrentSocket );
+ else {
+ CurrentClient = ClientList[ i ].out;
+ fflush( stdout );
+ executeCommand( cmdBuf );
+ fprintf( CurrentClient, "ksysguardd> " );
+ fflush( CurrentClient );
+ }
+ }
+ }
+ }
+ }
+ } else if ( FD_ISSET( STDIN_FILENO, fds ) ) {
+ if (readCommand( STDIN_FILENO, cmdBuf, sizeof( cmdBuf ) ) < 0) {
+ exit(0);
+ }
+ executeCommand( cmdBuf );
+ printf( "ksysguardd> " );
+ fflush( stdout );
+ }
+}
+
+static void initModules()
+{
+ struct SensorModul *entry;
+
+ /* initialize all sensors */
+ initCommand();
+
+ for ( entry = SensorModulList; entry->configName != NULL; entry++ ) {
+ if ( entry->initCommand != NULL && sensorAvailable( entry->configName ) ) {
+ entry->available = 1;
+ entry->initCommand( entry );
+ }
+ }
+
+ ReconfigureFlag = 0;
+}
+
+static void exitModules()
+{
+ struct SensorModul *entry;
+
+ for ( entry = SensorModulList; entry->configName != NULL; entry++ ) {
+ if ( entry->exitCommand != NULL && entry->available )
+ entry->exitCommand();
+ }
+
+ exitCommand();
+}
+
+/*
+================================ public part =================================
+*/
+
+int main( int argc, char* argv[] )
+{
+ fd_set fds;
+ struct timeval tv;
+ struct timeval last;
+
+#ifdef OSTYPE_FreeBSD
+ /**
+ If we are not root or the executable does not belong to the
+ kmem group, ksysguardd will crash because of permission problems
+ for opening /dev/kmem
+ */
+ struct group* grentry = NULL;
+
+ if ( geteuid() != 0 ) {
+ grentry = getgrnam( "kmem" );
+ if ( grentry == NULL ) {
+ fprintf( stderr, "the group kmem is missing on your system\n" );
+ return -1;
+ }
+
+ if ( getegid() != grentry->gr_gid ) {
+ fprintf( stderr, "ksysguardd can't be started because of permission conflicts!\n"
+ "Start the program as user 'root' or change its group to 'kmem' and set the sgid-bit\n" );
+ return -1;
+ }
+
+ endgrent();
+ }
+#endif
+
+ printWelcome( stdout );
+
+ if ( processArguments( argc, argv ) < 0 )
+ return -1;
+
+ parseConfigFile( ConfigFile );
+
+ initModules();
+
+ if ( RunAsDaemon ) {
+ makeDaemon();
+
+ if ( ( ServerSocket = createServerSocket() ) < 0 )
+ return -1;
+ resetClientList();
+ } else {
+ fprintf( stdout, "ksysguardd> " );
+ fflush( stdout );
+ CurrentClient = stdout;
+ ServerSocket = 0;
+ }
+
+ tv.tv_sec = TIMERINTERVAL;
+ tv.tv_usec = 0;
+ gettimeofday( &last, NULL );
+
+ while ( !QuitApp ) {
+ int highestFD = setupSelect( &fds );
+ /* wait for communication or timeouts */
+ if ( select( highestFD + 1, &fds, NULL, NULL, &tv ) >= 0 ) {
+ handleTimerEvent( &tv, &last );
+ handleSocketTraffic( ServerSocket, &fds );
+ }
+ }
+
+ exitModules();
+
+ freeConfigFile();
+
+ return 0;
+}