2
KSysGuard, the KDE System Guard
4
Copyright (c) 1999 - 2003 Chris Schlaeger <cs@kde.org>
5
Tobias Koenig <tokoe@kde.org>
6
2006 Greg Martyn <greg.martyn@gmail.com>
8
Solaris support by Torsten Kasch <tk@Genetik.Uni-Bielefeld.DE>
10
This program is free software; you can redistribute it and/or
11
modify it under the terms of version 2 of the GNU General Public
12
License as published by the Free Software Foundation.
14
This program is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
GNU General Public License for more details.
19
You should have received a copy of the GNU General Public License
20
along with this program; if not, write to the Free Software
21
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25
#include <config-workspace.h>
29
#include <netinet/in.h>
36
#include <sys/socket.h>
39
#include <sys/types.h>
43
#include <../version.h>
47
#include "ksysguardd.h"
49
#define CMDBUFSIZE 128
50
#define MAX_CLIENTS 100
57
static int ServerSocket;
58
static ClientInfo ClientList[ MAX_CLIENTS ];
59
static int SocketPort = -1;
60
static unsigned char BindToAllInterfaces = 0;
61
static int CurrentSocket;
62
static const char *LockFile = "/var/run/ksysguardd.pid";
63
static const char *ConfigFile = KSYSGUARDDRCFILE;
65
void signalHandler( int sig );
66
void makeDaemon( void );
67
void resetClientList( void );
68
int addClient( int client );
69
int delClient( int client );
70
int createServerSocket( void );
73
This variable is set to 1 if a module requests that the daemon should
79
This variable indicates whether we are running as daemon or (1) or if
80
we were have a controlling shell.
85
This pointer is used by all modules. It contains the file pointer of
86
the currently served client. This is stdout for non-daemon mode.
88
FILE* CurrentClient = 0;
90
static int processArguments( int argc, char* argv[] )
95
while ( ( option = getopt( argc, argv, "-p:f:dih" ) ) != EOF ) {
96
switch ( tolower( option ) ) {
98
SocketPort = atoi( optarg );
101
ConfigFile = strdup( optarg );
107
BindToAllInterfaces = 1;
112
fprintf(stderr, "Usage: %s [-d] [-i] [-p port]\n", argv[ 0 ] );
121
static void printWelcome( FILE* out )
123
fprintf( out, "ksysguardd %s\n"
124
"(c) 1999, 2000, 2001, 2002 Chris Schlaeger <cs@kde.org>\n"
125
"(c) 2001 Tobias Koenig <tokoe@kde.org>\n"
126
"(c) 2006 Greg Martyn <greg.martyn@gmail.com>\n"
127
"This program is part of the KDE Project and licensed under\n"
128
"the GNU GPL version 2. See http://www.kde.org for details.\n",
134
static int createLockFile()
138
if ( ( file = fopen( LockFile, "w+" ) ) != NULL ) {
140
lock.l_type = F_WRLCK;
145
if ( fcntl( fileno( file ), F_SETLK, &lock ) < 0 ) {
146
if ( ( errno == EACCES ) || ( errno == EAGAIN ) ) {
147
log_error( "ksysguardd is running already" );
148
fprintf( stderr, "ksysguardd is running already\n" );
154
fseek( file, 0, SEEK_SET );
155
fprintf( file, "%d\n", getpid() );
157
ftruncate( fileno( file ), ftell( file ) );
159
log_error( "Cannot create lockfile '%s'", LockFile );
160
fprintf( stderr, "Cannot create lockfile '%s'\n", LockFile );
165
We abandon 'file' here on purpose. It's needed nowhere else, but we
166
have to keep the file open and locked. The kernel will remove the
167
lock when the process terminates and the runlevel scripts has to
173
void signalHandler( int sig )
178
/* Not really anything to do here at the moment. */
184
static void installSignalHandler( void )
186
struct sigaction Action;
188
Action.sa_handler = signalHandler;
189
sigemptyset( &Action.sa_mask );
190
/* make sure that interrupted system calls are restarted. */
191
Action.sa_flags = SA_RESTART;
192
sigaction( SIGTERM, &Action, 0 );
193
sigaction( SIGQUIT, &Action, 0 );
196
static void dropPrivileges( void )
200
if ( ( pwd = getpwnam( "nobody" ) ) != NULL ) {
201
if ( !setgid(pwd->pw_gid) )
203
if (!geteuid() && getuid() != pwd->pw_uid)
207
log_error( "User 'nobody' does not exist." );
209
We exit here to avoid becoming vulnerable just because
210
user nobody does not exist.
216
void makeDaemon( void )
221
log_error( "fork() failed" );
227
if ( createLockFile() < 0 )
231
installSignalHandler();
233
fd = open("/dev/null", O_RDWR, 0);
235
dup2(fd, STDIN_FILENO);
236
dup2(fd, STDOUT_FILENO);
237
dup2(fd, STDERR_FILENO);
246
static int readCommand( int fd, char* cmdBuf, size_t len )
250
for ( i = 0; i < len; ++i )
252
int result = read( fd, &c, 1 );
254
return -1; /* Error */
258
return -1; /* Connection lost */
260
break; /* End of data */
264
break; /* End of line */
273
void resetClientList( void )
277
for ( i = 0; i < MAX_CLIENTS; i++ ) {
278
ClientList[ i ].socket = -1;
279
ClientList[ i ].out = 0;
284
addClient adds a new client to the ClientList.
286
int addClient( int client )
291
for ( i = 0; i < MAX_CLIENTS; i++ ) {
292
if ( ClientList[ i ].socket == -1 ) {
293
ClientList[ i ].socket = client;
294
if ( ( out = fdopen( client, "w+" ) ) == NULL ) {
295
log_error( "fdopen()" );
298
/* We use unbuffered IO */
299
fcntl( fileno( out ), F_SETFL, O_NDELAY );
300
ClientList[ i ].out = out;
302
fprintf( out, "ksysguardd> " );
313
delClient removes a client from the ClientList.
315
int delClient( int client )
319
for ( i = 0; i < MAX_CLIENTS; i++ ) {
320
if ( ClientList[i].socket == client ) {
321
fclose( ClientList[ i ].out );
322
ClientList[ i ].out = 0;
323
close( ClientList[ i ].socket );
324
ClientList[ i ].socket = -1;
332
int createServerSocket()
336
struct sockaddr_in s_in;
337
struct servent *service;
339
if ( ( newSocket = socket( PF_INET, SOCK_STREAM, 0 ) ) < 0 ) {
340
log_error( "socket()" );
344
setsockopt( newSocket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) );
347
The -p command line option always overrides the default or the
350
if ( SocketPort == -1 ) {
351
if ( ( service = getservbyname( "ksysguardd", "tcp" ) ) == NULL ) {
353
No entry in service directory and no command line request,
354
so we take the build-in default (the official IANA port).
356
SocketPort = PORT_NUMBER;
358
SocketPort = htons( service->s_port );
361
memset( &s_in, 0, sizeof( struct sockaddr_in ) );
362
s_in.sin_family = AF_INET;
363
if ( BindToAllInterfaces )
364
s_in.sin_addr.s_addr = htonl( INADDR_ANY );
366
s_in.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
367
s_in.sin_port = htons( SocketPort );
369
if ( bind( newSocket, (struct sockaddr*)&s_in, sizeof( s_in ) ) < 0 ) {
370
log_error( "Cannot bind to port %d", SocketPort );
374
if ( listen( newSocket, 5 ) < 0 ) {
375
log_error( "listen()" );
382
static int setupSelect( fd_set* fds )
384
int highestFD = ServerSocket;
387
Fill the filedescriptor array with all relevant descriptors. If we
388
not in daemon mode we only need to watch stdin.
392
FD_SET( ServerSocket, fds );
394
for ( i = 0; i < MAX_CLIENTS; i++ ) {
395
if ( ClientList[ i ].socket != -1 ) {
396
FD_SET( ClientList[ i ].socket, fds );
397
if ( highestFD < ClientList[ i ].socket )
398
highestFD = ClientList[ i ].socket;
402
FD_SET( STDIN_FILENO, fds );
403
if ( highestFD < STDIN_FILENO )
404
highestFD = STDIN_FILENO;
410
static void checkModules()
412
struct SensorModul *entry;
414
for ( entry = SensorModulList; entry->configName != NULL; entry++ )
415
if ( entry->checkCommand != NULL && entry->available )
416
entry->checkCommand();
419
static void handleTimerEvent( struct timeval* tv, struct timeval* last )
422
gettimeofday( &now, NULL );
423
/* Check if the last event was really TIMERINTERVAL seconds ago */
424
if ( now.tv_sec - last->tv_sec >= TIMERINTERVAL ) {
425
/* If so, update all sensors and save current time to last. */
430
Set tv so that the next timer event will be generated in
431
TIMERINTERVAL seconds.
433
tv->tv_usec = last->tv_usec - now.tv_usec;
434
if ( tv->tv_usec < 0 ) {
435
tv->tv_usec += 1000000;
436
tv->tv_sec = last->tv_sec + TIMERINTERVAL - 1 - now.tv_sec;
438
tv->tv_sec = last->tv_sec + TIMERINTERVAL - now.tv_sec;
441
static void handleSocketTraffic( int socketNo, const fd_set* fds )
443
char cmdBuf[ CMDBUFSIZE ];
448
if ( FD_ISSET( socketNo, fds ) ) {
450
struct sockaddr addr;
451
kde_socklen_t addr_len = sizeof( struct sockaddr );
453
/* a new connection */
454
if ( ( clientsocket = accept( socketNo, &addr, &addr_len ) ) < 0 ) {
455
log_error( "accept()" );
458
addClient( clientsocket );
461
for ( i = 0; i < MAX_CLIENTS; i++ ) {
462
if ( ClientList[ i ].socket != -1 ) {
463
CurrentSocket = ClientList[ i ].socket;
464
if ( FD_ISSET( ClientList[ i ].socket, fds ) ) {
466
if ( ( cnt = readCommand( CurrentSocket, cmdBuf, sizeof( cmdBuf ) - 1 ) ) <= 0 )
467
delClient( CurrentSocket );
469
cmdBuf[ cnt ] = '\0';
470
if ( strncmp( cmdBuf, "quit", 4 ) == 0 )
471
delClient( CurrentSocket );
473
CurrentClient = ClientList[ i ].out;
475
executeCommand( cmdBuf );
476
fprintf( CurrentClient, "ksysguardd> " );
477
fflush( CurrentClient );
483
} else if ( FD_ISSET( STDIN_FILENO, fds ) ) {
484
if (readCommand( STDIN_FILENO, cmdBuf, sizeof( cmdBuf ) ) < 0) {
487
executeCommand( cmdBuf );
488
printf( "ksysguardd> " );
493
static void initModules()
495
struct SensorModul *entry;
497
/* initialize all sensors */
500
for ( entry = SensorModulList; entry->configName != NULL; entry++ ) {
501
if ( entry->initCommand != NULL && sensorAvailable( entry->configName ) ) {
502
entry->available = 1;
503
entry->initCommand( entry );
510
static void exitModules()
512
struct SensorModul *entry;
514
for ( entry = SensorModulList; entry->configName != NULL; entry++ ) {
515
if ( entry->exitCommand != NULL && entry->available )
516
entry->exitCommand();
525
================================ public part =================================
529
* Will replace a "/" with "\/"
530
* Allocates a new string, so when calling this make sure to free the original.
532
char* escapeString( char* string ) {
536
int charsToEscape = 0;
538
/* Count how many characters we need to escape so that we know how much memory we'll have to allocate */
540
while (string[i] != '\0') {
541
if( string[i] == '/' ) {
548
/* Note: length doesn't count the \0 at the end of the string */
551
/* Allocate a new string, result, with enough room for the escaped characters */
552
result = (char *)malloc( length + charsToEscape + 1 );
554
/* Fill result with an escaped version of string */
556
while (string[i] != '\0') {
557
if( string[i] == '/' ) {
561
resultP[0] = string[i];
570
int main( int argc, char* argv[] )
576
#ifdef OSTYPE_FreeBSD
578
If we are not root or the executable does not belong to the
579
kmem group, ksysguardd will crash because of permission problems
580
for opening /dev/kmem
582
struct group* grentry = NULL;
584
if ( geteuid() != 0 ) {
585
grentry = getgrnam( "kmem" );
586
if ( grentry == NULL ) {
587
fprintf( stderr, "the group kmem is missing on your system\n" );
591
if ( getegid() != grentry->gr_gid ) {
592
fprintf( stderr, "ksysguardd can't be started because of permission conflicts!\n"
593
"Start the program as user 'root' or change its group to 'kmem' and set the sgid-bit\n" );
601
printWelcome( stdout );
603
if ( processArguments( argc, argv ) < 0 )
606
parseConfigFile( ConfigFile );
613
if ( ( ServerSocket = createServerSocket() ) < 0 )
617
fprintf( stdout, "ksysguardd> " );
619
CurrentClient = stdout;
623
tv.tv_sec = TIMERINTERVAL;
625
gettimeofday( &last, NULL );
628
int highestFD = setupSelect( &fds );
629
/* wait for communication or timeouts */
630
if ( select( highestFD + 1, &fds, NULL, NULL, &tv ) >= 0 ) {
631
handleTimerEvent( &tv, &last );
632
handleSocketTraffic( ServerSocket, &fds );