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"
34 #include <QMutableSetIterator>
40 /* if porting to an OS without signal.h please #define SIGTERM to something */
46 Processes::StaticPrivate
*Processes::d2
= 0;
48 class Processes::Private
51 Private() { mAbstractProcesses
= 0; mProcesses
.insert(0, &mFakeProcess
); mElapsedTimeCentiSeconds
= -1; ref
=1; }
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
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
)
83 mListProcesses
.clear();
84 delete mAbstractProcesses
;
85 mAbstractProcesses
= NULL
;
88 Processes
*Processes::getInstance(const QString
&host
) { //static
90 d2
= new StaticPrivate();
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());
100 d2
->processesLocal
->d
->ref
++;
102 return d2
->processesLocal
;
104 Processes
*processes
= d2
->processesRemote
.value(host
, NULL
);
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)));
119 void Processes::returnInstance(const QString
&host
) { //static
121 kDebug() << "Internal error - static class does not exist";
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";
131 if(--(d2
->processesLocal
->d
->ref
) == 0) {
132 delete d2
->processesLocal
;
133 d2
->processesLocal
= NULL
;
137 Processes
*processes
= d2
->processesRemote
.value(host
, NULL
);
139 kDebug() << "Internal error - returning instance we do not have";
142 if(--(processes
->d
->ref
) == 0) {
144 d2
->processesRemote
.remove(host
);
148 if(--(d2
->ref
) == 0) {
154 Processes::Processes(AbstractProcesses
*abstractProcesses
) : d(new Private())
156 d
->mAbstractProcesses
= abstractProcesses
;
157 connect( abstractProcesses
, SIGNAL( processesUpdated() ), SLOT( processesUpdated() ));
160 Processes::~Processes()
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
185 } while (p
->pid
!= 0);
186 ps
->parent
->children
.removeAll(ps
);
187 ps
->parent
= parent
; //the parent has changed
188 parent
->children
.append(ps
);
193 } while (p
->pid
!= 0);
194 emit
endMoveProcess();
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
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
));
214 ps
->setTotalUserUsage(ps
->userUsage
);
215 ps
->setTotalSysUsage(ps
->sysUsage
);
216 if(ps
->userUsage
!= 0 || ps
->sysUsage
!= 0) {
217 Process
*p
= ps
->parent
;
219 p
->totalUserUsage
+= ps
->userUsage
;
220 p
->totalSysUsage
+= ps
->sysUsage
;
221 emit
processChanged(p
, true);
227 emit
processChanged(ps
, false);
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
);
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();
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);
273 return addProcess(pid
, ppid
);
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;
285 if(d
->mLastUpdated
.elapsed() < updateDurationMS
) //don't update more often than the time given
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
300 QMutableSetIterator
<long> i(d
->mToBeProcessed
);
301 while( i
.hasNext()) {
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
320 d
->mProcessedLastTime
= beingProcessed
; //update the set for next time this function is called
325 void Processes::deleteProcess(long pid
)
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
;
344 } while (p
->pid
!= 0);
347 foreach( Process
*it
, d
->mListProcesses
) {
348 if(it
->index
> process
->index
)
350 Q_ASSERT(it
->index
== i
++);
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
);
393 processes
->answerReceived(id
, answer
);
397 #include "processes.moc"