/* KSysGuard, the KDE System Guard Copyright (c) 1999, 2000 Chris Schlaeger Irix support by Carsten Kroll This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ccont.h" #include "../../gui/SignalIDs.h" #include "ksysguardd.h" #include "Command.h" #include "ProcessList.h" #define BUFSIZE 1024 #define KDEINITLEN strlen("kdeinit: ") typedef struct { int alive; /* for "garbage collection" */ pid_t pid; /* process ID */ pid_t ppid; /* parent process ID */ uid_t uid; /* process owner (real UID) */ gid_t gid; /* process group (real GID) */ char *userName; /* process owner (name) */ int nThreads; /* # of threads in this process */ int Prio; /* scheduling priority */ size_t Size; /* total size of process image */ size_t RSSize; /* resident set size */ char State[8]; /* process state */ double Time; /* CPU time for the process in 100ms */ double Load; /* CPU load in % */ char Command[PRCOMSIZ];/* command name */ char CmdLine[PRARGSZ];/* command line */ double centStamp; /* timestamp for CPU load */ } ProcessInfo; static CONTAINER ProcessList = 0; static unsigned ProcessCount = 0; /* # of processes */ static DIR *procdir; /* handle for /proc */ static int pagesz; #define KBYTES 1024 /* * lwpStateName() -- return string representation of process state */ char *lwpStateName( prpsinfo_t lwpinfo ) { static char result[8]; switch( lwpinfo.pr_sname ) { case 'S': sprintf( result, "%s", "sleep" ); break; case 'R': sprintf( result, "%s", "run" ); break; case 'Z': sprintf( result, "%s", "zombie" ); break; case 'T': sprintf( result, "%s", "stop" ); break; case 'I': sprintf( result, "%s", "start" ); break; case 'X': sprintf( result, "%s", "wmem" ); case '0': sprintf( result, "%s/%d", "cpu", (int) lwpinfo.pr_sonproc ); break; default: sprintf( result, "%s", "???" ); break; } return( result ); } static void validateStr( char *string ) { char *ptr = string; /* * remove all chars that might screw up communication */ while( *ptr != '\0' ) { if( *ptr == '\t' || *ptr == '\n' || *ptr == '\r' ) *ptr = ' '; ptr++; } /* * make sure there's at least one char */ if( string[0] == '\0' ) strcpy( string, " " ); } static int processCmp( void *p1, void *p2 ) { return( ((ProcessInfo *) p1)->pid - ((ProcessInfo *) p2)->pid ); } static ProcessInfo *findProcessInList( pid_t pid ) { ProcessInfo key; long index; key.pid = pid; if( (index = search_ctnr( ProcessList, processCmp, &key )) < 0 ) return( NULL ); return( get_ctnr( ProcessList, index )); } static int updateProcess( pid_t pid ) { ProcessInfo *ps; int fd; char buf[BUFSIZE]; prpsinfo_t psinfo; struct passwd *pw; register double newCentStamp,timeDiff, usDiff,usTime; struct timeval tv; if( (ps = findProcessInList( pid )) == NULL ) { if( (ps = (ProcessInfo *) malloc( sizeof( ProcessInfo ))) == NULL ) { print_error( "cannot malloc()\n" ); return( -1 ); } ps->pid = pid; ps->userName = NULL; ps->alive = 0; gettimeofday(&tv, 0); ps->centStamp = (double)tv.tv_sec * 100.0 + (double)tv.tv_usec / 10000.0; push_ctnr( ProcessList, ps ); bsort_ctnr( ProcessList, processCmp ); } sprintf( buf, "%s/pinfo/%ld", PROCDIR, pid ); if( (fd = open( buf, O_RDONLY )) < 0 ) { /* process terminated */ return( -1 ); } if( ioctl(fd,PIOCPSINFO,&psinfo) < 0) { print_error( "cannot read psinfo from \"%s\"\n", buf ); close( fd ); return( -1 ); } close( fd ); ps->ppid = psinfo.pr_ppid; ps->uid = psinfo.pr_uid; ps->gid = psinfo.pr_gid; pw = getpwuid( psinfo.pr_uid ); if( ps->userName != NULL ) free( ps->userName ); ps->userName = strdup( pw->pw_name ); strncpy (ps->State,lwpStateName( psinfo ),8); ps->State[7]='\0'; ps->Prio = psinfo.pr_pri; gettimeofday(&tv, 0); newCentStamp = (double)tv.tv_sec * 100.0 + (double) tv.tv_usec / 10000.0; usTime = (double) psinfo.pr_time.tv_sec * 100.0 + (double)psinfo.pr_time.tv_nsec / 10000000.0; timeDiff = newCentStamp - ps->centStamp; usDiff = usTime - ps->Time; if ((timeDiff > 0.0) && (usDiff >= 0.0)) { ps->Load = (usDiff / timeDiff) * 100.0; /* During startup we get bigger loads since the time diff * cannot be correct. So we force it to 0. */ ps->Load = (ps->Load > 100.0) ? 0.0 : ps->Load; } else ps->Load = 0.0; ps->centStamp = newCentStamp; ps->Time = usTime; ps->Size = (psinfo.pr_size * pagesz)/KBYTES; ps->RSSize = (psinfo.pr_rssize * pagesz)/KBYTES; strncpy(ps->Command,psinfo.pr_fname,PRCOMSIZ); ps->Command[PRCOMSIZ-1]='\0'; strncpy(ps->CmdLine,psinfo.pr_psargs,PRARGSZ); ps->CmdLine[PRARGSZ-1]='\0'; validateStr( ps->Command ); validateStr( ps->CmdLine ); ps->alive = 1; return( 0 ); } static void cleanupProcessList( void ) { ProcessInfo *ps; ProcessCount = 0; for( ps = first_ctnr( ProcessList ); ps; ps = next_ctnr( ProcessList )) { if( ps->alive ) { ps->alive = 0; ProcessCount++; } else { free( remove_ctnr( ProcessList )); } } } void initProcessList( struct SensorModul* sm ) { if( (procdir = opendir( PROCDIR )) == NULL ) { print_error( "cannot open \"%s\" for reading\n", PROCDIR ); return; } pagesz=getpagesize(); ProcessList = new_ctnr(); updateProcessList(); /* * register the supported monitors & commands */ registerMonitor( "pscount", "integer", printProcessCount, printProcessCountInfo, sm ); registerMonitor( "ps", "table", printProcessList, printProcessListInfo, sm ); if (!RunAsDaemon) { registerCommand("kill", killProcess); registerCommand("setpriority", setPriority); } } void exitProcessList( void ) { removeMonitor("ps"); removeMonitor("pscount"); if (!RunAsDaemon) { removeCommand("kill"); removeCommand("setpriority"); } destr_ctnr( ProcessList, free ); } int updateProcessList( void ) { struct dirent *de; struct statfs sf; statfs("/proc/pinfo",&sf,sizeof(sf),0); ProcessCount = sf.f_files; rewinddir( procdir ); while( (de = readdir( procdir )) != NULL ) { /* * skip '.' and '..' */ if( de->d_name[0] == '.' ) continue; /* * fetch the process info and insert it into the info table */ updateProcess( (pid_t) atol( de->d_name )); } cleanupProcessList(); return( 0 ); } void printProcessListInfo( const char *cmd ) { fprintf(CurrentClient, "Name\tPID\tPPID\tGID\ttqStatus\tUser" "\tSize\tResident\t%% CPU\tPriority\tCommand\n" ); fprintf(CurrentClient, "s\td\td\td\ts\ts\tD\tD\tf\td\ts\n" ); } void printProcessList( const char *cmd ) { ProcessInfo *ps; for( ps = first_ctnr( ProcessList ); ps; ps = next_ctnr( ProcessList )) { fprintf(CurrentClient, "%s\t%ld\t%ld\t%ld\t%s\t%s\t%d\t%d\t%.2f\t%d\t%s\n", ps->Command, (long) ps->pid, (long) ps->ppid, (long) ps->gid, ps->State, ps->userName, ps->Size, ps->RSSize, ps->Load, ps->Prio, ps->CmdLine); } fprintf(CurrentClient, "\n"); } void printProcessCount( const char *cmd ) { fprintf(CurrentClient, "%d\n", ProcessCount ); } void printProcessCountInfo( const char *cmd ) { fprintf(CurrentClient, "Number of Processes\t0\t0\t\n" ); } void killProcess( const char *cmd ) { int sig, pid; sscanf( cmd, "%*s %d %d", &pid, &sig ); switch( sig ) { case MENU_ID_SIGABRT: sig = SIGABRT; break; case MENU_ID_SIGALRM: sig = SIGALRM; break; case MENU_ID_SIGCHLD: sig = SIGCHLD; break; case MENU_ID_SIGCONT: sig = SIGCONT; break; case MENU_ID_SIGFPE: sig = SIGFPE; break; case MENU_ID_SIGHUP: sig = SIGHUP; break; case MENU_ID_SIGILL: sig = SIGILL; break; case MENU_ID_SIGINT: sig = SIGINT; break; case MENU_ID_SIGKILL: sig = SIGKILL; break; case MENU_ID_SIGPIPE: sig = SIGPIPE; break; case MENU_ID_SIGQUIT: sig = SIGQUIT; break; case MENU_ID_SIGSEGV: sig = SIGSEGV; break; case MENU_ID_SIGSTOP: sig = SIGSTOP; break; case MENU_ID_SIGTERM: sig = SIGTERM; break; case MENU_ID_SIGTSTP: sig = SIGTSTP; break; case MENU_ID_SIGTTIN: sig = SIGTTIN; break; case MENU_ID_SIGTTOU: sig = SIGTTOU; break; case MENU_ID_SIGUSR1: sig = SIGUSR1; break; case MENU_ID_SIGUSR2: sig = SIGUSR2; break; } if( kill( (pid_t) pid, sig )) { switch( errno ) { case EINVAL: fprintf(CurrentClient, "4\n" ); break; case ESRCH: fprintf(CurrentClient, "3\n" ); break; case EPERM: fprintf(CurrentClient, "2\n" ); break; default: fprintf(CurrentClient, "1\n" ); /* unknown error */ break; } } else fprintf(CurrentClient, "0\n"); } void setPriority( const char *cmd ) { int pid, prio; sscanf( cmd, "%*s %d %d", &pid, &prio ); if( setpriority( PRIO_PROCESS, pid, prio )) { switch( errno ) { case EINVAL: fprintf(CurrentClient, "4\n" ); break; case ESRCH: fprintf(CurrentClient, "3\n" ); break; case EPERM: case EACCES: fprintf(CurrentClient, "2\n" ); break; default: fprintf(CurrentClient, "1\n" ); /* unknown error */ break; } } else fprintf(CurrentClient, "0\n"); }