summaryrefslogtreecommitdiffstats
path: root/kicker/applets/naughty/NaughtyProcessMonitor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kicker/applets/naughty/NaughtyProcessMonitor.cpp')
-rw-r--r--kicker/applets/naughty/NaughtyProcessMonitor.cpp475
1 files changed, 475 insertions, 0 deletions
diff --git a/kicker/applets/naughty/NaughtyProcessMonitor.cpp b/kicker/applets/naughty/NaughtyProcessMonitor.cpp
new file mode 100644
index 000000000..f9d352902
--- /dev/null
+++ b/kicker/applets/naughty/NaughtyProcessMonitor.cpp
@@ -0,0 +1,475 @@
+/*
+ Naughty applet - Runaway process monitor for the KDE panel
+
+ Copyright 2000 Rik Hemsley (rikkus) <rik@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/* OpenBSD support by Jean-Yves Burlett <jean-yves@burlett.org> */
+
+#ifdef __OpenBSD__
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+#include <sys/ucred.h>
+#include <sys/dkstat.h>
+#include <stdlib.h>
+#endif
+
+#include <sys/types.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <qfile.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+#include <qdir.h>
+#include <qtimer.h>
+#include <qmap.h>
+#include <qdatetime.h>
+
+#include <klocale.h>
+
+#include "NaughtyProcessMonitor.h"
+
+class NaughtyProcessMonitorPrivate
+{
+ public:
+
+ NaughtyProcessMonitorPrivate()
+ : interval_(0),
+ timer_(0),
+ oldLoad_(0),
+ triggerLevel_(0)
+ {
+ }
+
+ ~NaughtyProcessMonitorPrivate()
+ {
+ // Empty.
+ }
+
+ uint interval_;
+ QTimer * timer_;
+ QMap<ulong, uint> loadMap_;
+ QMap<ulong, uint> scoreMap_;
+#ifdef __OpenBSD__
+ QMap<ulong, uint> cacheLoadMap_;
+ QMap<ulong, uid_t> uidMap_;
+#endif
+ uint oldLoad_;
+ uint triggerLevel_;
+
+ private:
+
+ NaughtyProcessMonitorPrivate(const NaughtyProcessMonitorPrivate &);
+
+ NaughtyProcessMonitorPrivate & operator =
+ (const NaughtyProcessMonitorPrivate &);
+};
+
+NaughtyProcessMonitor::NaughtyProcessMonitor
+ (
+ uint interval,
+ uint triggerLevel,
+ QObject * parent,
+ const char * name
+ )
+ : QObject(parent, name)
+{
+ d = new NaughtyProcessMonitorPrivate;
+ d->interval_ = interval * 1000;
+ d->triggerLevel_ = triggerLevel;
+ d->timer_ = new QTimer(this);
+ connect(d->timer_, SIGNAL(timeout()), this, SLOT(slotTimeout()));
+}
+
+NaughtyProcessMonitor::~NaughtyProcessMonitor()
+{
+ delete d;
+}
+
+ void
+NaughtyProcessMonitor::start()
+{
+ d->timer_->start(d->interval_, true);
+}
+
+ void
+NaughtyProcessMonitor::stop()
+{
+ d->timer_->stop();
+}
+
+ uint
+NaughtyProcessMonitor::interval() const
+{
+ return d->interval_ / 1000;
+}
+
+ void
+NaughtyProcessMonitor::setInterval(uint i)
+{
+ stop();
+ d->interval_ = i * 1000;
+ start();
+}
+
+ uint
+NaughtyProcessMonitor::triggerLevel() const
+{
+ return d->triggerLevel_;
+}
+
+ void
+NaughtyProcessMonitor::setTriggerLevel(uint i)
+{
+ d->triggerLevel_ = i;
+}
+
+ void
+NaughtyProcessMonitor::slotTimeout()
+{
+ uint cpu = cpuLoad();
+
+ emit(load(cpu));
+
+ if (cpu > d->triggerLevel_ * (d->interval_ / 1000))
+ {
+ uint load;
+ QValueList<ulong> l(pidList());
+
+ for (QValueList<ulong>::ConstIterator it(l.begin()); it != l.end(); ++it)
+ if (getLoad(*it, load))
+ _process(*it, load);
+ }
+
+ d->timer_->start(d->interval_, true);
+}
+
+ void
+NaughtyProcessMonitor::_process(ulong pid, uint load)
+{
+ if (!d->loadMap_.contains(pid))
+ {
+ d->loadMap_.insert(pid, load);
+ return;
+ }
+
+ uint oldLoad = d->loadMap_[pid];
+ bool misbehaving = (load - oldLoad) > 40 * (d->interval_ / 1000);
+ bool wasMisbehaving = d->scoreMap_.contains(pid);
+
+ if (misbehaving)
+ if (wasMisbehaving)
+ {
+ d->scoreMap_.replace(pid, d->scoreMap_[pid] + 1);
+ if (canKill(pid))
+ emit(runawayProcess(pid, processName(pid)));
+ }
+ else
+ d->scoreMap_.insert(pid, 1);
+ else
+ if (wasMisbehaving)
+ d->scoreMap_.remove(pid);
+
+ d->loadMap_.replace(pid, load);
+}
+
+// Here begins the set of system-specific methods.
+
+ bool
+NaughtyProcessMonitor::canKill(ulong pid) const
+{
+#ifdef __linux__
+ QFile f("/proc/" + QString::number(pid) + "/status");
+
+ if (!f.open(IO_ReadOnly))
+ return false;
+
+ QTextStream t(&f);
+
+ QString s;
+
+ while (!t.atEnd() && s.left(4) != "Uid:")
+ s = t.readLine();
+
+ QStringList l(QStringList::split('\t', s));
+
+ uint a(l[1].toUInt());
+
+// What are these 3 fields for ? Would be nice if the Linux kernel docs
+// were complete, eh ?
+// uint b(l[2].toUInt());
+// uint c(l[3].toUInt());
+// uint d(l[4].toUInt());
+
+ return geteuid() == a;
+#elif defined(__OpenBSD__)
+ // simply check if entry exists in the uid map and use it
+ if (!d->uidMap_.contains(pid))
+ return false ;
+
+ return geteuid () == d->uidMap_[pid] ;
+#else
+ Q_UNUSED( pid );
+ return false;
+#endif
+}
+
+ QString
+NaughtyProcessMonitor::processName(ulong pid) const
+{
+#if defined(__linux__) || defined(__OpenBSD__)
+#ifdef __linux__
+ QFile f("/proc/" + QString::number(pid) + "/cmdline");
+
+ if (!f.open(IO_ReadOnly))
+ return i18n("Unknown");
+
+ QCString s;
+
+ while (true)
+ {
+ int c = f.getch();
+
+ // Stop at NUL
+ if (c == -1 || char(c) == '\0')
+ break;
+ else
+ s += char(c);
+ }
+
+ // Now strip 'kdeinit:' prefix.
+ QString unicode(QString::fromLocal8Bit(s));
+
+#elif defined(__OpenBSD__)
+ int mib[4] ;
+ size_t size ;
+ char **argv ;
+
+ // fetch argv for the process `pid'
+
+ mib[0] = CTL_KERN ;
+ mib[1] = KERN_PROC_ARGS ;
+ mib[2] = pid ;
+ mib[3] = KERN_PROC_ARGV ;
+
+ // we assume argv[0]'s size will be less than one page
+
+ size = getpagesize () ;
+ argv = (char **)calloc (size, sizeof (char)) ;
+ size-- ; // ensure argv is ended by 0
+ if (-1 == sysctl (mib, 4, argv, &size, NULL, 0)) {
+ free (argv) ;
+ return i18n("Unknown") ;
+ }
+
+ // Now strip 'kdeinit:' prefix.
+ QString unicode(QString::fromLocal8Bit(argv[0]));
+
+ free (argv) ;
+#endif
+
+ QStringList parts(QStringList::split(' ', unicode));
+
+ QString processName = parts[0] == "kdeinit:" ? parts[1] : parts[0];
+
+ int lastSlash = processName.findRev('/');
+
+ // Get basename, if there's a path.
+ if (-1 != lastSlash)
+ processName = processName.mid(lastSlash + 1);
+
+ return processName;
+
+#else
+ Q_UNUSED( pid );
+ return QString::null;
+#endif
+}
+
+ uint
+NaughtyProcessMonitor::cpuLoad() const
+{
+#ifdef __linux__
+ QFile f("/proc/stat");
+
+ if (!f.open(IO_ReadOnly))
+ return 0;
+
+ bool forgetThisOne = 0 == d->oldLoad_;
+
+ QTextStream t(&f);
+
+ QString s = t.readLine();
+
+ QStringList l(QStringList::split(' ', s));
+
+ uint user = l[1].toUInt();
+ uint sys = l[3].toUInt();
+
+ uint load = user + sys;
+ uint diff = load - d->oldLoad_;
+ d->oldLoad_ = load;
+
+ return (forgetThisOne ? 0 : diff);
+#elif defined(__OpenBSD__)
+ int mib[2] ;
+ long cp_time[CPUSTATES] ;
+ size_t size ;
+ uint load, diff ;
+ bool forgetThisOne = 0 == d->oldLoad_;
+
+ // fetch CPU time statistics
+
+ mib[0] = CTL_KERN ;
+ mib[1] = KERN_CPTIME ;
+
+ size = CPUSTATES * sizeof(long) ;
+
+ if (-1 == sysctl (mib, 2, cp_time, &size, NULL, 0))
+ return 0 ;
+
+ load = cp_time[CP_USER] + cp_time[CP_SYS] ;
+ diff = load - d->oldLoad_ ;
+ d->oldLoad_ = load ;
+
+ return (forgetThisOne ? 0 : diff);
+#else
+ return 0;
+#endif
+}
+
+ QValueList<ulong>
+NaughtyProcessMonitor::pidList() const
+{
+#ifdef __linux__
+ QStringList dl(QDir("/proc").entryList());
+
+ QValueList<ulong> pl;
+
+ for (QStringList::ConstIterator it(dl.begin()); it != dl.end(); ++it)
+ if (((*it)[0].isDigit()))
+ pl << (*it).toUInt();
+
+ return pl;
+#elif defined(__OpenBSD__)
+ int mib[3] ;
+ int nprocs = 0, nentries ;
+ size_t size ;
+ struct kinfo_proc *kp ;
+ int i ;
+ QValueList<ulong> l;
+
+ // fetch number of processes
+
+ mib[0] = CTL_KERN ;
+ mib[1] = KERN_NPROCS ;
+
+ if (-1 == sysctl (mib, 2, &nprocs, &size, NULL, 0))
+ return l ;
+
+ // magic size evaluation ripped from ps
+
+ size = (5 * nprocs * sizeof(struct kinfo_proc)) / 4 ;
+ kp = (struct kinfo_proc *)calloc (size, sizeof (char)) ;
+
+ // fetch process info
+
+ mib[0] = CTL_KERN ;
+ mib[1] = KERN_PROC ;
+ mib[2] = KERN_PROC_ALL ;
+
+ if (-1 == sysctl (mib, 3, kp, &size, NULL, 0)) {
+ free (kp) ;
+ return l ;
+ }
+
+ nentries = size / sizeof (struct kinfo_proc) ;
+
+ // time statistics and euid data are fetched only for processes in
+ // the pidList, so, instead of doing one sysctl per process for
+ // getLoad and canKill calls, simply cache the data we already have.
+
+ d->cacheLoadMap_.clear () ;
+ d->uidMap_.clear () ;
+ for (i = 0; i < nentries; i++) {
+ l << (unsigned long) kp[i].kp_proc.p_pid ;
+ d->cacheLoadMap_.insert (kp[i].kp_proc.p_pid,
+ (kp[i].kp_proc.p_uticks +
+ kp[i].kp_proc.p_sticks)) ;
+ d->uidMap_.insert (kp[i].kp_proc.p_pid,
+ kp[i].kp_eproc.e_ucred.cr_uid) ;
+ }
+
+ free (kp) ;
+
+ return l ;
+#else
+ QValueList<ulong> l;
+ return l;
+#endif
+}
+
+ bool
+NaughtyProcessMonitor::getLoad(ulong pid, uint & load) const
+{
+#ifdef __linux__
+ QFile f("/proc/" + QString::number(pid) + "/stat");
+
+ if (!f.open(IO_ReadOnly))
+ return false;
+
+ QTextStream t(&f);
+
+ QString line(t.readLine());
+
+ QStringList fields(QStringList::split(' ', line));
+
+ uint userTime (fields[13].toUInt());
+ uint sysTime (fields[14].toUInt());
+
+ load = userTime + sysTime;
+
+ return true;
+#elif defined(__OpenBSD__)
+ // use cache
+ if (!d->cacheLoadMap_.contains(pid))
+ return false ;
+
+ load = d->cacheLoadMap_[pid] ;
+ return true ;
+#else
+ Q_UNUSED( pid );
+ Q_UNUSED( load );
+ return false;
+#endif
+}
+
+ bool
+NaughtyProcessMonitor::kill(ulong pid) const
+{
+#if defined(__linux__) || defined(__OpenBSD__)
+ return 0 == ::kill(pid, SIGKILL);
+#else
+ Q_UNUSED( pid );
+ return false;
+#endif
+}
+
+#include "NaughtyProcessMonitor.moc"