~ubuntu-branches/ubuntu/gutsy/kdebase-workspace/gutsy-backports

« back to all changes in this revision

Viewing changes to ksysguard/ksysguardd/ksysguardd.c

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2007-09-05 20:45:14 UTC
  • Revision ID: james.westby@ubuntu.com-20070905204514-632hhspl0nvrc84i
Tags: upstream-3.93.0
ImportĀ upstreamĀ versionĀ 3.93.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    KSysGuard, the KDE System Guard
 
3
 
 
4
    Copyright (c) 1999 - 2003 Chris Schlaeger <cs@kde.org>
 
5
                              Tobias Koenig <tokoe@kde.org>
 
6
                         2006 Greg Martyn <greg.martyn@gmail.com>
 
7
 
 
8
    Solaris support by Torsten Kasch <tk@Genetik.Uni-Bielefeld.DE>
 
9
 
 
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.
 
13
 
 
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.
 
18
 
 
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.
 
22
 
 
23
*/
 
24
 
 
25
#include <config-workspace.h>
 
26
#include <ctype.h>
 
27
#include <fcntl.h>
 
28
#include <netdb.h>
 
29
#include <netinet/in.h>
 
30
#include <pwd.h>
 
31
#include <signal.h>
 
32
#include <stdio.h>
 
33
#include <stdlib.h>
 
34
#include <string.h>
 
35
#include <sys/file.h>
 
36
#include <sys/socket.h>
 
37
#include <sys/stat.h>
 
38
#include <sys/time.h>
 
39
#include <sys/types.h>
 
40
#include <sys/wait.h>
 
41
#include <unistd.h>
 
42
#include <errno.h>
 
43
#include <../version.h>
 
44
 
 
45
#include "modules.h"
 
46
 
 
47
#include "ksysguardd.h"
 
48
 
 
49
#define CMDBUFSIZE      128
 
50
#define MAX_CLIENTS     100
 
51
 
 
52
typedef struct {
 
53
  int socket;
 
54
  FILE* out;
 
55
} ClientInfo;
 
56
 
 
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;
 
64
 
 
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 );
 
71
 
 
72
/**
 
73
  This variable is set to 1 if a module requests that the daemon should
 
74
  be terminated.
 
75
 */
 
76
int QuitApp = 0;
 
77
 
 
78
/**
 
79
  This variable indicates whether we are running as daemon or (1) or if
 
80
  we were have a controlling shell.
 
81
 */
 
82
int RunAsDaemon = 0;
 
83
 
 
84
/**
 
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.
 
87
 */
 
88
FILE* CurrentClient = 0;
 
89
 
 
90
static int processArguments( int argc, char* argv[] )
 
91
{
 
92
  int option;
 
93
 
 
94
  opterr = 0;
 
95
  while ( ( option = getopt( argc, argv, "-p:f:dih" ) ) != EOF ) {
 
96
    switch ( tolower( option ) ) {
 
97
      case 'p':
 
98
        SocketPort = atoi( optarg );
 
99
        break;
 
100
      case 'f':
 
101
        ConfigFile = strdup( optarg );
 
102
        break;
 
103
      case 'd':
 
104
        RunAsDaemon = 1;
 
105
        break;
 
106
      case 'i':
 
107
        BindToAllInterfaces = 1;
 
108
        break;
 
109
      case '?':
 
110
      case 'h':
 
111
      default:
 
112
        fprintf(stderr, "Usage: %s [-d] [-i] [-p port]\n", argv[ 0 ] );
 
113
        return -1;
 
114
        break;
 
115
    }
 
116
  }
 
117
 
 
118
  return 0;
 
119
}
 
120
 
 
121
static void printWelcome( FILE* out )
 
122
{
 
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", 
 
129
           KSYSGUARD_VERSION );
 
130
 
 
131
  fflush( out );
 
132
}
 
133
 
 
134
static int createLockFile()
 
135
{
 
136
  FILE *file;
 
137
 
 
138
  if ( ( file = fopen( LockFile, "w+" ) ) != NULL ) {
 
139
    struct flock lock;
 
140
    lock.l_type = F_WRLCK;
 
141
    lock.l_whence = 0;
 
142
    lock.l_start = 0;
 
143
    lock.l_len = 0;
 
144
    lock.l_pid = -1;
 
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" );
 
149
        fclose( file );
 
150
        return -1;
 
151
      }
 
152
    }
 
153
 
 
154
    fseek( file, 0, SEEK_SET );
 
155
    fprintf( file, "%d\n", getpid() );
 
156
    fflush( file );
 
157
    ftruncate( fileno( file ), ftell( file ) );
 
158
  } else {
 
159
    log_error( "Cannot create lockfile '%s'", LockFile );
 
160
    fprintf( stderr, "Cannot create lockfile '%s'\n", LockFile );
 
161
    return -2;
 
162
  }
 
