2
KSysGuard, the KDE System Guard
4
Copyright (c) 1999 - 2001 Chris Schlaeger <cs@kde.org>
6
This program is free software; you can redistribute it and/or
7
modify it under the terms of version 2 of the GNU General Public
8
License as published by the Free Software Foundation.
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
15
You should have received a copy of the GNU General Public License
16
along with this program; if not, write to the Free Software
17
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
29
#include <sys/resource.h>
32
#include <sys/ptrace.h>
33
#include <asm/unistd.h>
37
#include "../../gui/SignalIDs.h"
39
#include "PWUIDCache.h"
41
#include "ksysguardd.h"
43
#include "ProcessList.h"
47
#define KDEINITLEN sizeof( "kdeinit: " )
50
extern int sys_ioprio_set(int, int, int);
51
extern int sys_ioprio_get(int, int);
55
/* Check if this system has ionice */
56
#if !defined(SYS_ioprio_get) || !defined(SYS_ioprio_set)
57
/* All new kernels have SYS_ioprio_get and _set defined, but for the few that do not, here are the definitions */
59
#define __NR_ioprio_set 289
60
#define __NR_ioprio_get 290
61
#elif defined(__ppc__) || defined(__powerpc__)
62
#define __NR_ioprio_set 273
63
#define __NR_ioprio_get 274
64
#elif defined(__x86_64__)
65
#define __NR_ioprio_set 251
66
#define __NR_ioprio_get 252
67
#elif defined(__ia64__)
68
#define __NR_ioprio_set 1274
69
#define __NR_ioprio_get 1275
72
#warning "This architecture does not support IONICE. Disabling ionice feature."
76
/* Map these to SYS_ioprio_get */
77
#define SYS_ioprio_get __NR_ioprio_get
78
#define SYS_ioprio_set __NR_ioprio_set
80
#endif /* !SYS_ioprio_get */
82
/* Set up ionice functions */
84
#define IOPRIO_WHO_PROCESS 1
85
#define IOPRIO_CLASS_SHIFT 13
87
/* Expose the kernel calls to usespace via syscall
88
* See man ioprio_set and man ioprio_get for information on these functions */
89
static int ioprio_set(int which, int who, int ioprio)
91
return syscall(SYS_ioprio_set, which, who, ioprio);
94
static int ioprio_get(int which, int who)
96
return syscall(SYS_ioprio_get, which, who);
109
/** The parent process ID */
112
/** The real user ID */
115
/** The real group ID */
118
/** The process ID of any application that is debugging this one. 0 if none */
121
/** A character description of the process status */
124
/** The tty the process owns */
128
The nice level. The range should be -20 to 20. I'm not sure
129
whether this is true for all platforms.
133
/** The scheduling priority. */
136
/** The i/o scheduling class and priority. */
137
int ioPriorityClass; /**< 0 for none, 1 for realtime, 2 for best-effort, 3 for idle. -1 for error. */
138
int ioPriority; /**< Between 0 and 7. 0 is highest priority, 7 is lowest. -1 for error. */
141
The total amount of virtual memory space that this process uses. This includes shared and
142
swapped memory, plus graphics memory and mmap'ed files and so on.
146
unsigned long vmSize;
149
The amount of physical memory the process currently uses, including the physical memory used by any
150
shared libraries that it uses. Hence 2 processes sharing a library will both report their vmRss as including
151
this shared memory, even though it's only allocated once.
158
/** The amount of physical memory that is used by this process, not including any memory used by any shared libraries.
160
unsigned long vmURss;
163
The number of 1/100 of a second the process has spend in user space.
164
If a machine has an uptime of 1 1/2 years or longer this is not a
165
good idea. I never thought that the stability of UNIX could get me
168
unsigned long userTime;
171
The number of 1/100 of a second the process has spent in system space.
172
If a machine has an uptime of 1 1/2 years or longer this is not a
173
good idea. I never thought that the stability of UNIX could get me
176
unsigned long sysTime;
178
/* NOTE: To get the user/system percentage, record the userTime and sysTime from between calls, then use the difference divided by the difference in time measure in 100th's of a second */
180
/** The name of the process */
183
/** The command used to start the process */
186
/** The login name of the user that owns this process */
191
void getIOnice( int pid, ProcessInfo *ps );
192
void ioniceProcess( const char* cmd );
194
static unsigned ProcessCount;
196
static void validateStr( char* str )
200
/* All characters that could screw up the communication will be removed. */
202
if ( *s == '\t' || *s == '\n' || *s == '\r' )
207
/* Make sure that string contains at least one character (blank). */
208
if ( str[ 0 ] == '\0' )
212
static bool getProcess( int pid, ProcessInfo *ps )
218
char tagformat[ 32 ];
222
snprintf( buf, BUFSIZE - 1, "/proc/%d/status", pid );
223
if ( ( fd = fopen( buf, "r" ) ) == 0 ) {
224
/* process has terminated in the mean time */
231
sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 );
232
sprintf( tagformat, "%%%ds", (int)sizeof( tag ) - 1 );
234
if ( fscanf( fd, format, buf ) != 1 )
236
buf[ sizeof( buf ) - 1 ] = '\0';
237
sscanf( buf, tagformat, tag );
238
tag[ sizeof( tag ) - 1 ] = '\0';
239
if ( strcmp( tag, "Name:" ) == 0 ) {
240
sscanf( buf, "%*s %63s", ps->name );
241
validateStr( ps->name );
242
} else if ( strcmp( tag, "Uid:" ) == 0 ) {
243
sscanf( buf, "%*s %d %*d %*d %*d", (int*)&ps->uid );
244
} else if ( strcmp( tag, "Gid:" ) == 0 ) {
245
sscanf( buf, "%*s %d %*d %*d %*d", (int*)&ps->gid );
246
} else if ( strcmp( tag, "TracerPid:" ) == 0 ) {
247
sscanf( buf, "%*s %d", (int*)&ps->tracerpid );
248
if (ps->tracerpid == 0)
249
ps->tracerpid = -1; /* ksysguard uses -1 to indicate no tracerpid, but linux uses 0 */
256
snprintf( buf, BUFSIZE - 1, "/proc/%d/stat", pid );
257
buf[ BUFSIZE - 1 ] = '\0';
258
if ( ( fd = fopen( buf, "r" ) ) == 0 )
261
if ( fscanf( fd, "%*d %*s %c %d %*d %*d %d %*d %*u %*u %*u %*u %*u %lu %lu"
262
"%*d %*d %*d %d %*u %*u %*d %lu %lu",
263
&status, (int*)&ps->ppid, &ttyNo,
264
&ps->userTime, &ps->sysTime, &ps->niceLevel, &ps->vmSize,
269
if (ps->ppid == 0) /* ksysguard uses -1 to indicate no parent, but linux uses 0 */
271
int major = ttyNo >> 8;
272
int minor = ttyNo & 0xff;
275
snprintf(ps->tty, sizeof(ps->tty)-1, "pts/%d", minor);
279
snprintf(ps->tty, sizeof(ps->tty)-1, "tty/%d", minor);
281
snprintf(ps->tty, sizeof(ps->tty)-1, "ttyS/%d", minor-64);
287
/*There was a "(ps->vmRss+3) * sysconf(_SC_PAGESIZE)" here originally. I have no idea why! After comparing it to
288
meminfo and other tools, this means we report the RSS by 12 bytes different compared to them. So I'm removing the +3
289
to be consistent. NEXT TIME COMMENT STRANGE THINGS LIKE THAT! :-)
291
Update: I think I now know why. The kernel reserves 3kb for process information.
293
ps->vmRss = ps->vmRss * sysconf(_SC_PAGESIZE) / 1024; /*convert to KiB*/
294
ps->vmSize /= 1024; /* convert to KiB */
299
snprintf( buf, BUFSIZE - 1, "/proc/%d/statm", pid );
300
buf[ BUFSIZE - 1 ] = '\0';
302
if ( ( fd = fopen( buf, "r" ) ) != 0 ) {
303
unsigned long shared;
304
if ( fscanf( fd, "%*d %*u %lu",
306
/* we use the rss - shared to find the amount of memory just this app uses */
307
ps->vmURss = ps->vmRss - (shared * sysconf(_SC_PAGESIZE) / 1024);
313
/* status decoding as taken from fs/proc/array.c */
315
strcpy( ps->status, "running" );
316
else if ( status == 'S' )
317
strcpy( ps->status, "sleeping" );
318
else if ( status == 'D' )
319
strcpy( ps->status, "disk sleep" );
320
else if ( status == 'Z' )
321
strcpy( ps->status, "zombie" );
322
else if ( status == 'T' )
323
strcpy( ps->status, "stopped" );
324
else if ( status == 'W' )
325
strcpy( ps->status, "paging" );
327
sprintf( ps->status, "Unknown: %c", status );
330
snprintf( buf, BUFSIZE - 1, "/proc/%d/cmdline", pid );
331
if ( ( fd = fopen( buf, "r" ) ) == 0 )
334
ps->cmdline[ 0 ] = '\0';
337
while( (ps->cmdline[i] = fgetc(fd)) != EOF && i < sizeof(ps->cmdline)-3) {
338
if(ps->cmdline[i] == '\0')
339
ps->cmdline[i] = ' ';
345
if(ps->cmdline[i-2] == ' ') ps->cmdline[i-2] = '\0';
346
else ps->cmdline[i-1] = '\0';
348
ps->cmdline[0] = '\0';
351
validateStr( ps->cmdline );
355
/* Ugly hack to "fix" program name for kdeinit launched programs. */
356
if ( strcmp( ps->name, "kdeinit" ) == 0 &&
357
strncmp( ps->cmdline, "kdeinit: ", KDEINITLEN ) == 0 &&
358
strcmp( ps->cmdline + KDEINITLEN, "Running..." ) != 0 ) {
360
char* end = strchr( ps->cmdline + KDEINITLEN, ' ' );
362
len = ( end - ps->cmdline ) - KDEINITLEN;
364
len = strlen( ps->cmdline + KDEINITLEN );
366
if ( len > sizeof( ps->name ) - 1 )
367
len = sizeof( ps->name ) - 1;
368
strncpy( ps->name, ps->cmdline + KDEINITLEN, len );
369
ps->name[ len ] = '\0';
372
/* find out user name with the process uid */
373
uName = getCachedPWUID( ps->uid );
374
strncpy( ps->userName, uName, sizeof( ps->userName ) - 1 );
375
ps->userName[ sizeof( ps->userName ) - 1 ] = '\0';
376
validateStr( ps->userName );
383
void printProcessList( const char* cmd)
386
struct dirent* entry;
391
while ( ( entry = readdir( procDir ) ) ) {
392
if ( isdigit( entry->d_name[ 0 ] ) ) {
394
pid = atol( entry->d_name );
395
if(getProcess( pid, &ps )) /* Print out the details of the process. Because of a stupid bug in kde3 ksysguard, make sure cmdline and tty are not empty */
396
output( "%s\t%ld\t%ld\t%lu\t%lu\t%s\t%lu\t%lu\t%d\t%lu\t%lu\t%lu\t%s\t%ld\t%s\t%s\t%d\t%d\n",
397
ps.name, pid, (long)ps.ppid,
398
(long)ps.uid, (long)ps.gid, ps.status, ps.userTime,
399
ps.sysTime, ps.niceLevel, ps.vmSize, ps.vmRss, ps.vmURss,
400
(ps.userName[0]==0)?" ":ps.userName, (long)ps.tracerpid,
401
(ps.tty[0]==0)?" ":ps.tty, (ps.cmdline[0]==0)?" ":ps.cmdline,
402
ps.ioPriorityClass, ps.ioPriority
410
void getIOnice( int pid, ProcessInfo *ps ) {
412
int ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid); /* Returns from 0 to 7 for the iopriority, and -1 if there's an error */
415
ps->ioPriorityClass = -1;
416
return; /* Error. Just give up. */
418
ps->ioPriority = ioprio & 0xff; /* Bottom few bits are the priority */
419
ps->ioPriorityClass = ioprio >> IOPRIO_CLASS_SHIFT; /* Top few bits are the class */
421
return; /* Do nothing, if we do not support this architecture */
427
================================ public part =================================
430
void initProcessList( struct SensorModul* sm )
434
registerMonitor( "pscount", "integer", printProcessCount, printProcessCountInfo, sm );
435
registerMonitor( "ps", "table", printProcessList, printProcessListInfo, sm );
437
if ( !RunAsDaemon ) {
438
registerCommand( "kill", killProcess );
439
registerCommand( "setpriority", setPriority );
441
registerCommand( "ionice", ioniceProcess );
445
/*open /proc now in advance*/
446
/* read in current process list via the /proc file system entry */
447
if ( ( procDir = opendir( "/proc" ) ) == NULL ) {
448
print_error( "Cannot open directory \'/proc\'!\n"
449
"The kernel needs to be compiled with support\n"
450
"for /proc file system enabled!\n" );
455
void exitProcessList( void )
457
removeMonitor( "ps" );
458
removeMonitor( "pscount" );
460
if ( !RunAsDaemon ) {
461
removeCommand( "kill" );
462
removeCommand( "setpriority" );
469
void printProcessListInfo( const char* cmd )
472
output( "Name\tPID\tPPID\tUID\tGID\tStatus\tUser Time\tSystem Time\tNice\tVmSize"
473
"\tVmRss\tVmURss\tLogin\tTracerPID\tTTY\tCommand\tIO Priority Class\tIO Priority\n" );
474
output( "s\td\td\td\td\tS\td\td\td\tD\tD\tD\ts\td\ts\ts\td\td\n" );
477
void printProcessCount( const char* cmd )
480
struct dirent* entry;
483
while ( ( entry = readdir( procDir ) ) )
484
if ( isdigit( entry->d_name[ 0 ] ) )
488
output( "%d\n", ProcessCount );
491
void printProcessCountInfo( const char* cmd )
494
output( "Number of Processes\t0\t0\t\n" );
497
void killProcess( const char* cmd )
499
/* Sends a signal (not necessarily kill!) to the process. cmd is a string containing "kill <pid> <signal>" */
502
sscanf( cmd, "%*s %d %d", &pid, &sig );
504
case MENU_ID_SIGABRT:
507
case MENU_ID_SIGALRM:
510
case MENU_ID_SIGCHLD:
513
case MENU_ID_SIGCONT:
528
case MENU_ID_SIGKILL:
531
case MENU_ID_SIGPIPE:
534
case MENU_ID_SIGQUIT:
537
case MENU_ID_SIGSEGV:
540
case MENU_ID_SIGSTOP:
543
case MENU_ID_SIGTERM:
546
case MENU_ID_SIGTSTP:
549
case MENU_ID_SIGTTIN:
552
case MENU_ID_SIGTTOU:
555
case MENU_ID_SIGUSR1:
558
case MENU_ID_SIGUSR2:
563
if ( kill( (pid_t)pid, sig ) ) {
566
output( "4\t%d\n", pid );
569
output( "3\t%d\n", pid );
573
exit(0);/* Won't execute unless execve fails. Need this for the parent process to continue */
575
output( "2\t%d\n", pid );
577
default: /* unknown error */
578
output( "1\t%d\n", pid );
582
output( "0\t%d\n", pid );
585
void setPriority( const char* cmd )
588
/** as: setpriority <pid> <priority> */
589
sscanf( cmd, "%*s %d %d", &pid, &prio );
590
if ( setpriority( PRIO_PROCESS, pid, prio ) ) {
593
output( "4\t%d\t%d\n", pid, prio );
596
output( "3\t%d\t%d\nn", pid, prio );
600
output( "2\t%d\t%d\n", pid, prio );
602
default: /* unknown error */
603
output( "1\t%d\t%d\n", pid, prio );
607
output( "0\t%d\t%d\n",pid, prio );
610
void ioniceProcess( const char* cmd )
612
/* Re-ionice's a process. cmd is a string containing:
614
* ionice <pid> <class> <priority>
616
* where c = 1 for real time, 2 for best-effort, 3 for idle
617
* and priority is between 0 and 7, 0 being the highest priority, and ignored if c=3
619
* For more information, see: man ionice
625
if(sscanf( cmd, "%*s %d %d %d", &pid, &class, &priority ) < 2) {
626
output( "4\t%d\n", pid ); /* 4 means error in values */
627
return; /* Error with input. */
631
if(pid < 1 || class < 0 || class > 3) {
632
output( "4\t%d\n", pid ); /* 4 means error in values */
633
return; /* Error with input. Just ignore. */
636
if (ioprio_set(IOPRIO_WHO_PROCESS, pid, priority | class << IOPRIO_CLASS_SHIFT) == -1) {
639
output( "4\t%d\n", pid );
642
output( "3\t%d\n", pid );
645
output( "2\t%d\n", pid );
647
default: /* unknown error */
648
output( "1\t%d\n", pid );
653
output( "0\t%d\n", pid );
657
/** should never reach here */
658
output( "1\t%d\n", pid );