add more spacing
[personal-kdebase.git] / workspace / libs / ksysguard / processcore / processes_linux_p.cpp
blob1fd6a100bd4ffd2a9013438be3c9c058ca3b134f
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 <QByteArray>
31 #include <QTextStream>
33 //for sysconf
34 #include <unistd.h>
35 //for kill and setNice
36 #include <sys/types.h>
37 #include <signal.h>
38 #include <sys/resource.h>
39 #include <dirent.h>
40 #include <stdlib.h>
41 //for ionice
42 #include <sys/ptrace.h>
43 #include <asm/unistd.h>
44 //for getsched
45 #include <sched.h>
47 #define PROCESS_BUFFER_SIZE 1000
49 /* For ionice */
50 extern int sys_ioprio_set(int, int, int);
51 extern int sys_ioprio_get(int, int);
53 #define HAVE_IONICE
54 /* Check if this system has ionice */
55 #if !defined(SYS_ioprio_get) || !defined(SYS_ioprio_set)
56 /* All new kernels have SYS_ioprio_get and _set defined, but for the few that do not, here are the definitions */
57 #if defined(__i386__)
58 #define __NR_ioprio_set 289
59 #define __NR_ioprio_get 290
60 #elif defined(__ppc__) || defined(__powerpc__)
61 #define __NR_ioprio_set 273
62 #define __NR_ioprio_get 274
63 #elif defined(__x86_64__)
64 #define __NR_ioprio_set 251
65 #define __NR_ioprio_get 252
66 #elif defined(__ia64__)
67 #define __NR_ioprio_set 1274
68 #define __NR_ioprio_get 1275
69 #else
70 #ifdef __GNUC__
71 #warning "This architecture does not support IONICE. Disabling ionice feature."
72 #endif
73 #undef HAVE_IONICE
74 #endif
75 /* Map these to SYS_ioprio_get */
76 #define SYS_ioprio_get __NR_ioprio_get
77 #define SYS_ioprio_set __NR_ioprio_set
79 #endif /* !SYS_ioprio_get */
81 /* Set up ionice functions */
82 #ifdef HAVE_IONICE
83 #define IOPRIO_WHO_PROCESS 1
84 #define IOPRIO_CLASS_SHIFT 13
86 /* Expose the kernel calls to usespace via syscall
87 * See man ioprio_set and man ioprio_get for information on these functions */
88 static int ioprio_set(int which, int who, int ioprio)
90 return syscall(SYS_ioprio_set, which, who, ioprio);
93 static int ioprio_get(int which, int who)
95 return syscall(SYS_ioprio_get, which, who);
97 #endif
102 namespace KSysGuard
105 class ProcessesLocal::Private
107 public:
108 Private() { mProcDir = opendir( "/proc" );}
109 ~Private();
110 inline bool readProcStatus(long pid, Process *process);
111 inline bool readProcStat(long pid, Process *process);
112 inline bool readProcStatm(long pid, Process *process);
113 inline bool readProcCmdline(long pid, Process *process);
114 inline bool getNiceness(long pid, Process *process);
115 QFile mFile;
116 char mBuffer[PROCESS_BUFFER_SIZE+1]; //used as a buffer to read data into
117 DIR* mProcDir;
120 ProcessesLocal::Private::~Private()
122 closedir(mProcDir);
125 ProcessesLocal::ProcessesLocal() : d(new Private())
130 bool ProcessesLocal::Private::readProcStatus(long pid, Process *process)
132 mFile.setFileName(QString("/proc/%1/status").arg(pid));
133 if(!mFile.open(QIODevice::ReadOnly | QIODevice::Text))
134 return false; /* process has terminated in the meantime */
136 process->uid = 0;
137 process->gid = 0;
138 process->tracerpid = 0;
140 int size;
141 int found = 0; //count how many fields we found
142 while( (size = mFile.readLine( mBuffer, sizeof(mBuffer))) > 0) { //-1 indicates an error
143 switch( mBuffer[0]) {
144 case 'N':
145 if((unsigned int)size > sizeof("Name:") && qstrncmp(mBuffer, "Name:", sizeof("Name:")-1) == 0) {
146 process->name = QString::fromLocal8Bit(mBuffer + sizeof("Name:")-1, size-sizeof("Name:")+1).trimmed();
147 if(++found == 4) goto finish;
149 break;
150 case 'U':
151 if((unsigned int)size > sizeof("Uid:") && qstrncmp(mBuffer, "Uid:", sizeof("Uid:")-1) == 0) {
152 sscanf(mBuffer + sizeof("Uid:") -1, "%Ld %Ld %Ld %Ld", &process->uid, &process->euid, &process->suid, &process->fsuid );
153 if(++found == 4) goto finish;
155 break;
156 case 'G':
157 if((unsigned int)size > sizeof("Gid:") && qstrncmp(mBuffer, "Gid:", sizeof("Gid:")-1) == 0) {
158 sscanf(mBuffer + sizeof("Gid:")-1, "%Ld %Ld %Ld %Ld", &process->gid, &process->egid, &process->sgid, &process->fsgid );
159 if(++found == 4) goto finish;
161 break;
162 case 'T':
163 if((unsigned int)size > sizeof("TracerPid:") && qstrncmp(mBuffer, "TracerPid:", sizeof("TracerPid:")-1) == 0) {
164 process->tracerpid = atol(mBuffer + sizeof("TracerPid:")-1);
165 if(++found == 4) goto finish;
167 break;
168 default:
169 break;
173 finish:
174 mFile.close();
175 return true;
178 long ProcessesLocal::getParentPid(long pid) {
179 Q_ASSERT(pid != 0);
180 d->mFile.setFileName(QString("/proc/%1/stat").arg(pid));
181 if(!d->mFile.open(QIODevice::ReadOnly | QIODevice::Text))
182 return 0; /* process has terminated in the meantime */
184 int size; //amount of data read in
185 if( (size = d->mFile.readLine( d->mBuffer, sizeof(d->mBuffer))) <= 0) { //-1 indicates nothing read
186 d->mFile.close();
187 return 0;
190 d->mFile.close();
191 int current_word = 0;
192 char *word = d->mBuffer;
194 while(true) {
195 if(word[0] == ' ' ) {
196 if(++current_word == 3)
197 break;
198 } else if(word[0] == 0) {
199 return 0; //end of data - serious problem
201 word++;
203 return atol(++word);
206 bool ProcessesLocal::Private::readProcStat(long pid, Process *ps)
208 QString filename = QString("/proc/%1/stat").arg(pid);
209 // As an optomization, if the last file read in was stat, then we already have this info in memory
210 if(mFile.fileName() != filename) {
211 mFile.setFileName(filename);
212 if(!mFile.open(QIODevice::ReadOnly | QIODevice::Text))
213 return false; /* process has terminated in the meantime */
214 if( mFile.readLine( mBuffer, sizeof(mBuffer)) <= 0) { //-1 indicates nothing read
215 mFile.close();
216 return false;
218 mFile.close();
221 int current_word = 0; //count from 0
222 char *word = mBuffer;
223 char status='\0';
224 long vmSize = 0;
225 long vmRSS = 0;
226 while(current_word < 23) {
227 if(word[0] == ' ' ) {
228 ++current_word;
229 switch(current_word) {
230 case 2: //status
231 status=word[1]; // Look at the first letter of the status.
232 // We analyze this after the while loop
233 break;
234 case 6: //ttyNo
236 int ttyNo = atoi(word+1);
237 int major = ttyNo >> 8;
238 int minor = ttyNo & 0xff;
239 switch(major) {
240 case 136:
241 ps->setTty(QByteArray("pts/") + QByteArray::number(minor));
242 break;
243 case 5:
244 ps->setTty(QByteArray("tty"));
245 case 4:
246 if(minor < 64)
247 ps->setTty(QByteArray("tty") + QByteArray::number(minor));
248 else
249 ps->setTty(QByteArray("ttyS") + QByteArray::number(minor-64));
250 break;
251 default:
252 ps->setTty(QByteArray());
255 break;
256 case 13: //userTime
257 ps->setUserTime(atoll(word+1));
258 break;
259 case 14: //sysTime
260 ps->setSysTime(atoll(word+1));
261 break;
262 case 18: //niceLevel
263 ps->setNiceLevel(atoi(word+1)); /*Or should we use getPriority instead? */
264 break;
265 case 22: //vmSize
266 vmSize = atol(word+1);
267 break;
268 case 23: //vmRSS
269 vmRSS = atol(word+1);
270 break;
271 default:
272 break;
274 } else if(word[0] == 0) {
275 return false; //end of data - serious problem
277 word++;
280 /* There was a "(ps->vmRss+3) * sysconf(_SC_PAGESIZE)" here in the original ksysguard code. I have no idea why! After comparing it to
281 * meminfo and other tools, this means we report the RSS by 12 bytes differently compared to them. So I'm removing the +3
282 * to be consistent. NEXT TIME COMMENT STRANGE THINGS LIKE THAT! :-) */
283 ps->setVmRSS(vmRSS * sysconf(_SC_PAGESIZE) / 1024); /*convert to KiB*/
284 ps->setVmSize(vmSize /= 1024); /* convert to KiB */
286 switch( status) {
287 case 'R':
288 ps->setStatus(Process::Running);
289 break;
290 case 'S':
291 ps->setStatus(Process::Sleeping);
292 break;
293 case 'D':
294 ps->setStatus(Process::DiskSleep);
295 break;
296 case 'Z':
297 ps->setStatus(Process::Zombie);
298 break;
299 case 'T':
300 ps->setStatus(Process::Stopped);
301 break;
302 case 'W':
303 ps->setStatus(Process::Paging);
304 break;
305 default:
306 ps->setStatus(Process::OtherStatus);
307 break;
309 return true;
312 bool ProcessesLocal::Private::readProcStatm(long pid, Process *process)
314 mFile.setFileName(QString("/proc/%1/statm").arg(pid));
315 if(!mFile.open(QIODevice::ReadOnly | QIODevice::Text))
316 return false; /* process has terminated in the meantime */
318 if( mFile.readLine( mBuffer, sizeof(mBuffer)) <= 0) { //-1 indicates nothing read
319 mFile.close();
320 return 0;
322 mFile.close();
324 int current_word = 0;
325 char *word = mBuffer;
327 while(true) {
328 if(word[0] == ' ' ) {
329 if(++current_word == 2) //number of pages that are shared
330 break;
331 } else if(word[0] == 0) {
332 return false; //end of data - serious problem
334 word++;
336 long shared = atol(word+1);
338 /* we use the rss - shared to find the amount of memory just this app uses */
339 process->vmURSS = process->vmRSS - (shared * sysconf(_SC_PAGESIZE) / 1024);
340 return true;
344 bool ProcessesLocal::Private::readProcCmdline(long pid, Process *process)
346 if(!process->command.isNull()) return true; //only parse the cmdline once
347 mFile.setFileName(QString("/proc/%1/cmdline").arg(pid));
348 if(!mFile.open(QIODevice::ReadOnly | QIODevice::Text))
349 return false; /* process has terminated in the meantime */
351 QTextStream in(&mFile);
352 process->command = in.readAll();
354 //cmdline seperates parameters with the NULL character
355 process->command.replace('\0', ' ');
356 process->command = process->command.trimmed();
358 mFile.close();
359 return true;
362 bool ProcessesLocal::Private::getNiceness(long pid, Process *process) {
363 int sched = sched_getscheduler(pid);
364 switch(sched) {
365 case (SCHED_OTHER):
366 process->scheduler = KSysGuard::Process::Other;
367 break;
368 case (SCHED_RR):
369 process->scheduler = KSysGuard::Process::RoundRobin;
370 break;
371 case (SCHED_FIFO):
372 process->scheduler = KSysGuard::Process::Fifo;
373 break;
374 #ifdef SCHED_BATCH
375 case (SCHED_BATCH):
376 process->scheduler = KSysGuard::Process::Batch;
377 break;
378 #endif
379 default:
380 process->scheduler = KSysGuard::Process::Other;
382 if(sched == SCHED_FIFO || sched == SCHED_RR) {
383 struct sched_param param;
384 if(sched_getparam(pid, &param) == 0)
385 process->niceLevel = param.sched_priority;
386 else
387 process->niceLevel = 0; //Error getting scheduler parameters.
390 #ifdef HAVE_IONICE
391 int ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid); /* Returns from 0 to 7 for the iopriority, and -1 if there's an error */
392 if(ioprio == -1) {
393 process->ioniceLevel = -1;
394 process->ioPriorityClass = KSysGuard::Process::None;
395 return false; /* Error. Just give up. */
397 process->ioniceLevel = ioprio & 0xff; /* Bottom few bits are the priority */
398 process->ioPriorityClass = (KSysGuard::Process::IoPriorityClass)(ioprio >> IOPRIO_CLASS_SHIFT); /* Top few bits are the class */
399 #else
400 return false; /* Do nothing, if we do not support this architecture */
401 #endif
402 return true;
405 bool ProcessesLocal::updateProcessInfo( long pid, Process *process)
407 if(!d->readProcStat(pid, process)) return false;
408 if(!d->readProcStatus(pid, process)) return false;
409 if(!d->readProcStatm(pid, process)) return false;
410 if(!d->readProcCmdline(pid, process)) return false;
411 if(!d->getNiceness(pid, process)) return false;
413 return true;
416 QSet<long> ProcessesLocal::getAllPids( )
418 QSet<long> pids;
419 if(d->mProcDir==NULL) return pids; //There's not much we can do without /proc
420 struct dirent* entry;
421 rewinddir(d->mProcDir);
422 while ( ( entry = readdir( d->mProcDir ) ) )
423 if ( entry->d_name[ 0 ] >= '0' && entry->d_name[ 0 ] <= '9' )
424 pids.insert(atol( entry->d_name ));
425 return pids;
428 bool ProcessesLocal::sendSignal(long pid, int sig) {
429 if ( kill( (pid_t)pid, sig ) ) {
430 //Kill failed
431 return false;
433 return true;
436 bool ProcessesLocal::setNiceness(long pid, int priority) {
437 if(pid <= 0) return false; // check the parameters
438 if ( setpriority( PRIO_PROCESS, pid, priority ) ) {
439 //set niceness failed
440 return false;
442 return true;
445 bool ProcessesLocal::setScheduler(long pid, int priorityClass, int priority) {
446 if(priorityClass == KSysGuard::Process::Other || priorityClass == KSysGuard::Process::Batch)
447 priority = 0;
448 if(pid <= 0) return false; // check the parameters
449 struct sched_param params;
450 params.sched_priority = priority;
451 switch(priorityClass) {
452 case (KSysGuard::Process::Other):
453 return (sched_setscheduler( pid, SCHED_OTHER, &params) == 0);
454 case (KSysGuard::Process::RoundRobin):
455 return (sched_setscheduler( pid, SCHED_RR, &params) == 0);
456 case (KSysGuard::Process::Fifo):
457 return (sched_setscheduler( pid, SCHED_FIFO, &params) == 0);
458 #ifdef SCHED_BATCH
459 case (KSysGuard::Process::Batch):
460 return (sched_setscheduler( pid, SCHED_BATCH, &params) == 0);
461 #endif
462 default:
463 return false;
468 bool ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority) {
469 #ifdef HAVE_IONICE
470 if (ioprio_set(IOPRIO_WHO_PROCESS, pid, priority | priorityClass << IOPRIO_CLASS_SHIFT) == -1) {
471 //set io niceness failed
472 return false;
474 return true;
475 #else
476 return false;
477 #endif
480 bool ProcessesLocal::supportsIoNiceness() {
481 #ifdef HAVE_IONICE
482 return true;
483 #else
484 return false;
485 #endif
488 long long ProcessesLocal::totalPhysicalMemory() {
489 //Try to get the memory via sysconf. Note the cast to long long to try to avoid a long overflow
490 //Should we use sysconf(_SC_PAGESIZE) or getpagesize() ?
491 long long memory = ((long long)sysconf(_SC_PHYS_PAGES)) * (sysconf(_SC_PAGESIZE)/1024);
492 if(memory > 0) return memory;
494 //This is backup code incase the above failed. It should never fail on a linux system.
496 d->mFile.setFileName("/proc/meminfo");
497 if(!d->mFile.open(QIODevice::ReadOnly | QIODevice::Text))
498 return 0;
500 int size;
501 while( (size = d->mFile.readLine( d->mBuffer, sizeof(d->mBuffer))) > 0) { //-1 indicates an error
502 switch( d->mBuffer[0]) {
503 case 'M':
504 if((unsigned int)size > sizeof("MemTotal:") && qstrncmp(d->mBuffer, "MemTotal:", sizeof("MemTotal:")-1) == 0) {
505 d->mFile.close();
506 return atoll(d->mBuffer + sizeof("MemTotal:")-1);
510 return 0; // Not found. Probably will never happen
512 ProcessesLocal::~ProcessesLocal()
514 delete d;