163
 
 
164
  /**
 
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
 
168
    remove the pid file.
 
169
   */
 
170
  return 0;
 
171
}
 
172
 
 
173
void signalHandler( int sig )
 
174
{
 
175
  switch ( sig ) {
 
176
    case SIGQUIT:
 
177
    case SIGTERM:
 
178
      /* Not really anything to do here at the moment. */
 
179
      exit( 0 );
 
180
      break;
 
181
  }
 
182
}
 
183
 
 
184
static void installSignalHandler( void )
 
185
{
 
186
  struct sigaction Action;
 
187
 
 
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 );
 
194
}
 
195
 
 
196
static void dropPrivileges( void )
 
197
{
 
198
  struct passwd *pwd;
 
199
 
 
200
  if ( ( pwd = getpwnam( "nobody" ) ) != NULL ) {
 
201
    if ( !setgid(pwd->pw_gid) )
 
202
      setuid(pwd->pw_uid);
 
203
    if (!geteuid() && getuid() != pwd->pw_uid)
 
204
      _exit(1);
 
205
  }
 
206
  else {
 
207
    log_error( "User 'nobody' does not exist." );
 
208
    /**
 
209
      We exit here to avoid becoming vulnerable just because
 
210
      user nobody does not exist.
 
211
     */
 
212
    _exit(1);
 
213
  }
 
214
}
 
215
 
 
216
void makeDaemon( void )
 
217
{
 
218
  int fd = -1;
 
219
  switch ( fork() ) {
 
220
    case -1:
 
221
      log_error( "fork() failed" );
 
222
      break;
 
223
    case 0:
 
224
      setsid();
 
225
      chdir( "/" );
 
226
      umask( 0 );
 
227
      if ( createLockFile() < 0 )
 
228
        _exit( 1 );
 
229
 
 
230
      dropPrivileges();
 
231
      installSignalHandler();
 
232
      
 
233
      fd = open("/dev/null", O_RDWR, 0); 
 
234
      if (fd != -1) {
 
235
          dup2(fd, STDIN_FILENO);
 
236
          dup2(fd, STDOUT_FILENO);
 
237
          dup2(fd, STDERR_FILENO);
 
238
          close (fd);
 
239
      }
 
240
      break;
 
241
    default:
 
242
      exit( 0 );
 
243
  }
 
244
}
 
245
 
 
246
static int readCommand( int fd, char* cmdBuf, size_t len )
 
247
{
 
248
  unsigned int i;
 
249
  char c;
 
250
  for ( i = 0; i < len; ++i )
 
251
  {
 
252
    int result = read( fd, &c, 1 );
 
253
    if (result < 0)
 
254
      return -1; /* Error */
 
255
      
 
256
    if (result == 0) {
 
257
      if (i == 0)
 
258
        return -1; /* Connection lost */
 
259
 
 
260
      break; /* End of data */
 
261
    }
 
262
    
 
263
    if (c == '\n')
 
264
      break; /* End of line */
 
265
 
 
266
    cmdBuf[ i ] = c;
 
267
  }
 
268
  cmdBuf[i] = '\0';
 
269
 
 
270
  return i;
 
271
}
 
272
 
 
273
void resetClientList( void )
 
274
{
 
275
  int i;
 
276
 
 
277
  for ( i = 0; i < MAX_CLIENTS; i++ ) {
 
278
    ClientList[ i ].socket = -1;
 
279
    ClientList[ i ].out = 0;
 
280
  }
 
281
}
 
282
 
 
283
/**
 
284
  addClient adds a new client to the ClientList.
 
285
 */
 
286
int addClient( int client )
 
287
{
 
288
  int i;
 
289
  FILE* out;
 
290
 
 
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()" );
 
296
        return -1;
 
297
      }
 
298
      /* We use unbuffered IO */
 
299
      fcntl( fileno( out ), F_SETFL, O_NDELAY );
 
300
      ClientList[ i ].out = out;
 
301
      printWelcome( out );
 
302
      fprintf( out, "ksysguardd> " );
 
303
      fflush( out );
 
304
 
 
305
      return 0;
 
306
    }
 
307
  }
 
308
 
 
309
  return -1;
 
310
}
 
311
 
 
312
/**
 
313
  delClient removes a client from the ClientList.
 
314
 */
 
315
int delClient( int client )
 
316
{
 
317
  int i;
 
318
 
 
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;
 
325
      return 0;
 
326
    }
 
327
  }
 
328
 
 
329
  return -1;
 
330
}
 
331
 
 
332
int createServerSocket()
 
333
{
 
334
  int i = 1;
 
335
  int newSocket;
 
336
  struct sockaddr_in s_in;
 
337
  struct servent *service;
 
338
 
 
339
  if ( ( newSocket = socket( PF_INET, SOCK_STREAM, 0 ) ) < 0 ) {
 
340
    log_error( "socket()" );
 
341
    return -1;
 
342
  }
 
343
 
 
344
  setsockopt( newSocket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) );
 
