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>
46
#include "ksysguardd.h"
48
#ifdef HAVE_SYS_INOTIFY_H
49
#include <sys/inotify.h>
52
#define CMDBUFSIZE 128
53
#define MAX_CLIENTS 100
60
static int ServerSocket;
61
static ClientInfo ClientList[ MAX_CLIENTS ];
62
static int SocketPort = -1;
63
static unsigned char BindToAllInterfaces = 0;
64
static int CurrentSocket;
65
static const char LockFile[] = "/var/run/ksysguardd.pid";
66
static const char *ConfigFile = KSYSGUARDDRCFILE;
68
void signalHandler( int sig );
69
void makeDaemon( void );
70
void resetClientList( void );
71
int addClient( int client );
72
int delClient( int client );
73
int createServerSocket( void );
76
This variable is set to 1 if a module requests that the daemon should
82
This variable indicates whether we are running as daemon or (1) or if
83
we were have a controlling shell.
88
This pointer is used by all modules. It contains the file pointer of
89
the currently served client. This is stdout for non-daemon mode.
91
FILE* CurrentClient = 0;
93
static int processArguments( int argc, char* argv[] )
98
while ( ( option = getopt( argc, argv, "-p:f:dih" ) ) != EOF ) {
99
switch ( tolower( option ) ) {
101
SocketPort = atoi( optarg );
104
ConfigFile = strdup( optarg );
110
BindToAllInterfaces = 1;
115
fprintf(stderr, "Usage: %s [-d] [-i] [-p port]\n", argv[ 0 ] );
124
static void printWelcome( FILE* out )
126
fprintf( out, "ksysguardd 4\n"
127
"(c) 1999, 2000, 2001, 2002 Chris Schlaeger <cs@kde.org>\n"
128
"(c) 2001 Tobias Koenig <tokoe@kde.org>\n"
129
"(c) 2006-2008 Greg Martyn <greg.martyn@gmail.com>\n"
130
"This program is part of the KDE Project and licensed under\n"
131
"the GNU GPL version 2. See http://www.kde.org for details.\n");
136
static int createLockFile()
140
if ( ( file = fopen( LockFile, "w+" ) ) != NULL ) {
142
lock.l_type = F_WRLCK;
147
if ( fcntl( fileno( file ), F_SETLK, &lock ) < 0 ) {
148
if ( ( errno == EACCES ) || ( errno == EAGAIN ) ) {
149
log_error( "ksysguardd is running already" );
150
fprintf( stderr, "ksysguardd is running already\n" );
156
fseek( file, 0, SEEK_SET );
157
fprintf( file, "%d\n", getpid() );
159
if (ftruncate( fileno( file ), ftell( file ) ) == -1) {
160
log_error( "Cannot set size of lockfile '%s'", LockFile );
161
fprintf( stderr, "Cannot set size of lockfile '%s'\n", LockFile );
166
log_error( "Cannot create lockfile '%s'", LockFile );
167
fprintf( stderr, "Cannot create lockfile '%s'\n", LockFile );
172
We abandon 'file' here on purpose. It's needed nowhere else, but we
173
have to keep the file open and locked. The kernel will remove the
174
lock when the process terminates and the runlevel scripts has to
180
static void dropPrivileges( void )
184
if ( ( pwd = getpwnam( "nobody" ) ) != NULL ) {
185
if ( !setgid(pwd->pw_gid) )
187
if (!geteuid() && getuid() != pwd->pw_uid)
191
log_error( "User 'nobody' does not exist." );
193
We exit here to avoid becoming vulnerable just because
194
user nobody does not exist.
200
void makeDaemon( void )
205
log_error( "fork() failed" );
209
if( chdir( "/" ) == -1) {
210
log_error("chdir(\"/\") failed");
214
if ( createLockFile() < 0 )
219
fd = open("/dev/null", O_RDWR, 0);
221
dup2(fd, STDIN_FILENO);
222
dup2(fd, STDOUT_FILENO);
223
dup2(fd, STDERR_FILENO);
232
static int readCommand( int fd, char* cmdBuf, size_t len )
236
for ( i = 0; i < len; ++i )
238
int result = read( fd, &c, 1 );
240
return -1; /* Error */
244
return -1; /* Connection lost */
246
break; /* End of data */
250
break; /* End of line */
259
void resetClientList( void )
263
for ( i = 0; i < MAX_CLIENTS; i++ ) {
264
ClientList[ i ].socket = -1;
265
ClientList[ i ].out = 0;
270
addClient adds a new client to the ClientList.
272
int addClient( int client )
277
for ( i = 0; i < MAX_CLIENTS; i++ ) {
278
if ( ClientList[ i ].socket == -1 ) {
279
ClientList[ i ].socket = client;
280
if ( ( out = fdopen( client, "w+" ) ) == NULL ) {
281
log_error( "fdopen()" );
284
/* We use unbuffered IO */
285
fcntl( fileno( out ), F_SETFL, O_NDELAY );
286
ClientList[ i ].out = out;
288
fprintf( out, "ksysguardd> " );
299
delClient removes a client from the ClientList.
301
int delClient( int client )
305
for ( i = 0; i < MAX_CLIENTS; i++ ) {
306
if ( ClientList[i].socket == client ) {
307
fclose( ClientList[ i ].out );
308
ClientList[ i ].out = 0;
309
close( ClientList[ i ].socket );
310
ClientList[ i ].socket = -1;
318
int createServerSocket()
322
struct sockaddr_in s_in;
323
struct servent *service;
325
if ( ( newSocket = socket( PF_INET, SOCK_STREAM, 0 ) ) < 0 ) {
326
log_error( "socket()" );
330
setsockopt( newSocket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) );
333
The -p command line option always overrides the default or the
336
if ( SocketPort == -1 ) {
337
if ( ( service = getservbyname( "ksysguardd", "tcp" ) ) == NULL ) {
339
No entry in service directory and no command line request,
340
so we take the build-in default (the official IANA port).
342
SocketPort = PORT_NUMBER;
344
SocketPort = htons( service->s_port );
347
memset( &s_in, 0, sizeof( struct sockaddr_in ) );
348
s_in.sin_family = AF_INET;
349
if ( BindToAllInterfaces )
350
s_in.sin_addr.s_addr = htonl( INADDR_ANY );
352
s_in.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
353
s_in.sin_port = htons( SocketPort );
355
if ( bind( newSocket, (struct sockaddr*)&s_in, sizeof( s_in ) ) < 0 ) {
356
log_error( "Cannot bind to port %d", SocketPort );
360
if ( listen( newSocket, 5 ) < 0 ) {
361
log_error( "listen()" );
368
static int setupSelect( fd_set* fds )
370
int highestFD = ServerSocket;
373
Fill the filedescriptor array with all relevant descriptors. If we
374
not in daemon mode we only need to watch stdin.
378
FD_SET( ServerSocket, fds );
380
for ( i = 0; i < MAX_CLIENTS; i++ ) {
381
if ( ClientList[ i ].socket != -1 ) {
382
FD_SET( ClientList[ i ].socket, fds );
383
if ( highestFD < ClientList[ i ].socket )
384
highestFD = ClientList[ i ].socket;
388
FD_SET( STDIN_FILENO, fds );
389
if ( highestFD < STDIN_FILENO )
390
highestFD = STDIN_FILENO;
396
static void checkModules()
398
struct SensorModul *entry;
400
for ( entry = SensorModulList; entry->configName != NULL; entry++ )
401
if ( entry->checkCommand != NULL && entry->available )
402
entry->checkCommand();
405
static void handleSocketTraffic( int socketNo, const fd_set* fds )
407
char cmdBuf[ CMDBUFSIZE ];
412
if ( FD_ISSET( socketNo, fds ) ) {
414
struct sockaddr addr;
415
kde_socklen_t addr_len = sizeof( struct sockaddr );
417
/* a new connection */
418
if ( ( clientsocket = accept( socketNo, &addr, &addr_len ) ) < 0 ) {
419
log_error( "accept()" );
422
addClient( clientsocket );
425
for ( i = 0; i < MAX_CLIENTS; i++ ) {
426
if ( ClientList[ i ].socket != -1 ) {
427
CurrentSocket = ClientList[ i ].socket;
428
if ( FD_ISSET( ClientList[ i ].socket, fds ) ) {
430
if ( ( cnt = readCommand( CurrentSocket, cmdBuf, sizeof( cmdBuf ) - 1 ) ) <= 0 )
431
delClient( CurrentSocket );
433
cmdBuf[ cnt ] = '\0';
434
if ( strncmp( cmdBuf, "quit", 4 ) == 0 )
435
delClient( CurrentSocket );
437
CurrentClient = ClientList[ i ].out;
439
executeCommand( cmdBuf );
440
output( "ksysguardd> " );
441
fflush( CurrentClient );
447
} else if ( FD_ISSET( STDIN_FILENO, fds ) ) {
448
if (readCommand( STDIN_FILENO, cmdBuf, sizeof( cmdBuf ) ) < 0) {
451
executeCommand( cmdBuf );
452
printf( "ksysguardd> " );
457
static void initModules()
459
struct SensorModul *entry;
461
/* initialize all sensors */
464
for ( entry = SensorModulList; entry->configName != NULL; entry++ ) {
465
if ( entry->initCommand != NULL && sensorAvailable( entry->configName ) ) {
466
entry->available = 1;
467
entry->initCommand( entry );
474
static void exitModules()
476
struct SensorModul *entry;
478
for ( entry = SensorModulList; entry->configName != NULL; entry++ ) {
479
if ( entry->exitCommand != NULL && entry->available )
480
entry->exitCommand();
489
================================ public part =================================
493
* Will replace a "/" with "\/"
494
* Allocates a new string, so when calling this make sure to free the original.
496
char* escapeString( char* string ) {
500
int charsToEscape = 0;
502
/* Count how many characters we need to escape so that we know how much memory we'll have to allocate */
504
while (string[i] != '\0') {
505
if( string[i] == '/' ) {
512
/* Note: length doesn't count the \0 at the end of the string */
515
/* Allocate a new string, result, with enough room for the escaped characters */
516
if(! (result = (char *)malloc( length + charsToEscape + 1 )) ) {
517
print_error("Malloc failed - out of memory");
521
/* Fill result with an escaped version of string */
523
while (string[i] != '\0') {
524
if( string[i] == '/' ) {
528
resultP[0] = string[i];
537
#ifdef HAVE_SYS_INOTIFY_H
538
static void setupInotify(int *mtabfd) {
539
(*mtabfd) = inotify_init ();
540
if ((*mtabfd) >= 0) {
541
int wd = inotify_add_watch ((*mtabfd), "/etc/mtab", IN_MODIFY | IN_CREATE | IN_DELETE);
542
if(wd < 0) (*mtabfd) = -1; /* error setting up inotify watch */
547
int main( int argc, char* argv[] )
551
printWelcome( stdout );
553
if ( processArguments( argc, argv ) < 0 )
556
parseConfigFile( ConfigFile );
563
if ( ( ServerSocket = createServerSocket() ) < 0 )
567
fprintf( stdout, "ksysguardd> " );
569
CurrentClient = stdout;
573
#ifdef HAVE_SYS_INOTIFY_H
574
/* Monitor mtab for changes */
576
setupInotify(&mtabfd);
581
gettimeofday( &last, NULL );
584
int highestFD = setupSelect( &fds );
585
#ifdef HAVE_SYS_INOTIFY_H
587
FD_SET( mtabfd, &fds);
588
if(mtabfd > highestFD) highestFD = mtabfd;
591
/* wait for communication or timeouts */
592
int ret = select( highestFD + 1, &fds, NULL, NULL, NULL );
594
gettimeofday( &now, NULL );
595
if ( now.tv_sec - last.tv_sec >= 5 ) { /* 5 second intervals */
596
/* If so, update all sensors and save current time to last. */
600
#ifdef HAVE_SYS_INOTIFY_H
601
if(mtabfd >= 0 && FD_ISSET(mtabfd, &fds)) {
603
setupInotify(&mtabfd);
606
handleSocketTraffic( ServerSocket, &fds );