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
);
106 #include "config-ksysguardd.h" /*For HAVE_XRES*/
108 extern int setup_xres();
109 extern void xrestop_populate_client_data();
110 extern void printXres(FILE *CurrentClient
);
111 static int have_xres
= 0;
116 /** The parent process ID */
119 /** The real user ID */
122 /** The real group ID */
125 /** The process ID of any application that is debugging this one. 0 if none */
128 /** A character description of the process status */
131 /** The tty the process owns */
135 The nice level. The range should be -20 to 20. I'm not sure
136 whether this is true for all platforms.
140 /** The scheduling priority. */
143 /** The i/o scheduling class and priority. */
144 int ioPriorityClass
; /** 0 for none, 1 for realtime, 2 for best-effort, 3 for idle. -1 for error. */
145 int ioPriority
; /** Between 0 and 7. 0 is highest priority, 7 is lowest. -1 for error. */
148 The total amount of virtual memory space that this process uses. This includes shared and
149 swapped memory, plus graphics memory and mmap'ed files and so on.
153 unsigned long vmSize
;
156 The amount of physical memory the process currently uses, including the physical memory used by any
157 shared libraries that it uses. Hence 2 processes sharing a library will both report their vmRss as including
158 this shared memory, even though it's only allocated once.
165 /** The amount of physical memory that is used by this process, not including any memory used by any shared libraries.
167 unsigned long vmURss
;
170 The number of 1/100 of a second the process has spend in user space.
171 If a machine has an uptime of 1 1/2 years or longer this is not a
172 good idea. I never thought that the stability of UNIX could get me
175 unsigned long userTime
;
178 The number of 1/100 of a second the process has spent in system space.
179 If a machine has an uptime of 1 1/2 years or longer this is not a
180 good idea. I never thought that the stability of UNIX could get me
183 unsigned long sysTime
;
185 /* 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 */
187 /** The name of the process */
190 /** The command used to start the process */
193 /** The login name of the user that owns this process */
198 void getIOnice( int pid
, ProcessInfo
*ps
);
199 void ioniceProcess( const char* cmd
);
201 static unsigned ProcessCount
;
203 static void validateStr( char* str
)
207 /* All characters that could screw up the communication will be removed. */
209 if ( *s
== '\t' || *s
== '\n' || *s
== '\r' )
214 /* Make sure that string contains at least one character (blank). */
215 if ( str
[ 0 ] == '\0' )
219 static bool getProcess( int pid
, ProcessInfo
*ps
)
225 char tagformat
[ 32 ];
229 snprintf( buf
, BUFSIZE
- 1, "/proc/%d/status", pid
);
230 if ( ( fd
= fopen( buf
, "r" ) ) == 0 ) {
231 /* process has terminated in the mean time */
238 sprintf( format
, "%%%d[^\n]\n", (int)sizeof( buf
) - 1 );
239 sprintf( tagformat
, "%%%ds", (int)sizeof( tag
) - 1 );
241 if ( fscanf( fd
, format
, buf
) != 1 )
243 buf
[ sizeof( buf
) - 1 ] = '\0';
244 sscanf( buf
, tagformat
, tag
);
245 tag
[ sizeof( tag
) - 1 ] = '\0';
246 if ( strcmp( tag
, "Name:" ) == 0 ) {
247 sscanf( buf
, "%*s %63s", ps
->name
);
248 validateStr( ps
->name
);
249 } else if ( strcmp( tag
, "Uid:" ) == 0 ) {
250 sscanf( buf
, "%*s %d %*d %*d %*d", (int*)&ps
->uid
);
251 } else if ( strcmp( tag
, "Gid:" ) == 0 ) {
252 sscanf( buf
, "%*s %d %*d %*d %*d", (int*)&ps
->gid
);
253 } else if ( strcmp( tag
, "TracerPid:" ) == 0 ) {
254 sscanf( buf
, "%*s %d", (int*)&ps
->tracerpid
);
261 snprintf( buf
, BUFSIZE
- 1, "/proc/%d/stat", pid
);
262 buf
[ BUFSIZE
- 1 ] = '\0';
263 if ( ( fd
= fopen( buf
, "r" ) ) == 0 )
266 if ( fscanf( fd
, "%*d %*s %c %d %*d %*d %d %*d %*u %*u %*u %*u %*u %lu %lu"
267 "%*d %*d %*d %d %*u %*u %*d %lu %lu",
268 &status
, (int*)&ps
->ppid
, &ttyNo
,
269 &ps
->userTime
, &ps
->sysTime
, &ps
->niceLevel
, &ps
->vmSize
,
274 int major
= ttyNo
>> 8;
275 int minor
= ttyNo
& 0xff;
278 snprintf(ps
->tty
, sizeof(ps
->tty
)-1, "pts/%d", minor
);
282 snprintf(ps
->tty
, sizeof(ps
->tty
)-1, "tty/%d", minor
);
284 snprintf(ps
->tty
, sizeof(ps
->tty
)-1, "ttyS/%d", minor
-64);
290 /*There was a "(ps->vmRss+3) * sysconf(_SC_PAGESIZE)" here originally. I have no idea why! After comparing it to
291 meminfo and other tools, this means we report the RSS by 12 bytes different compared to them. So I'm removing the +3
292 to be consistent. NEXT TIME COMMENT STRANGE THINGS LIKE THAT! :-)*/
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 fprintf( CurrentClient
, "%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
406 fprintf( CurrentClient
, "\n" );
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
);
446 have_xres
= setup_xres();
448 registerLegacyMonitor( "xres", "table", printXresList
, printXresListInfo
, sm
);
452 /*open /proc now in advance*/
453 /* read in current process list via the /proc file system entry */
454 if ( ( procDir
= opendir( "/proc" ) ) == NULL
) {
455 print_error( "Cannot open directory \'/proc\'!\n"
456 "The kernel needs to be compiled with support\n"
457 "for /proc file system enabled!\n" );
462 void exitProcessList( void )
464 removeMonitor( "ps" );
465 removeMonitor( "pscount" );
469 removeMonitor( "xres" );
471 if ( !RunAsDaemon
) {
472 removeCommand( "kill" );
473 removeCommand( "setpriority" );
479 void printXresListInfo( const char *cmd
)
482 fprintf(CurrentClient
, "XPid\tXIdentifier\tXPxmMem\tXNumPxm\tXMemOther\n");
483 fprintf(CurrentClient
, "d\ts\tD\td\tD\n");
486 void printXresList(const char*cmd
)
489 printXres(CurrentClient
);
494 void printProcessListInfo( const char* cmd
)
497 fprintf( CurrentClient
, "Name\tPID\tPPID\tUID\tGID\tStatus\tUser Time\tSystem Time\tNice\tVmSize"
498 "\tVmRss\tVmURss\tLogin\tTracerPID\tTTY\tCommand\tIO Priority Class\tIO Priority\n" );
499 fprintf( CurrentClient
, "s\td\td\td\td\tS\td\td\td\tD\tD\tD\ts\td\ts\ts\td\td\n" );
502 void printProcessCount( const char* cmd
)
505 struct dirent
* entry
;
508 while ( ( entry
= readdir( procDir
) ) )
509 if ( isdigit( entry
->d_name
[ 0 ] ) )
513 fprintf( CurrentClient
, "%d\n", ProcessCount
);
516 void printProcessCountInfo( const char* cmd
)
519 fprintf( CurrentClient
, "Number of Processes\t0\t0\t\n" );
522 void killProcess( const char* cmd
)
524 /* Sends a signal (not neccessarily kill!) to the process. cmd is a string containing "kill <pid> <signal>" */
527 sscanf( cmd
, "%*s %d %d", &pid
, &sig
);
529 case MENU_ID_SIGABRT
:
532 case MENU_ID_SIGALRM
:
535 case MENU_ID_SIGCHLD
:
538 case MENU_ID_SIGCONT
:
553 case MENU_ID_SIGKILL
:
556 case MENU_ID_SIGPIPE
:
559 case MENU_ID_SIGQUIT
:
562 case MENU_ID_SIGSEGV
:
565 case MENU_ID_SIGSTOP
:
568 case MENU_ID_SIGTERM
:
571 case MENU_ID_SIGTSTP
:
574 case MENU_ID_SIGTTIN
:
577 case MENU_ID_SIGTTOU
:
580 case MENU_ID_SIGUSR1
:
583 case MENU_ID_SIGUSR2
:
588 if ( kill( (pid_t
)pid
, sig
) ) {
591 fprintf( CurrentClient
, "4\t%d\n", pid
);
594 fprintf( CurrentClient
, "3\t%d\n", pid
);
598 exit(0);/* Won't execute unless execve fails. Need this for the parent process to continue */
600 fprintf( CurrentClient
, "2\t%d\n", pid
);
602 default: /* unknown error */
603 fprintf( CurrentClient
, "1\t%d\n", pid
);
607 fprintf( CurrentClient
, "0\t%d\n", pid
);
610 void setPriority( const char* cmd
)
613 /** as: setpriority <pid> <priority> */
614 sscanf( cmd
, "%*s %d %d", &pid
, &prio
);
615 if ( setpriority( PRIO_PROCESS
, pid
, prio
) ) {
618 fprintf( CurrentClient
, "4\t%d\t%d\n", pid
, prio
);
621 fprintf( CurrentClient
, "3\t%d\t%d\nn", pid
, prio
);
625 fprintf( CurrentClient
, "2\t%d\t%d\n", pid
, prio
);
627 default: /* unknown error */
628 fprintf( CurrentClient
, "1\t%d\t%d\n", pid
, prio
);
632 fprintf( CurrentClient
, "0\t%d\t%d\n",pid
, prio
);
635 void ioniceProcess( const char* cmd
)
637 /* Re-ionice's a process. cmd is a string containing:
639 * ionice <pid> <class> <priority>
641 * where c = 1 for real time, 2 for best-effort, 3 for idle
642 * and priority is between 0 and 7, 0 being the highest priority, and ignored if c=3
644 * For more information, see: man ionice
650 if(sscanf( cmd
, "%*s %d %d %d", &pid
, &class, &priority
) < 2) {
651 fprintf( CurrentClient
, "4\t%d\n", pid
); /* 4 means error in values */
652 return; /* Error with input. */
656 if(pid
< 1 || class < 0 || class > 3) {
657 fprintf( CurrentClient
, "4\t%d\n", pid
); /* 4 means error in values */
658 return; /* Error with input. Just ignore. */
661 if (ioprio_set(IOPRIO_WHO_PROCESS
, pid
, priority
| class << IOPRIO_CLASS_SHIFT
) == -1) {
664 fprintf( CurrentClient
, "4\t%d\n", pid
);
667 fprintf( CurrentClient
, "3\t%d\n", pid
);
670 fprintf( CurrentClient
, "2\t%d\n", pid
);
672 default: /* unknown error */
673 fprintf( CurrentClient
, "1\t%d\n", pid
);
678 fprintf( CurrentClient
, "0\t%d\n", pid
);
682 /** should never reach here */
683 fprintf( CurrentClient
, "1\t%d\n", pid
);