add more spacing
[personal-kdebase.git] / workspace / libs / ksysguard / processcore / processes.cpp
blobd62063837061baa68ab4603acf72762f9507f15c
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.h"
23 #include "processes_base_p.h"
24 #include "processes_local_p.h"
25 #include "processes_remote_p.h"
26 #include "process.h"
28 #include <klocale.h>
29 #include <kglobal.h>
30 #include <kdebug.h>
32 #include <QHash>
33 #include <QSet>
34 #include <QMutableSetIterator>
35 #include <QByteArray>
37 //for sysconf
38 #include <unistd.h>
40 /* if porting to an OS without signal.h please #define SIGTERM to something */
41 #include <signal.h>
44 namespace KSysGuard
46 Processes::StaticPrivate *Processes::d2 = 0;
48 class Processes::Private
50 public:
51 Private() { mAbstractProcesses = 0; mProcesses.insert(0, &mFakeProcess); mElapsedTimeCentiSeconds = -1; ref=1; }
52 ~Private();
54 QSet<long> mToBeProcessed;
55 QSet<long> mProcessedLastTime;
57 QHash<long, Process *> mProcesses; //This must include mFakeProcess at pid 0
58 QList<Process *> mListProcesses; //A list of the processes. Does not include mFakeProcesses
59 Process mFakeProcess; //A fake process with pid 0 just so that even init points to a parent
61 AbstractProcesses *mAbstractProcesses; //The OS specific code to get the process information
62 QTime mLastUpdated; //This is the time we last updated. Used to calculate cpu usage.
63 long mElapsedTimeCentiSeconds; //The number of centiseconds (100ths of a second) that passed since the last update
65 int ref; //Reference counter. When it reaches 0, delete.
68 class Processes::StaticPrivate
70 public:
71 StaticPrivate() { processesLocal = 0; ref =1; }
72 Processes *processesLocal;
73 QHash<QString, Processes*> processesRemote;
74 int ref; //Reference counter. When it reaches 0, delete.
77 Processes::Private::~Private() {
78 foreach(Process *process, mProcesses) {
79 if(process != &mFakeProcess)
80 delete process;
82 mProcesses.clear();
83 mListProcesses.clear();
84 delete mAbstractProcesses;
85 mAbstractProcesses = NULL;
88 Processes *Processes::getInstance(const QString &host) { //static
89 if(!d2) {
90 d2 = new StaticPrivate();
91 } else {
92 d2->ref++;
94 if(host.isEmpty()) {
95 //Localhost processes
96 if(!d2->processesLocal) {
97 KGlobal::locale()->insertCatalog("processcore"); //Make sure we include the translation stuff. This needs to be run before any i18n call here
98 d2->processesLocal = new Processes(new ProcessesLocal());
99 } else {
100 d2->processesLocal->d->ref++;
102 return d2->processesLocal;
103 } else {
104 Processes *processes = d2->processesRemote.value(host, NULL);
105 if( !processes ) {
106 //connect to it
107 KGlobal::locale()->insertCatalog("processcore"); //Make sure we include the translation stuff. This needs to be run before any i18n call here
108 ProcessesRemote *remote = new ProcessesRemote(host);
109 processes = new Processes( remote );
110 d2->processesRemote.insert(host, processes);
111 connect(remote, SIGNAL(runCommand(const QString &, int )), processes, SIGNAL(runCommand(const QString&, int)));
112 } else {
113 processes->d->ref++;
115 return processes;
119 void Processes::returnInstance(const QString &host) { //static
120 if(!d2) {
121 kDebug() << "Internal error - static class does not exist";
122 return;
124 if(host.isEmpty()) {
125 //Localhost processes
126 if(!d2->processesLocal) {
127 //Serious error. Returning instance we don't have.
128 kDebug() << "Internal error - returning instance we do not have";
129 return;
130 } else {
131 if(--(d2->processesLocal->d->ref) == 0) {
132 delete d2->processesLocal;
133 d2->processesLocal = NULL;
136 } else {
137 Processes *processes = d2->processesRemote.value(host, NULL);
138 if( !processes ) {
139 kDebug() << "Internal error - returning instance we do not have";
140 return;
141 } else {
142 if(--(processes->d->ref) == 0) {
143 delete processes;
144 d2->processesRemote.remove(host);
148 if(--(d2->ref) == 0) {
149 delete d2;
150 d2 = NULL;
154 Processes::Processes(AbstractProcesses *abstractProcesses) : d(new Private())
156 d->mAbstractProcesses = abstractProcesses;
157 connect( abstractProcesses, SIGNAL( processesUpdated() ), SLOT( processesUpdated() ));
160 Processes::~Processes()
162 delete d;
164 Process *Processes::getProcess(long pid) const
166 return d->mProcesses.value(pid);
169 QList<Process *> Processes::getAllProcesses() const
171 return d->mListProcesses;
173 bool Processes::updateProcess( Process *ps, long ppid, bool onlyReparent)
175 Process *parent = d->mProcesses.value(ppid);
176 Q_ASSERT(parent); //even init has a non-null parent - the mFakeProcess
178 if(ps->parent != parent) {
179 emit beginMoveProcess(ps, parent/*new parent*/);
180 //Processes has been reparented
181 Process *p = ps;
182 do {
183 p = p->parent;
184 p->numChildren--;
185 } while (p->pid!= 0);
186 ps->parent->children.removeAll(ps);
187 ps->parent = parent; //the parent has changed
188 parent->children.append(ps);
189 p = ps;
190 do {
191 p = p->parent;
192 p->numChildren++;
193 } while (p->pid!= 0);
194 emit endMoveProcess();
196 if(onlyReparent)
197 return true;
199 ps->parent = parent;
200 ps->parent_pid = ppid;
202 //Now we can actually get the process info
203 long oldUserTime = ps->userTime;
204 long oldSysTime = ps->sysTime;
205 ps->changes = Process::Nothing;
206 bool success = d->mAbstractProcesses->updateProcessInfo(ps->pid, ps);
208 //Now we have the process info. Calculate the cpu usage and total cpu usage for itself and all its parents
209 if(oldUserTime != -1 && d->mElapsedTimeCentiSeconds!= 0) { //Update the user usage and sys usage
210 #ifndef Q_OS_NETBSD
211 ps->setUserUsage((int)(((ps->userTime - oldUserTime)*100.0 + 0.5) / d->mElapsedTimeCentiSeconds));
212 ps->setSysUsage((int)(((ps->sysTime - oldSysTime)*100.0 + 0.5) / d->mElapsedTimeCentiSeconds));
213 #endif
214 ps->setTotalUserUsage(ps->userUsage);
215 ps->setTotalSysUsage(ps->sysUsage);
216 if(ps->userUsage != 0 || ps->sysUsage != 0) {
217 Process *p = ps->parent;
218 while(p->pid != 0) {
219 p->totalUserUsage += ps->userUsage;
220 p->totalSysUsage += ps->sysUsage;
221 emit processChanged(p, true);
222 p= p->parent;
227 emit processChanged(ps, false);
229 return success;
232 bool Processes::addProcess(long pid, long ppid)
234 Process *parent = d->mProcesses.value(ppid);
235 if(!parent) return false; //How can this be?
236 //it's a new process - we need to set it up
237 Process *ps = new Process(pid, ppid, parent);
239 emit beginAddProcess(ps);
241 d->mProcesses.insert(pid, ps);
243 ps->index = d->mListProcesses.count();
244 d->mListProcesses.append(ps);
246 ps->parent->children.append(ps);
247 Process *p = ps;
248 do {
249 p = p->parent;
250 p->numChildren++;
251 } while (p->pid!= 0);
252 ps->parent_pid = ppid;
254 //Now we can actually get the process info
255 bool success = d->mAbstractProcesses->updateProcessInfo(pid, ps);
256 emit endAddProcess();
257 return success;
260 bool Processes::updateOrAddProcess( long pid)
262 long ppid = d->mAbstractProcesses->getParentPid(pid);
264 if(d->mToBeProcessed.contains(ppid)) {
265 //Make sure that we update the parent before we update this one. Just makes things a bit easier.
266 d->mToBeProcessed.remove(ppid);
267 d->mProcessedLastTime.remove(ppid); //It may or may not be here - remove it if it is there
268 updateOrAddProcess(ppid);
271 Process *ps = d->mProcesses.value(pid, 0);
272 if(!ps)
273 return addProcess(pid, ppid);
274 else
275 return updateProcess(ps, ppid);
278 void Processes::updateAllProcesses( long updateDurationMS )
280 if(d->mElapsedTimeCentiSeconds == -1) {
281 //First time update has been called
282 d->mLastUpdated.start();
283 d->mElapsedTimeCentiSeconds = 0;
284 } else {
285 if(d->mLastUpdated.elapsed() < updateDurationMS) //don't update more often than the time given
286 return;
287 d->mElapsedTimeCentiSeconds = d->mLastUpdated.restart() / 10;
290 d->mAbstractProcesses->updateAllProcesses();
293 void Processes::processesUpdated() {
294 d->mToBeProcessed = d->mAbstractProcesses->getAllPids();
296 QSet<long> beingProcessed(d->mToBeProcessed); //keep a copy so that we can replace mProcessedLastTime with this at the end of this function
298 long pid;
300 QMutableSetIterator<long> i(d->mToBeProcessed);
301 while( i.hasNext()) {
302 pid = i.next();
303 i.remove();
304 d->mProcessedLastTime.remove(pid); //It may or may not be here - remove it if it is there
305 updateOrAddProcess(pid); //This adds the process or changes an extisting one
306 i.toFront(); //we can remove entries from this set elsewhere, so our iterator might be invalid. reset it back to the start of the set
310 QMutableSetIterator<long> i(d->mProcessedLastTime);
311 while( i.hasNext()) {
312 //We saw these pids last time, but not this time. That means we have to delete them now
313 pid = i.next();
314 i.remove();
315 deleteProcess(pid);
316 i.toFront();
320 d->mProcessedLastTime = beingProcessed; //update the set for next time this function is called
321 return;
325 void Processes::deleteProcess(long pid)
327 Q_ASSERT(pid > 0);
329 Process *process = d->mProcesses.value(pid);
330 foreach( Process *it, process->children) {
331 d->mProcessedLastTime.remove(it->pid);
332 deleteProcess(it->pid);
335 emit beginRemoveProcess(process);
337 d->mProcesses.remove(pid);
338 d->mListProcesses.removeAll(process);
339 process->parent->children.removeAll(process);
340 Process *p = process;
341 do {
342 p = p->parent;
343 p->numChildren--;
344 } while (p->pid!= 0);
346 int i=0;
347 foreach( Process *it, d->mListProcesses ) {
348 if(it->index > process->index)
349 it->index--;
350 Q_ASSERT(it->index == i++);
353 delete process;
354 emit endRemoveProcess();
358 bool Processes::killProcess(long pid) {
359 return sendSignal(pid, SIGTERM);
362 bool Processes::sendSignal(long pid, int sig) {
363 return d->mAbstractProcesses->sendSignal(pid, sig);
366 bool Processes::setNiceness(long pid, int priority) {
367 return d->mAbstractProcesses->setNiceness(pid, priority);
370 bool Processes::setScheduler(long pid, KSysGuard::Process::Scheduler priorityClass, int priority) {
371 return d->mAbstractProcesses->setScheduler(pid, priorityClass, priority);
374 bool Processes::setIoNiceness(long pid, KSysGuard::Process::IoPriorityClass priorityClass, int priority) {
375 return d->mAbstractProcesses->setIoNiceness(pid, priorityClass, priority);
378 bool Processes::supportsIoNiceness() {
379 return d->mAbstractProcesses->supportsIoNiceness();
382 long long Processes::totalPhysicalMemory() {
383 return d->mAbstractProcesses->totalPhysicalMemory();
386 long Processes::numberProcessorCores() {
387 return d->mAbstractProcesses->numberProcessorCores();
390 void Processes::answerReceived( int id, const QList<QByteArray>& answer ) {
391 KSysGuard::ProcessesRemote *processes = dynamic_cast<KSysGuard::ProcessesRemote *>(d->mAbstractProcesses);
392 if(processes)
393 processes->answerReceived(id, answer);
397 #include "processes.moc"