~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to ksysguard/ksysguardd/ksysguardd.c

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

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
 
 
44
#include "modules.h"
 
45
 
 
46
#include "ksysguardd.h"
 
47
 
 
48
#ifdef HAVE_SYS_INOTIFY_H
 
49
#include <sys/inotify.h>
 
50
#endif
 
51
 
 
52
#define CMDBUFSIZE      128
 
53
#define MAX_CLIENTS     100
 
54
 
 
55
typedef struct {
 
56
  int socket;
 
57
  FILE* out;
 
58
} ClientInfo;
 
59
 
 
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;
 
67
 
 
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 );
 
74
 
 
75
/**
 
76
  This variable is set to 1 if a module requests that the daemon should
 
77
  be terminated.
 
78
 */
 
79
int QuitApp = 0;
 
80
 
 
81
/**
 
82
  This variable indicates whether we are running as daemon or (1) or if
 
83
  we were have a controlling shell.
 
84
 */
 
85
int RunAsDaemon = 0;
 
86
 
 
87
/**
 
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.
 
90
 */
 
91
FILE* CurrentClient = 0;
 
92
 
 
93
static int processArguments( int argc, char* argv[] )
 
94
{
 
95
  int option;
 
96
 
 
97
  opterr = 0;
 
98
  while ( ( option = getopt( argc, argv, "-p:f:dih" ) ) != EOF ) {
 
99
    switch ( tolower( option ) ) {
 
100
      case 'p':
 
101
        SocketPort = atoi( optarg );
 
102
        break;
 
103
      case 'f':
 
104
        ConfigFile = strdup( optarg );
 
105
        break;
 
106
      case 'd':
 
107
        RunAsDaemon = 1;
 
108
        break;
 
109
      case 'i':
 
110
        BindToAllInterfaces = 1;
 
111
        break;
 
112
      case '?':
 
113
      case 'h':
 
114
      default:
 
115
        fprintf(stderr, "Usage: %s [-d] [-i] [-p port]\n", argv[ 0 ] );
 
116
        return -1;
 
117
        break;
 
118
    }
 
119
  }
 
120
 
 
121
  return 0;
 
122
}
 
123
 
 
124
static void printWelcome( FILE* out )
 
125
{
 
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");
 
132
 
 
133
  fflush( out );
 
134
}
 
135
 
 
136
static int createLockFile()
 
137
{
 
138
  FILE *file;
 
139
 
 
140
  if ( ( file = fopen( LockFile, "w+" ) ) != NULL ) {
 
141
    struct flock lock;
 
142
    lock.l_type = F_WRLCK;
 
143
    lock.l_whence = 0;
 
144
    lock.l_start = 0;
 
145
    lock.l_len = 0;
 
146
    lock.l_pid = -1;
 
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" );
 
151
        fclose( file );
 
152
        return -1;
 
153
      }
 
154
    }
 
155
 
 
156
    fseek( file, 0, SEEK_SET );
 
157
    fprintf( file, "%d\n", getpid() );
 
158
    fflush( file );
 
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 );
 
162
      fclose( file );
 
163
      return -2;
 
164
    }
 
165
  } else {
 
166
    log_error( "Cannot create lockfile '%s'", LockFile );
 
167
    fprintf( stderr, "Cannot create lockfile '%s'\n", LockFile );
 
168
    return -2;
 
169
  }
 
170
 
 
171
  /**
 
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
 
175
    remove the pid file.
 
176
   */
 
177
  return 0;
 
178
}
 
179
 
 
180
static void dropPrivileges( void )
 
181
{
 
182
  struct passwd *pwd;
 
183
 
 
184
  if ( ( pwd = getpwnam( "nobody" ) ) != NULL ) {
 
185
    if ( !setgid(pwd->pw_gid) )
 
186
      setuid(pwd->pw_uid);
 
187
    if (!geteuid() && getuid() != pwd->pw_uid)
 
188
      _exit(1);
 
189
  }
 
190
  else {
 
191
    log_error( "User 'nobody' does not exist." );
 
192
    /**
 
193
      We exit here to avoid becoming vulnerable just because
 
194
      user nobody does not exist.
 
195
     */
 
196
    _exit(1);
 
197
  }
 
198
}
 
199
 
 
200
void makeDaemon( void )
 