345
 
 
346
  /**
 
347
    The -p command line option always overrides the default or the
 
348
    service entry.
 
349
   */
 
350
  if ( SocketPort == -1 ) {
 
351
    if ( ( service = getservbyname( "ksysguardd", "tcp" ) ) == NULL ) {
 
352
      /**
 
353
        No entry in service directory and no command line request,
 
354
        so we take the build-in default (the official IANA port).
 
355
       */
 
356
      SocketPort = PORT_NUMBER;
 
357
    } else
 
358
      SocketPort = htons( service->s_port );
 
359
  }
 
360
 
 
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 );
 
365
  else
 
366
      s_in.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
 
367
  s_in.sin_port = htons( SocketPort );
 
368
 
 
369
  if ( bind( newSocket, (struct sockaddr*)&s_in, sizeof( s_in ) ) < 0 ) {
 
370
    log_error( "Cannot bind to port %d", SocketPort );
 
371
    return -1;
 
372
  }
 
373
 
 
374
  if ( listen( newSocket, 5 ) < 0 ) {
 
375
    log_error( "listen()" );
 
376
    return -1;
 
377
  }
 
378
 
 
379
  return newSocket;
 
380
}
 
381
 
 
382
static int setupSelect( fd_set* fds )
 
383
{
 
384
  int highestFD = ServerSocket;
 
385
  FD_ZERO( fds );
 
386
  /**
 
387
    Fill the filedescriptor array with all relevant descriptors. If we
 
388
    not in daemon mode we only need to watch stdin.
 
389
   */
 
390
  if ( RunAsDaemon ) {
 
391
    int i;
 
392
    FD_SET( ServerSocket, fds );
 
393
 
 
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;
 
399
      }
 
400
    }
 
401
  } else {
 
402
    FD_SET( STDIN_FILENO, fds );
 
403
    if ( highestFD < STDIN_FILENO )
 
404
      highestFD = STDIN_FILENO;
 
405
  }
 
406
 
 
407
  return highestFD;
 
408
}
 
409
 
 
410
static void checkModules()
 
411
{
 
412
  struct SensorModul *entry;
 
413
 
 
414
  for ( entry = SensorModulList; entry->configName != NULL; entry++ )
 
415
    if ( entry->checkCommand != NULL && entry->available )
 
416
      entry->checkCommand();
 
417
}
 
418
 
 
419
static void handleTimerEvent( struct timeval* tv, struct timeval* last )
 
420
{
 
421
  struct timeval now;
 
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. */
 
426
    checkModules();
 
427
    *last = now;
 
428
  }
 
429
  /**
 
430
    Set tv so that the next timer event will be generated in
 
431
    TIMERINTERVAL seconds.
 
432
   */
 
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;
 
437
  } else
 
438
    tv->tv_sec = last->tv_sec + TIMERINTERVAL - now.tv_sec;
 
439
}
 
440
 
 
441
static void handleSocketTraffic( int socketNo, const fd_set* fds )
 
442
{
 
443
  char cmdBuf[ CMDBUFSIZE ];
 
444
 
 
445
  if ( RunAsDaemon ) {
 
446
    int i;
 
447
 
 
448
    if ( FD_ISSET( socketNo, fds ) ) {
 
449
      int clientsocket;
 
450
      struct sockaddr addr;
 
451
      kde_socklen_t addr_len = sizeof( struct sockaddr );
 
452
 
 
453
      /* a new connection */
 
454
      if ( ( clientsocket = accept( socketNo, &addr, &addr_len ) ) < 0 ) {
 
455
        log_error( "accept()" );
 
456
        exit( 1 );
 
457
      } else
 
458
        addClient( clientsocket );
 
459
    }
 
460
 
 
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 ) ) {
 
465
          ssize_t cnt;
 
466
          if ( ( cnt = readCommand( CurrentSocket, cmdBuf, sizeof( cmdBuf ) - 1 ) ) <= 0 )
 
467
            delClient( CurrentSocket );
 
468
          else {
 
469
            cmdBuf[ cnt ] = '\0';
 
470
            if ( strncmp( cmdBuf, "quit", 4 ) == 0 )
 
471
              delClient( CurrentSocket );
 
472
            else {
 
473
              CurrentClient = ClientList[ i ].out;
 
474
              fflush( stdout );
 
475
              executeCommand( cmdBuf );
 
476
              fprintf( CurrentClient, "ksysguardd> " );
 
477
              fflush( CurrentClient );
 
478
            }
 
479
          }
 
480
        }
 
481
      }
 
482
    }
 
