Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / libs / ksysguard / processcore / processes_linux_p.cpp
blobd2c33d7e47ce1ff8286b6a9aa1f378d17e3f25f0
1 /* This file is part of the KDE project
3 Copyright (C) 2007 John Tapsell <tapsell@kde.org>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library 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 GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 #include "processes_local_p.h"
23 #include "process.h"
25 #include <klocale.h>
27 #include <QFile>
28 #include <QHash>
29 #include <QSet>
30 #include <QMutableSetIterator>
31 #include <QByteArray>
32 #include <QTextStream>
34 //for sysconf
35 #include <unistd.h>
36 //for kill and setNice
37 #include <sys/types.h>
38 #include <signal.h>
39 #include <sys/resource.h>
40 #include <dirent.h>
41 #include <stdlib.h>
42 //for ionice
43 #include <sys/ptrace.h>
44 #include <asm/unistd.h>
45 //for getsched
46 #include <sched.h>
48 #define PROCESS_BUFFER_SIZE 1000
50 /* For ionice */
51 extern int sys_ioprio_set(int, int, int);
52 extern int sys_ioprio_get(int, int);
54 #define HAVE_IONICE
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 */
58 #if defined(__i386__)
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
70 #else
71 #ifdef __GNUC__
72 #warning "This architecture does not support IONICE. Disabling ionice feature."
73 #endif
74 #undef HAVE_IONICE
75 #endif
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 */
83 #ifdef HAVE_IONICE
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);
98 #endif
103 namespace KSysGuard
106 class ProcessesLocal::Private
108 public:
109 Private() { mProcDir = opendir( "/proc" );}
110 ~Private();
111 inline bool readProcStatus(long pid, Process *process);
112 inline bool readProcStat(long pid, Process *process);
113 inline bool readProcStatm(long pid, Process *process);
114 inline bool readProcCmdline(long pid, Process *process);
115 inline bool getNiceness(long pid, Process *process);
116 QFile mFile;
117 char mBuffer[PROCESS_BUFFER_SIZE+1]; //used as a buffer to read data into
118 DIR* mProcDir;
121 ProcessesLocal::Private::~Private()
123 closedir(mProcDir);
126 ProcessesLocal::ProcessesLocal() : d(new Private())
131 bool ProcessesLocal::Private::readProcStatus(long pid, Process *process)
133 mFile.setFileName(QString("/proc/%1/status").arg(pid));
134 if(!mFile.open(QIODevice::ReadOnly | QIODevice::Text))
135 return false; /* process has terminated in the meantime */
137 process->uid = 0;
138 process->gid = 0;
139 process->tracerpid = 0;
141 int size;
142 int found = 0; //count how many fields we found
143 while( (size = mFile.readLine( mBuffer, sizeof(mBuffer))) > 0) { //-1 indicates an error
144 switch( mBuffer[0]) {
145 case 'N':
146 if((unsigned int)size > sizeof("Name:") && qstrncmp(mBuffer, "Name:", sizeof("Name:")-1) == 0) {
147 process->name = QString::fromLocal8Bit(mBuffer + sizeof("Name:")-1, size-sizeof("Name:")+1).trimmed();
148 if(++found == 4) goto finish;
150 break;
151 case 'U':
152 if((unsigned int)size > sizeof("Uid:") && qstrncmp(mBuffer, "Uid:", sizeof("Uid:")-1) == 0) {
153 sscanf(mBuffer + sizeof("Uid:") -1, "%Ld %Ld %Ld %Ld", &process->uid, &process->euid, &process->suid, &process->fsuid );
154 if(++found == 4) goto finish;
156 break;
157 case 'G':
158 if((unsigned int)size > sizeof("Gid:") && qstrncmp(mBuffer, "Gid:", sizeof("Gid:")-1) == 0) {
159 sscanf(mBuffer + sizeof("Gid:")-1, "%Ld %Ld %Ld %Ld", &process->gid, &process->egid, &process->sgid, &process->fsgid );
160 if(++found == 4) goto finish;
162 break;
163 case 'T':
164 if((unsigned int)size > sizeof("TracerPid:") && qstrncmp(mBuffer, "TracerPid:", sizeof("TracerPid:")-1) == 0) {
165 process->tracerpid = atol(mBuffer + sizeof("TracerPid:")-1);
166 if(++found == 4) goto finish;
168 break;
169 default:
170 break;
174 finish:
175 mFile.close();
176 return true;
179 long ProcessesLocal::getParentPid(long pid) {
180 Q_ASSERT(pid != 0);
181 d->mFile.setFileName(QString("/proc/%1/stat").arg(pid));
182 if(!d->mFile.open(QIODevice::ReadOnly | QIODevice::Text))
183 return 0; /* process has terminated in the meantime */
185 int size; //amount of data read in
186 if( (size = d->mFile.readLine( d->mBuffer, sizeof(d->mBuffer))) <= 0) { //-1 indicates nothing read
187 d->mFile.close();
188 return 0;
191 d->mFile.close();
192 int current_word = 0;
193 char *word = d->mBuffer;
195 while(true) {
196 if(word[0] == ' ' ) {
197 if(++current_word == 3)
198 break;
199 } else if(word[0] == 0) {
200 return 0; //end of data - serious problem
202 word++;
204 return atol(++word);
207 bool ProcessesLocal::Private::readProcStat(long pid, Process *ps)
209 QString filename = QString("/proc/%1/stat").arg(pid);
210 // As an optomization, if the last file read in was stat, then we already have this info in memory
211 if(mFile.fileName() != filename) {
212 mFile.setFileName(filename);
213 if(!mFile.open(QIODevice::ReadOnly | QIODevice::Text))
214 return false; /* process has terminated in the meantime */
215 if( mFile.readLine( mBuffer, sizeof(mBuffer)) <= 0) { //-1 indicates nothing read
216 mFile.close();
217 return false;
219 mFile.close();
222 int current_word = 0; //count from 0
223 char *word = mBuffer;
224 char status='\0';
225 long vmSize = 0;
226 long vmRSS = 0;
227 while(current_word < 23) {
228 if(word[0] == ' ' ) {
229 ++current_word;
230 switch(current_word) {
231 case 2: //status
232 status=word[1]; // Look at the first letter of the status.
233 // We analyze this after the while loop
234 break;
235 case 6: //ttyNo
237 int ttyNo = atoi(word+1);
238 int major = ttyNo >> 8;
239 int minor = ttyNo & 0xff;
240 switch(major) {
241 case 136:
242 ps->setTty(QByteArray("pts/") + QByteArray::number(minor));
243 break;
244 case 5:
245 ps->setTty(QByteArray("tty"));
246 case 4:
247 if(minor < 64)
248 ps->setTty(QByteArray("tty") + QByteArray::number(minor));
249 else
250 ps->setTty(QByteArray("ttyS") + QByteArray::number(minor-64));
251 break;
252 default:
253 ps->setTty(QByteArray());
256 break;
257 case 13: //userTime
258 ps->setUserTime(atoll(word+1));
259 break;
260 case 14: //sysTime
261 ps->setSysTime(atoll(word+1));
262 break;
263 case 18: //niceLevel
264 ps->setNiceLevel(atoi(word+1)); /*Or should we use getPriority instead? */
265 break;
266 case 22: //vmSize
267 vmSize = atol(word+1);
268 break;
269 case 23: //vmRSS
270 vmRSS = atol(word+1);
271 break;
272 default:
273 break;
275 } else if(word[0] == 0) {
276 return false; //end of data - serious problem
278 word++;
281 /* There was a "(ps->vmRss+3) * sysconf(_SC_PAGESIZE)" here in the original ksysguard code. I have no idea why! After comparing it to
282 * meminfo and other tools, this means we report the RSS by 12 bytes differently compared to them. So I'm removing the +3
283 * to be consistent. NEXT TIME COMMENT STRANGE THINGS LIKE THAT! :-) */
284 ps->setVmRSS(vmRSS * sysconf(_SC_PAGESIZE) / 1024); /*convert to KiB*/
285 ps->setVmSize(vmSize /= 1024); /* convert to KiB */
287 switch( status) {
288 case 'R':
289 ps->setStatus(Process::Running);
290 break;
291 case 'S':
292 ps->setStatus(Process::Sleeping);
293 break;
294 case 'D':
295 ps->setStatus(Process::DiskSleep);
296 break;
297 case 'Z':
298 ps->setStatus(Process::Zombie);
299 break;
300 case 'T':
301 ps->setStatus(Process::Stopped);
302 break;
303 case 'W':
304 ps->setStatus(Process::Paging);
305 break;
306 default:
307 ps->setStatus(Process::OtherStatus);
308 break;
310 return true;
313 bool ProcessesLocal::Private::readProcStatm(long pid, Process *process)
315 mFile.setFileName(QString("/proc/%1/statm").arg(pid));
316 if(!mFile.open(QIODevice::ReadOnly | QIODevice::Text))
317 return false; /* process has terminated in the meantime */
319 if( mFile.readLine( mBuffer, sizeof(mBuffer)) <= 0) { //-1 indicates nothing read
320 mFile.close();
321 return 0;
323 mFile.close();
325 int current_word = 0;
326 char *word = mBuffer;
328 while(true) {
329 if(word[0] == ' ' ) {
330 if(++current_word == 2) //number of pages that are shared
331 break;
332 } else if(word[0] == 0) {
333 return false; //end of data - serious problem
335 word++;
337 long shared = atol(word+1);
339 /* we use the rss - shared to find the amount of memory just this app uses */
340 process->vmURSS = process->vmRSS - (shared * sysconf(_SC_PAGESIZE) / 1024);
341 return true;
345 bool ProcessesLocal::Private::readProcCmdline(long pid, Process *process)
347 if(!process->command.isNull()) return true; //only parse the cmdline once
348 mFile.setFileName(QString("/proc/%1/cmdline").arg(pid));
349 if(!mFile.open(QIODevice::ReadOnly | QIODevice::Text))
350 return false; /* process has terminated in the meantime */
352 QTextStream in(&mFile);
353 process->command = in.readAll();
355 //cmdline seperates parameters with the NULL character
356 process->command.replace('\0', ' ');
357 process->command = process->command.trimmed();
359 mFile.close();
360 return true;
363 bool ProcessesLocal::Private::getNiceness(long pid, Process *process) {
364 int sched = sched_getscheduler(pid);
365 switch(sched) {
366 case (SCHED_OTHER):
367 process->scheduler = KSysGuard::Process::Other;
368 break;
369 case (SCHED_RR):
370 process->scheduler = KSysGuard::Process::RoundRobin;
371 break;
372 case (SCHED_FIFO):
373 process->scheduler = KSysGuard::Process::Fifo;
374 break;
375 #ifdef SCHED_BATCH
376 case (SCHED_BATCH):
377 process->scheduler = KSysGuard::Process::Batch;
378 break;
379 #endif
380 default:
381 process->scheduler = KSysGuard::Process::Other;
383 if(sched == SCHED_FIFO || sched == SCHED_RR) {
384 struct sched_param param;
385 if(sched_getparam(pid, &param) == 0)
386 process->niceLevel = param.sched_priority;
387 else
388 process->niceLevel = 0; //Error getting scheduler parameters.
391 #ifdef HAVE_IONICE
392 int ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid); /* Returns from 0 to 7 for the iopriority, and -1 if there's an error */
393 if(ioprio == -1) {
394 process->ioniceLevel = -1;
395 process->ioPriorityClass = KSysGuard::Process::None;
396 return false; /* Error. Just give up. */
398 process->ioniceLevel = ioprio & 0xff; /* Bottom few bits are the priority */
399 process->ioPriorityClass = (KSysGuard::Process::IoPriorityClass)(ioprio >> IOPRIO_CLASS_SHIFT); /* Top few bits are the class */
400 #else
401 return false; /* Do nothing, if we do not support this architecture */
402 #endif
403 return true;
406 bool ProcessesLocal::updateProcessInfo( long pid, Process *process)
408 if(!d->readProcStat(pid, process)) return false;
409 if(!d->readProcStatus(pid, process)) return false;
410 if(!d->readProcStatm(pid, process)) return false;
411 if(!d->readProcCmdline(pid, process)) return false;
412 if(!d->getNiceness(pid, process)) return false;
414 return true;
417 QSet<long> ProcessesLocal::getAllPids( )
419 QSet<long> pids;
420 if(d->mProcDir==NULL) return pids; //There's not much we can do without /proc
421 struct dirent* entry;
422 rewinddir(d->mProcDir);
423 while ( ( entry = readdir( d->mProcDir ) ) )
424 if ( entry->d_name[ 0 ] >= '0' && entry->d_name[ 0 ] <= '9' )
425 pids.insert(atol( entry->d_name ));
426 return pids;
429 bool ProcessesLocal::sendSignal(long pid, int sig) {
430 if ( kill( (pid_t)pid, sig ) ) {
431 //Kill failed
432 return false;
434 return true;
437 bool ProcessesLocal::setNiceness(long pid, int priority) {
438 if(pid <= 0) return false; // check the parameters
439 if ( setpriority( PRIO_PROCESS, pid, priority ) ) {
440 //set niceness failed
441 return false;
443 return true;
446 bool ProcessesLocal::setScheduler(long pid, int priorityClass, int priority) {
447 if(priorityClass == KSysGuard::Process::Other || priorityClass == KSysGuard::Process::Batch)
448 priority = 0;
449 if(pid <= 0) return false; // check the parameters
450 struct sched_param params;
451 params.sched_priority = priority;
452 switch(priorityClass) {
453 case (KSysGuard::Process::Other):
454 return (sched_setscheduler( pid, SCHED_OTHER, &params) == 0);
455 case (KSysGuard::Process::RoundRobin):
456 return (sched_setscheduler( pid, SCHED_RR, &params) == 0);
457 case (KSysGuard::Process::Fifo):
458 return (sched_setscheduler( pid, SCHED_FIFO, &params) == 0);
459 #ifdef SCHED_BATCH
460 case (KSysGuard::Process::Batch):
461 return (sched_setscheduler( pid, SCHED_BATCH, &params) == 0);
462 #endif
463 default:
464 return false;
469 bool ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority) {
470 #ifdef HAVE_IONICE
471 if (ioprio_set(IOPRIO_WHO_PROCESS, pid, priority | priorityClass << IOPRIO_CLASS_SHIFT) == -1) {
472 //set io niceness failed
473 return false;
475 return true;
476 #else
477 return false;
478 #endif
481 bool ProcessesLocal::supportsIoNiceness() {
482 #ifdef HAVE_IONICE
483 return true;
484 #else
485 return false;
486 #endif
489 long long ProcessesLocal::totalPhysicalMemory() {
490 //Try to get the memory via sysconf. Note the cast to long long to try to avoid a long overflow
491 //Should we use sysconf(_SC_PAGESIZE) or getpagesize() ?
492 long long memory = ((long long)sysconf(_SC_PHYS_PAGES)) * (sysconf(_SC_PAGESIZE)/1024);
493 if(memory > 0) return memory;
495 //This is backup code incase the above failed. It should never fail on a linux system.
497 d->mFile.setFileName("/proc/meminfo");
498 if(!d->mFile.open(QIODevice::ReadOnly | QIODevice::Text))
499 return 0;
501 int size;
502 while( (size = d->mFile.readLine( d->mBuffer, sizeof(d->mBuffer))) > 0) { //-1 indicates an error
503 switch( d->mBuffer[0]) {
504 case 'M':
505 if((unsigned int)size > sizeof("MemTotal:") && qstrncmp(d->mBuffer, "MemTotal:", sizeof("MemTotal:")-1) == 0) {
506 d->mFile.close();
507 return atoll(d->mBuffer + sizeof("MemTotal:")-1);
511 return 0; // Not found. Probably will never happen
513 ProcessesLocal::~ProcessesLocal()
515 delete d;