201
{
 
202
  int fd = -1;
 
203
  switch ( fork() ) {
 
204
    case -1:
 
205
      log_error( "fork() failed" );
 
206
      break;
 
207
    case 0:
 
208
      setsid();
 
209
      if( chdir( "/" ) == -1) {
 
210
          log_error("chdir(\"/\") failed");
 
211
          _exit(1);
 
212
      }
 
213
      umask( 0 );
 
214
      if ( createLockFile() < 0 )
 
215
        _exit( 1 );
 
216
 
 
217
      dropPrivileges();
 
218
 
 
219
      fd = open("/dev/null", O_RDWR, 0);
 
220
      if (fd != -1) {
 
221
          dup2(fd, STDIN_FILENO);
 
222
          dup2(fd, STDOUT_FILENO);
 
223
          dup2(fd, STDERR_FILENO);
 
224
          close (fd);
 
225
      }
 
226
      break;
 
227
    default:
 
228
      exit( 0 );
 
229
  }
 
230
}
 
231
 
 
232
static int readCommand( int fd, char* cmdBuf, size_t len )
 
233
{
 
234
  unsigned int i;
 
235
  char c;
 
236
  for ( i = 0; i < len; ++i )
 
237
  {
 
238
    int result = read( fd, &c, 1 );
 
239
    if (result < 0)
 
240
      return -1; /* Error */
 
241
 
 
242
    if (result == 0) {
 
243
      if (i == 0)
 
244
        return -1; /* Connection lost */
 
245
 
 
246
      break; /* End of data */
 
247
    }
 
248
 
 
249
    if (c == '\n')
 
250
      break; /* End of line */
 
251
 
 
252
    cmdBuf[ i ] = c;
 
253
  }
 
254
  cmdBuf[i] = '\0';
 
255
 
 
256
  return i;
 
257
}
 
258
 
 
259
void resetClientList( void )
 
260
{
 
261
  int i;
 
262
 
 
263
  for ( i = 0; i < MAX_CLIENTS; i++ ) {
 
264
    ClientList[ i ].socket = -1;
 
265
    ClientList[ i ].out = 0;
 
266
  }
 
267
}
 
268
 
 
269
/**
 
270
  addClient adds a new client to the ClientList.
 
271
 */
 
272
int addClient( int client )
 
273
{
 
274
  int i;
 
275
  FILE* out;
 
276
 
 
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()" );
 
282
        return -1;
 
283
      }
 
284
      /* We use unbuffered IO */
 
285
      fcntl( fileno( out ), F_SETFL, O_NDELAY );
 
286
      ClientList[ i ].out = out;
 
287
      printWelcome( out );
 
288
      fprintf( out, "ksysguardd> " );
 
289
      fflush( out );
 
290
 
 
291
      return 0;
 
292
    }
 
293
  }
 
294
 
 
295
  return -1;
 
296
}
 
297
 
 
298
/**
 
299
  delClient removes a client from the ClientList.
 
300
 */
 
301
int delClient( int client )
 
302
{
 
303
  int i;
 
304
 
 
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;
 
311
      return 0;
 
312
    }
 
313
  }
 
314
 
 
315
  return -1;
 
316
}
 
317
 
 
318
int createServerSocket()
 
319
{
 
320
  int i = 1;
 
321
  int newSocket;
 
322
  struct sockaddr_in s_in;
 
323
  struct servent *service;
 
324
 
 
325
  if ( ( newSocket = socket( PF_INET, SOCK_STREAM, 0 ) ) < 0 ) {
 
326
    log_error( "socket()" );
 
327
    return -1;
 
328
  }
 
329
 
 
330
  setsockopt( newSocket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) );
 
331
 
 
332
  /**
 
333
    The -p command line option always overrides the default or the
 
334
    service entry.
 
335
   */
 
336
  if ( SocketPort == -1 ) {
 
337
    if ( ( service = getservbyname( "ksysguardd", "tcp" ) ) == NULL ) {
 
338
      /**
 
339
        No entry in service directory and no command line request,
 
340
        so we take the build-in default (the official IANA port).
 
341
       */
 
342
      SocketPort = PORT_NUMBER;
 
343
    } else
 
344
      SocketPort = htons( service->s_port );
 
345
  }
 
346
 
 
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 );
 
351
  else
 
352
      s_in.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
 
353
  s_in.sin_port = htons( SocketPort );
 