483
  } else if ( FD_ISSET( STDIN_FILENO, fds ) ) {
 
484
    if (readCommand( STDIN_FILENO, cmdBuf, sizeof( cmdBuf ) ) < 0) {
 
485
      exit(0);
 
486
    }
 
487
    executeCommand( cmdBuf );
 
488
    printf( "ksysguardd> " );
 
489
    fflush( stdout );
 
490
  }
 
491
}
 
492
 
 
493
static void initModules()
 
494
{
 
495
  struct SensorModul *entry;
 
496
 
 
497
  /* initialize all sensors */
 
498
  initCommand();
 
499
 
 
500
  for ( entry = SensorModulList; entry->configName != NULL; entry++ ) {
 
501
    if ( entry->initCommand != NULL && sensorAvailable( entry->configName ) ) {
 
502
      entry->available = 1;
 
503
      entry->initCommand( entry );
 
504
    }
 
505
  }
 
506
 
 
507
  ReconfigureFlag = 0;
 
508
}
 
509
 
 
510
static void exitModules()
 
511
{
 
512
  struct SensorModul *entry;
 
513
 
 
514
  for ( entry = SensorModulList; entry->configName != NULL; entry++ ) {
 
515
    if ( entry->exitCommand != NULL && entry->available )
 
516
      entry->exitCommand();
 
517
  }
 
518
 
 
519
  exitCommand();
 
520
}
 
521
 
 
522
 
 
523
 
 
524
/*
 
525
================================ public part =================================
 
526
*/
 
527
 
 
528
/*
 
529
 *  Will replace a "/" with "\/"
 
530
 *  Allocates a new string, so when calling this make sure to free the original.
 
531
 */
 
532
char* escapeString( char* string ) {
 
533
  int i, length;
 
534
  char* result;
 
535
  char* resultP;
 
536
  int charsToEscape = 0;
 
537
 
 
538
  /* Count how many characters we need to escape so that we know how much memory we'll have to allocate */
 
539
  i = 0;
 
540
  while (string[i] != '\0') {
 
541
    if( string[i] == '/' ) {
 
542
      ++charsToEscape;
 
543
    }
 
544
 
 
545
    ++i;
 
546
  }
 
547
 
 
548
  /* Note: length doesn't count the \0 at the end of the string */
 
549
  length = i;
 
550
 
 
551
  /* Allocate a new string, result, with enough room for the escaped characters */
 
552
  result = (char *)malloc( length + charsToEscape + 1 );
 
553
  resultP = result;
 
554
  /* Fill result with an escaped version of string */
 
555
  i = 0;
 
556
  while (string[i] != '\0') {
 
557
    if( string[i] == '/' ) {
 
558
      resultP[0] = '\\';
 
559
      resultP++;
 
560
    }
 
561
    resultP[0] = string[i];
 
562
    resultP++;
 
563
    
 
564
    ++i;
 
565
  }
 
566
  resultP[0] = '\0';
 
567
  return result;
 
568
}
 
569
 
 
570
int main( int argc, char* argv[] )
 
571
{
 
572
  fd_set fds;
 
573
  struct timeval tv;
 
574
  struct timeval last;
 
575
 
 
576
#ifdef OSTYPE_FreeBSD
 
577
        /**
 
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
 
581
   */
 
582
  struct group* grentry = NULL;
 
583
 
 
584
  if ( geteuid() != 0 ) {
 
585
    grentry = getgrnam( "kmem" );
 
586
    if ( grentry == NULL ) {
 
587
      fprintf( stderr, "the group kmem is missing on your system\n" );
 
588
      return -1;
 
589
    }
 
590
 
 
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" );
 
594
      return -1;
 
595
    }
 
596
 
 
597
    endgrent();
 
598
  }
 
599
#endif
 
600
 
 
601
  printWelcome( stdout );
 
602
 
 
603
  if ( processArguments( argc, argv ) < 0 )
 
604
    return -1;
 
605
 
 
606
  parseConfigFile( ConfigFile );
 
607
 
 
608
  initModules();
 
609
 
 
610
  if ( RunAsDaemon ) {
 
611
    makeDaemon();
 
612
 
 
613
    if ( ( ServerSocket = createServerSocket() ) < 0 )
 
614
      return -1;
 
615
    resetClientList();
 
616
  } else {
 
617
    fprintf( stdout, "ksysguardd> " );
 
618
    fflush( stdout );
 
619
    CurrentClient = stdout;
 
620
    ServerSocket = 0;
 
621
  }
 
622
 
 
623
  tv.tv_sec = TIMERINTERVAL;
 
624
  tv.tv_usec = 0;
 
625
  gettimeofday( &last, NULL );
 
626
 
 
627
  while ( !QuitApp ) {
 
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 );
 
633
    }
 
634
  }
 
635
 
 
636
  exitModules();
 
637
 
 
638
  freeConfigFile();
 
639
 
 
640
  return 0;
 
641
}