File indexing completed on 2024-04-28 05:31:38

0001 /*
0002     SPDX-FileCopyrightText: 2007 John Tapsell <tapsell@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "processes.h"
0008 #include "processes_atop_p.h"
0009 #include "processes_base_p.h"
0010 #include "processes_local_p.h"
0011 #include "processes_remote_p.h"
0012 
0013 #include <QByteArray>
0014 #include <QElapsedTimer>
0015 #include <QMutableSetIterator>
0016 #include <QSet>
0017 #include <QVariantMap>
0018 
0019 // for sysconf
0020 #include <unistd.h>
0021 
0022 /* if porting to an OS without signal.h  please #define SIGTERM to something */
0023 #include <signal.h>
0024 
0025 namespace KSysGuard
0026 {
0027 class Q_DECL_HIDDEN Processes::Private
0028 {
0029 public:
0030     Private(Processes *q_ptr)
0031     {
0032         mFakeProcess.setParent(&mFakeProcess);
0033         mAbstractProcesses = nullptr;
0034         mHistoricProcesses = nullptr;
0035         mIsLocalHost = true;
0036         mProcesses.insert(-1, &mFakeProcess);
0037         mElapsedTimeMilliSeconds = 0;
0038         mUpdateFlags = {};
0039         mUsingHistoricalData = false;
0040         mLastError = Error::NoError;
0041         q = q_ptr;
0042     }
0043     ~Private();
0044     void markProcessesAsEnded(long pid);
0045 
0046     QSet<long> mToBeProcessed;
0047     QSet<long> mEndedProcesses; ///< Processes that have finished
0048 
0049     QHash<long, Process *> mProcesses; ///< This must include mFakeProcess at pid -1
0050     QList<Process *> mListProcesses; ///< A list of the processes.  Does not include mFakeProcesses
0051     Process mFakeProcess; ///< A fake process with pid -1 just so that even init points to a parent
0052 
0053     AbstractProcesses *mAbstractProcesses; ///< The OS specific code to get the process information
0054     ProcessesATop *mHistoricProcesses; ///< A way to get historic information about processes
0055     bool mIsLocalHost; ///< Whether this is localhost or not
0056 
0057     QElapsedTimer mLastUpdated; ///< This is the time we last updated.  Used to calculate cpu usage.
0058     long mElapsedTimeMilliSeconds; ///< The number of milliseconds  (1000ths of a second) that passed since the last update
0059 
0060     Processes::UpdateFlags mUpdateFlags;
0061     bool mUsingHistoricalData; ///< Whether to return historical data for updateProcess() etc
0062     Processes *q;
0063 
0064     Error mLastError;
0065 };
0066 
0067 Processes::Private::~Private()
0068 {
0069     Q_FOREACH (Process *process, mProcesses) {
0070         if (process != &mFakeProcess) {
0071             delete process;
0072         }
0073     }
0074     mProcesses.clear();
0075     mListProcesses.clear();
0076     delete mAbstractProcesses;
0077     mAbstractProcesses = nullptr;
0078     delete mHistoricProcesses;
0079     mHistoricProcesses = nullptr;
0080 }
0081 
0082 Processes::Processes(const QString &host, QObject *parent)
0083     : QObject(parent)
0084     , d(new Private(this))
0085 {
0086     qRegisterMetaType<KSysGuard::Process::Updates>();
0087 
0088     if (host.isEmpty()) {
0089         d->mAbstractProcesses = new ProcessesLocal();
0090     } else {
0091         ProcessesRemote *remote = new ProcessesRemote(host);
0092         d->mAbstractProcesses = remote;
0093         connect(remote, &ProcessesRemote::runCommand, this, &Processes::runCommand);
0094     }
0095     d->mIsLocalHost = host.isEmpty();
0096     connect(d->mAbstractProcesses, &AbstractProcesses::processesUpdated, this, &Processes::processesUpdated);
0097     connect(d->mAbstractProcesses, &AbstractProcesses::processUpdated, this, &Processes::processUpdated);
0098 }
0099 
0100 Processes::~Processes()
0101 {
0102     delete d;
0103 }
0104 
0105 Processes::Error Processes::lastError() const
0106 {
0107     return d->mLastError;
0108 }
0109 Process *Processes::getProcess(long pid) const
0110 {
0111     return d->mProcesses.value(pid);
0112 }
0113 
0114 const QList<Process *> &Processes::getAllProcesses() const
0115 {
0116     return d->mListProcesses;
0117 }
0118 
0119 int Processes::processCount() const
0120 {
0121     return d->mListProcesses.count();
0122 }
0123 
0124 bool Processes::updateProcess(Process *ps, long ppid)
0125 {
0126     Process *parent = d->mProcesses.value(ppid, &d->mFakeProcess);
0127     Q_ASSERT(parent); // even init has a non-null parent - the mFakeProcess
0128 
0129     if (ps->parent() != parent) {
0130         Q_EMIT beginMoveProcess(ps, parent /*new parent*/);
0131         // Processes has been reparented
0132         Process *p = ps;
0133         do {
0134             p = p->parent();
0135             p->numChildren() -= (ps->numChildren() + 1);
0136         } while (p->pid() != -1);
0137         Q_ASSERT(ps != parent);
0138         ps->parent()->children().removeAll(ps);
0139         ps->setParent(parent); // the parent has changed
0140         parent->children().append(ps);
0141         p = ps;
0142         do {
0143             p = p->parent();
0144             p->numChildren() += (ps->numChildren() + 1);
0145         } while (p->pid() != -1);
0146         Q_EMIT endMoveProcess();
0147         Q_ASSERT(ps != parent);
0148         ps->setParent(parent);
0149     }
0150 
0151     ps->setParentPid(ppid);
0152 
0153     bool success = updateProcessInfo(ps);
0154     Q_EMIT processChanged(ps, false);
0155 
0156     return success;
0157 }
0158 
0159 bool Processes::updateProcessInfo(Process *ps)
0160 {
0161     // Now we can actually get the process info
0162     qlonglong oldUserTime = ps->userTime();
0163     qlonglong oldSysTime = ps->sysTime();
0164 
0165     qlonglong oldIoCharactersRead = 0;
0166     qlonglong oldIoCharactersWritten = 0;
0167     qlonglong oldIoReadSyscalls = 0;
0168     qlonglong oldIoWriteSyscalls = 0;
0169     qlonglong oldIoCharactersActuallyRead = 0;
0170     qlonglong oldIoCharactersActuallyWritten = 0;
0171 
0172     if (d->mUpdateFlags.testFlag(Processes::IOStatistics)) {
0173         oldIoCharactersRead = ps->ioCharactersRead();
0174         oldIoCharactersWritten = ps->ioCharactersWritten();
0175         oldIoReadSyscalls = ps->ioReadSyscalls();
0176         oldIoWriteSyscalls = ps->ioWriteSyscalls();
0177         oldIoCharactersActuallyRead = ps->ioCharactersActuallyRead();
0178         oldIoCharactersActuallyWritten = ps->ioCharactersActuallyWritten();
0179     }
0180 
0181     ps->setChanges(Process::Nothing);
0182     bool success;
0183     if (d->mUsingHistoricalData) {
0184         success = d->mHistoricProcesses->updateProcessInfo(ps->pid(), ps);
0185     } else {
0186         success = d->mAbstractProcesses->updateProcessInfo(ps->pid(), ps);
0187     }
0188 
0189     // Now we have the process info.  Calculate the cpu usage and total cpu usage for itself and all its parents
0190     if (!d->mUsingHistoricalData && d->mElapsedTimeMilliSeconds != 0) { // Update the user usage and sys usage
0191 #ifndef Q_OS_NETBSD
0192         /* The elapsed time is the d->mElapsedTimeMilliSeconds
0193          * (which is of the order 2 seconds or so) plus a small
0194          * correction where we get the amount of time elapsed since
0195          * we start processing. This is because the processing itself
0196          * can take a non-trivial amount of time.  */
0197         int elapsedTime = ps->elapsedTimeMilliSeconds();
0198         ps->setElapsedTimeMilliSeconds(d->mLastUpdated.elapsed());
0199         elapsedTime = ps->elapsedTimeMilliSeconds() - elapsedTime + d->mElapsedTimeMilliSeconds;
0200         if (elapsedTime > 0) {
0201             ps->setUserUsage((int)(((ps->userTime() - oldUserTime) * 1000.0) / elapsedTime));
0202             ps->setSysUsage((int)(((ps->sysTime() - oldSysTime) * 1000.0) / elapsedTime));
0203         }
0204 #endif
0205 
0206         static auto calculateRate = [](qlonglong current, qlonglong previous, int elapsedTime) {
0207             if (elapsedTime <= 0 || previous <= 0) {
0208                 return 0.0;
0209             }
0210             return (current - previous) * 1000.0 / elapsedTime;
0211         };
0212 
0213         if (d->mUpdateFlags.testFlag(Processes::IOStatistics)) {
0214             ps->setIoCharactersReadRate(calculateRate(ps->ioCharactersRead(), oldIoCharactersRead, elapsedTime));
0215             ps->setIoCharactersWrittenRate(calculateRate(ps->ioCharactersWritten(), oldIoCharactersWritten, elapsedTime));
0216             ps->setIoReadSyscallsRate(calculateRate(ps->ioReadSyscalls(), oldIoReadSyscalls, elapsedTime));
0217             ps->setIoWriteSyscallsRate(calculateRate(ps->ioWriteSyscalls(), oldIoWriteSyscalls, elapsedTime));
0218             ps->setIoCharactersActuallyReadRate(calculateRate(ps->ioCharactersActuallyRead(), oldIoCharactersActuallyRead, elapsedTime));
0219             ps->setIoCharactersActuallyWrittenRate(calculateRate(ps->ioCharactersActuallyWritten(), oldIoCharactersActuallyWritten, elapsedTime));
0220         } else {
0221             ps->setIoCharactersReadRate(0);
0222             ps->setIoCharactersWrittenRate(0);
0223             ps->setIoReadSyscallsRate(0);
0224             ps->setIoWriteSyscallsRate(0);
0225             ps->setIoCharactersActuallyReadRate(0);
0226             ps->setIoCharactersActuallyWrittenRate(0);
0227         }
0228     }
0229     if (d->mUsingHistoricalData || d->mElapsedTimeMilliSeconds != 0) {
0230         ps->setTotalUserUsage(ps->userUsage());
0231         ps->setTotalSysUsage(ps->sysUsage());
0232         if (ps->userUsage() != 0 || ps->sysUsage() != 0) {
0233             Process *p = ps->parent();
0234             while (p->pid() != -1) {
0235                 p->totalUserUsage() += ps->userUsage();
0236                 p->totalSysUsage() += ps->sysUsage();
0237                 Q_EMIT processChanged(p, true);
0238                 p = p->parent();
0239             }
0240         }
0241     }
0242 
0243     return success;
0244 }
0245 
0246 bool Processes::addProcess(long pid, long ppid)
0247 {
0248     Process *parent = d->mProcesses.value(ppid);
0249     if (!parent) {
0250         // Under race conditions, the parent could have already quit
0251         // In this case, attach to top leaf
0252         parent = &d->mFakeProcess;
0253         Q_ASSERT(parent); // even init has a non-null parent - the mFakeProcess
0254     }
0255     // it's a new process - we need to set it up
0256     Process *ps = new Process(pid, ppid, parent);
0257 
0258     Q_EMIT beginAddProcess(ps);
0259 
0260     d->mProcesses.insert(pid, ps);
0261 
0262     ps->setIndex(d->mListProcesses.count());
0263     d->mListProcesses.append(ps);
0264 
0265     ps->parent()->children().append(ps);
0266     Process *p = ps;
0267     do {
0268         Q_ASSERT(p);
0269         p = p->parent();
0270         p->numChildren()++;
0271     } while (p->pid() != -1);
0272     ps->setParentPid(ppid);
0273 
0274     // Now we can actually get the process info
0275     bool success = updateProcessInfo(ps);
0276     Q_EMIT endAddProcess();
0277     return success;
0278 }
0279 
0280 bool Processes::updateOrAddProcess(long pid)
0281 {
0282     long ppid;
0283     if (d->mUsingHistoricalData) {
0284         ppid = d->mHistoricProcesses->getParentPid(pid);
0285     } else {
0286         ppid = d->mAbstractProcesses->getParentPid(pid);
0287     }
0288 
0289     if (ppid == pid) {
0290         // Shouldn't ever happen
0291         ppid = -1;
0292     }
0293 
0294     if (d->mToBeProcessed.contains(ppid)) {
0295         // Make sure that we update the parent before we update this one.  Just makes things a bit easier.
0296         d->mToBeProcessed.remove(ppid);
0297         updateOrAddProcess(ppid);
0298     }
0299 
0300     Process *ps = d->mProcesses.value(pid);
0301     if (!ps) {
0302         return addProcess(pid, ppid);
0303     } else {
0304         return updateProcess(ps, ppid);
0305     }
0306 }
0307 
0308 void Processes::updateAllProcesses(long updateDurationMS, Processes::UpdateFlags updateFlags)
0309 {
0310     d->mUpdateFlags = updateFlags;
0311 
0312     if (d->mUsingHistoricalData || d->mLastUpdated.elapsed() >= updateDurationMS || !d->mLastUpdated.isValid()) {
0313         d->mElapsedTimeMilliSeconds = d->mLastUpdated.restart();
0314         if (d->mUsingHistoricalData) {
0315             d->mHistoricProcesses->updateAllProcesses(d->mUpdateFlags);
0316         } else {
0317             d->mAbstractProcesses->updateAllProcesses(d->mUpdateFlags); // For a local machine, this will directly call Processes::processesUpdated()
0318         }
0319     }
0320 }
0321 
0322 void Processes::processesUpdated()
0323 {
0324     // First really delete any processes that ended last time
0325     long pid;
0326     {
0327         QSetIterator<long> i(d->mEndedProcesses);
0328         while (i.hasNext()) {
0329             pid = i.next();
0330             deleteProcess(pid);
0331         }
0332     }
0333 
0334     if (d->mUsingHistoricalData) {
0335         d->mToBeProcessed = d->mHistoricProcesses->getAllPids();
0336     } else {
0337         d->mToBeProcessed = d->mAbstractProcesses->getAllPids();
0338     }
0339 
0340     QSet<long> endedProcesses;
0341     for (Process *p : d->mListProcesses) {
0342         if (!d->mToBeProcessed.contains(p->pid())) {
0343             endedProcesses += p->pid();
0344         }
0345     }
0346 
0347     {
0348         QMutableSetIterator<long> i(d->mToBeProcessed);
0349         while (i.hasNext()) {
0350             pid = i.next();
0351             i.remove();
0352             updateOrAddProcess(pid); // This adds the process or changes an existing one
0353             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
0354         }
0355     }
0356     {
0357         QSetIterator<long> i(endedProcesses);
0358         while (i.hasNext()) {
0359             // We saw these pids last time, but not this time.  That means we have to mark them for deletion now
0360             pid = i.next();
0361             d->markProcessesAsEnded(pid);
0362         }
0363         d->mEndedProcesses = endedProcesses;
0364     }
0365 
0366     Q_EMIT updated();
0367 }
0368 
0369 void Processes::processUpdated(long pid, const Process::Updates &changes)
0370 {
0371     auto process = d->mProcesses.value(pid);
0372     if (!process) {
0373         return;
0374     }
0375 
0376     for (auto entry : changes) {
0377         switch (entry.first) {
0378         case Process::VmPSS:
0379             process->setVmPSS(entry.second.toLongLong());
0380             break;
0381         default:
0382             break;
0383         }
0384     }
0385 
0386     Q_EMIT processChanged(process, false);
0387 }
0388 
0389 void Processes::Private::markProcessesAsEnded(long pid)
0390 {
0391     Q_ASSERT(pid >= 0);
0392 
0393     Process *process = mProcesses.value(pid);
0394     if (!process) {
0395         return;
0396     }
0397     process->setStatus(Process::Ended);
0398     Q_EMIT q->processChanged(process, false);
0399 }
0400 void Processes::deleteProcess(long pid)
0401 {
0402     Q_ASSERT(pid >= 0);
0403 
0404     Process *process = d->mProcesses.value(pid);
0405     if (!process) {
0406         return;
0407     }
0408     Q_FOREACH (Process *it, process->children()) {
0409         deleteProcess(it->pid());
0410     }
0411 
0412     Q_EMIT beginRemoveProcess(process);
0413 
0414     d->mProcesses.remove(pid);
0415     d->mListProcesses.removeAll(process);
0416     process->parent()->children().removeAll(process);
0417     Process *p = process;
0418     do {
0419         Q_ASSERT(p);
0420         p = p->parent();
0421         p->numChildren()--;
0422     } while (p->pid() != -1);
0423 #ifndef QT_NO_DEBUG
0424     int i = 0;
0425 #endif
0426     Q_FOREACH (Process *it, d->mListProcesses) {
0427         if (it->index() > process->index()) {
0428             it->setIndex(it->index() - 1);
0429         }
0430 #ifndef QT_NO_DEBUG
0431         Q_ASSERT(it->index() == i++);
0432 #endif
0433     }
0434 
0435     delete process;
0436     Q_EMIT endRemoveProcess();
0437 }
0438 
0439 bool Processes::killProcess(long pid)
0440 {
0441     return sendSignal(pid, SIGTERM);
0442 }
0443 
0444 bool Processes::sendSignal(long pid, int sig)
0445 {
0446     auto processes = d->mUsingHistoricalData ? d->mHistoricProcesses : d->mAbstractProcesses;
0447     auto error = processes->sendSignal(pid, sig);
0448     if (error != NoError) {
0449         d->mLastError = error;
0450         return false;
0451     }
0452     return true;
0453 }
0454 
0455 bool Processes::setNiceness(long pid, int priority)
0456 {
0457     auto processes = d->mUsingHistoricalData ? d->mHistoricProcesses : d->mAbstractProcesses;
0458     auto error = processes->setNiceness(pid, priority);
0459     if (error != NoError) {
0460         d->mLastError = error;
0461         return false;
0462     }
0463     return true;
0464 }
0465 
0466 bool Processes::setScheduler(long pid, KSysGuard::Process::Scheduler priorityClass, int priority)
0467 {
0468     auto processes = d->mUsingHistoricalData ? d->mHistoricProcesses : d->mAbstractProcesses;
0469     auto error = processes->setScheduler(pid, priorityClass, priority);
0470     if (error != NoError) {
0471         d->mLastError = error;
0472         return false;
0473     }
0474     return true;
0475 }
0476 
0477 bool Processes::setIoNiceness(long pid, KSysGuard::Process::IoPriorityClass priorityClass, int priority)
0478 {
0479     auto processes = d->mUsingHistoricalData ? d->mHistoricProcesses : d->mAbstractProcesses;
0480     auto error = processes->setIoNiceness(pid, priorityClass, priority);
0481     if (error != NoError) {
0482         d->mLastError = error;
0483         return false;
0484     }
0485     return true;
0486 }
0487 
0488 bool Processes::supportsIoNiceness()
0489 {
0490     if (d->mUsingHistoricalData) {
0491         return d->mHistoricProcesses->supportsIoNiceness();
0492     }
0493     return d->mAbstractProcesses->supportsIoNiceness();
0494 }
0495 
0496 long long Processes::totalPhysicalMemory()
0497 {
0498     return d->mAbstractProcesses->totalPhysicalMemory();
0499 }
0500 
0501 long Processes::numberProcessorCores()
0502 {
0503     return d->mAbstractProcesses->numberProcessorCores();
0504 }
0505 
0506 void Processes::answerReceived(int id, const QList<QByteArray> &answer)
0507 {
0508     KSysGuard::ProcessesRemote *processes = qobject_cast<KSysGuard::ProcessesRemote *>(d->mAbstractProcesses);
0509     if (processes) {
0510         processes->answerReceived(id, answer);
0511     }
0512 }
0513 
0514 QList<QPair<QDateTime, uint>> Processes::historiesAvailable() const
0515 {
0516     if (!d->mIsLocalHost) {
0517         return QList<QPair<QDateTime, uint>>();
0518     }
0519     if (!d->mHistoricProcesses) {
0520         d->mHistoricProcesses = new ProcessesATop();
0521     }
0522 
0523     return d->mHistoricProcesses->historiesAvailable();
0524 }
0525 
0526 void Processes::useCurrentData()
0527 {
0528     if (d->mUsingHistoricalData) {
0529         delete d->mHistoricProcesses;
0530         d->mHistoricProcesses = nullptr;
0531         connect(d->mAbstractProcesses, &AbstractProcesses::processesUpdated, this, &Processes::processesUpdated);
0532         d->mUsingHistoricalData = false;
0533     }
0534 }
0535 
0536 bool Processes::setViewingTime(const QDateTime &when)
0537 {
0538     if (!d->mIsLocalHost) {
0539         d->mLastError = NotSupported;
0540         return false;
0541     }
0542     if (!d->mUsingHistoricalData) {
0543         if (!d->mHistoricProcesses)
0544             d->mHistoricProcesses = new ProcessesATop();
0545         disconnect(d->mAbstractProcesses, &AbstractProcesses::processesUpdated, this, &Processes::processesUpdated);
0546         connect(d->mHistoricProcesses, &AbstractProcesses::processesUpdated, this, &Processes::processesUpdated);
0547         d->mUsingHistoricalData = true;
0548     }
0549     return d->mHistoricProcesses->setViewingTime(when);
0550 }
0551 
0552 bool Processes::loadHistoryFile(const QString &filename)
0553 {
0554     if (!d->mIsLocalHost) {
0555         d->mLastError = NotSupported;
0556         return false;
0557     }
0558     if (!d->mHistoricProcesses) {
0559         d->mHistoricProcesses = new ProcessesATop(false);
0560     }
0561 
0562     return d->mHistoricProcesses->loadHistoryFile(filename);
0563 }
0564 
0565 QString Processes::historyFileName() const
0566 {
0567     if (!d->mIsLocalHost || !d->mHistoricProcesses) {
0568         return QString();
0569     }
0570     return d->mHistoricProcesses->historyFileName();
0571 }
0572 QDateTime Processes::viewingTime() const
0573 {
0574     if (!d->mIsLocalHost || !d->mHistoricProcesses) {
0575         return QDateTime();
0576     }
0577     return d->mHistoricProcesses->viewingTime();
0578 }
0579 
0580 bool Processes::isHistoryAvailable() const
0581 {
0582     if (!d->mIsLocalHost) {
0583         return false;
0584     }
0585     if (!d->mHistoricProcesses) {
0586         d->mHistoricProcesses = new ProcessesATop();
0587     }
0588 
0589     return d->mHistoricProcesses->isHistoryAvailable();
0590 }
0591 
0592 }