354
 
 
355
  if ( bind( newSocket, (struct sockaddr*)&s_in, sizeof( s_in ) ) < 0 ) {
 
356
    log_error( "Cannot bind to port %d", SocketPort );
 
357
    return -1;
 
358
  }
 
359
 
 
360
  if ( listen( newSocket, 5 ) < 0 ) {
 
361
    log_error( "listen()" );
 
362
    return -1;
 
363
  }
 
364
 
 
365
  return newSocket;
 
366
}
 
367
 
 
368
static int setupSelect( fd_set* fds )
 
369
{
 
370
  int highestFD = ServerSocket;
 
371
  FD_ZERO( fds );
 
372
  /**
 
373
    Fill the filedescriptor array with all relevant descriptors. If we
 
374
    not in daemon mode we only need to watch stdin.
 
375
   */
 
376
  if ( RunAsDaemon ) {
 
377
    int i;
 
378
    FD_SET( ServerSocket, fds );
 
379
 
 
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;
 
385
      }
 
386
    }
 
387
  } else {
 
388
    FD_SET( STDIN_FILENO, fds );
 
389
    if ( highestFD < STDIN_FILENO )
 
390
      highestFD = STDIN_FILENO;
 
391
  }
 
392
 
 
393
  return highestFD;
 
394
}
 
395
 
 
396
static void checkModules()
 
397
{
 
398
  struct SensorModul *entry;
 
399
 
 
400
  for ( entry = SensorModulList; entry->configName != NULL; entry++ )
 
401
    if ( entry->checkCommand != NULL && entry->available )
 
402
      entry->checkCommand();
 
403
}
 
404
 
 
405
static void handleSocketTraffic( int socketNo, const fd_set* fds )
 
406
{
 
407
  char cmdBuf[ CMDBUFSIZE ];
 
408
 
 
409
  if ( RunAsDaemon ) {
 
410
    int i;
 
411
 
 
412
    if ( FD_ISSET( socketNo, fds ) ) {
 
413
      int clientsocket;
 
414
      struct sockaddr addr;
 
415
      kde_socklen_t addr_len = sizeof( struct sockaddr );
 
416
 
 
417
      /* a new connection */
 
418
      if ( ( clientsocket = accept( socketNo, &addr, &addr_len ) ) < 0 ) {
 
419
        log_error( "accept()" );
 
420
        exit( 1 );
 
421
      } else
 
422
        addClient( clientsocket );
 
423
    }
 
424
 
 
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 ) ) {
 
429
          ssize_t cnt;
 
430
          if ( ( cnt = readCommand( CurrentSocket, cmdBuf, sizeof( cmdBuf ) - 1 ) ) <= 0 )
 
431
            delClient( CurrentSocket );
 
432
          else {
 
433
            cmdBuf[ cnt ] = '\0';
 
434
            if ( strncmp( cmdBuf, "quit", 4 ) == 0 )
 
435
              delClient( CurrentSocket );
 
436
            else {
 
437
              CurrentClient = ClientList[ i ].out;
 
438
              fflush( stdout );
 
439
              executeCommand( cmdBuf );
 
440
              output( "ksysguardd> " );
 
441
              fflush( CurrentClient );
 
442
            }
 
443
          }
 
444
        }
 
445
      }
 
446
    }
 
447
  } else if ( FD_ISSET( STDIN_FILENO, fds ) ) {
 
448
    if (readCommand( STDIN_FILENO, cmdBuf, sizeof( cmdBuf ) ) < 0) {
 
449
      exit(0);
 
450
    }
 
451
    executeCommand( cmdBuf );
 
452
    printf( "ksysguardd> " );
 
453
    fflush( stdout );
 
454
  }
 
455
}
 
456
 
 
457
static void initModules()
 
458
{
 
459
  struct SensorModul *entry;
 
460
 
 
461
  /* initialize all sensors */
 
462
  initCommand();
 
463
 
 
464
  for ( entry = SensorModulList; entry->configName != NULL; entry++ ) {
 
465
    if ( entry->initCommand != NULL && sensorAvailable( entry->configName ) ) {
 
466
      entry->available = 1;
 
467
      entry->initCommand( entry );
 
468
    }
 
469
  }
 
470
 
 
471
  ReconfigureFlag = 0;
 
472
}
 
473
 
 
474
static void exitModules()
 
475
{
 
476
  struct SensorModul *entry;
 
477
 
 
478
  for ( entry = SensorModulList; entry->configName != NULL; entry++ ) {
 
479
    if ( entry->exitCommand != NULL && entry->available )
 
480
      entry->exitCommand();
 
481
  }
 
482
 
 
483
  exitCommand();
 
484
}
 
485
 
 
486
 
 
487
 
 
488
/*
 
489
================================ public part =================================
 
490
*/
 
491
 
 
492
/*
 
493
 *  Will replace a "/" with "\/"
 
494
 *  Allocates a new string, so when calling this make sure to free the original.
 
495
 */
 
496
char* escapeString( char* string ) {
 
497
  int i, length;
 
498
  char* result;
 
499
  char* resultP;
 
500
  int charsToEscape = 0;
 
501
 
 
502
  /* Count how many characters we need to escape so that we know how much memory we'll have to allocate */
 
503
  i = 0;
 
504
  while (string[i] != '\0') {
 
505
    if( string[i] == '/' ) {
 
506
      ++charsToEscape;
 
507
    }
 
508
 
 
509
    ++i;
 
510
  }
 
511
 
 
512
  /* Note: length doesn't count the \0 at the end of the string */
 
513
  length = i;
 
514
 
 
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");
 
518
    exit(1);
 
519
  }
 
520
  resultP = result;
 
521
  /* Fill result with an escaped version of string */
 
522
  i = 0;
 
523
  while (string[i] != '\0') {
 
524
    if( string[i] == '/' ) {
 
525
      resultP[0] = '\\';
 
526
      resultP++;
 
527
    }
 
528
    resultP[0] = string[i];
 
529
    resultP++;
 
530
 
 
531
    ++i;
 
532
  }
 
533
  resultP[0] = '\0';
 
534
  return result;
 
535
}
 
536
 
 
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 */
 
543
  }
 
544
 
 
545
}
 
546
#endif
 
547
int main( int argc, char* argv[] )
 
548
{
 
549
  fd_set fds;
 
550
 
 
551
  printWelcome( stdout );
 
552
 
 
553
  if ( processArguments( argc, argv ) < 0 )
 
554
    return -1;
 
555
 
 
556
  parseConfigFile( ConfigFile );
 
557
 
 
558
  initModules();
 
559
 
 
560
  if ( RunAsDaemon ) {
 
561
    makeDaemon();
 
562
 
 
563
    if ( ( ServerSocket = createServerSocket() ) < 0 )
 
564
      return -1;
 
565
    resetClientList();
 
566
  } else {
 
567
    fprintf( stdout, "ksysguardd> " );
 
568
    fflush( stdout );
 
569
    CurrentClient = stdout;
 
570
    ServerSocket = 0;
 
571
  }
 
572
 
 
573
#ifdef HAVE_SYS_INOTIFY_H
 
574
  /* Monitor mtab for changes */
 
575
  int mtabfd = 0;
 
576
  setupInotify(&mtabfd);
 
577
#endif
 
578
 
 
579
  struct timeval now;
 
580
  struct timeval last;
 
581
  gettimeofday( &last, NULL );
 
582
 
 
583
  while ( !QuitApp ) {
 
584
    int highestFD = setupSelect( &fds );
 
585
#ifdef HAVE_SYS_INOTIFY_H
 
586
    if(mtabfd >= 0)
 
587
      FD_SET( mtabfd, &fds);
 
588
    if(mtabfd > highestFD) highestFD = mtabfd;
 
589
#endif
 
590
 
 
591
    /* wait for communication or timeouts */
 
592
    int ret = select( highestFD + 1, &fds, NULL, NULL, NULL );
 
593
    if(ret >= 0) {
 
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. */
 
597
            checkModules();
 
598
            last = now;
 
599
        }
 
600
#ifdef HAVE_SYS_INOTIFY_H
 
601
        if(mtabfd >= 0 && FD_ISSET(mtabfd, &fds)) {
 
602
            close(mtabfd);
 
603
            setupInotify(&mtabfd);
 
604
        }
 
605
#endif
 
606
        handleSocketTraffic( ServerSocket, &fds );
 
607
    }
 
608
  }
 
609
 
 
610
  exitModules();
 
611
 
 
612
  freeConfigFile();
 
613
 
 
614
  return 0;
 
615
}