You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
291 lines
7.0 KiB
291 lines
7.0 KiB
/*
|
|
appobserver.cpp - An application observer/killer
|
|
Copyright (C) 2005 Konrad Twardowski <kdtonline@poczta.onet.pl>
|
|
|
|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include "appobserver.h"
|
|
#include "miscutils.h"
|
|
#include "mmainwindow.h"
|
|
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
|
|
#include <qcombobox.h>
|
|
#include <qlistbox.h>
|
|
#include <qprocess.h>
|
|
#include <qwhatsthis.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <kiconloader.h>
|
|
#include <klocale.h>
|
|
#include <kmessagebox.h>
|
|
#include <kpushbutton.h>
|
|
|
|
// TODO: 2.0: bigger combo box list
|
|
// http://ariya.blogspot.com/2006/03/resize-pop-up-list-of-combo-box_15.html
|
|
|
|
// public
|
|
|
|
AppObserver::AppObserver(QWidget *parent)
|
|
: QHBox(parent),
|
|
_processesMap(new QMap<int, Process>()),
|
|
_process(0),
|
|
_buf(QString::null)
|
|
{
|
|
setName("AppObserver");
|
|
|
|
// refresh button
|
|
b_refresh = new KPushButton(this, "KPushButton::b_refresh");
|
|
b_refresh->setIconSet(SmallIcon("reload"));
|
|
b_refresh->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
|
|
MiscUtils::setHint(b_refresh, i18n("Refresh the list of processes"));
|
|
connect(b_refresh, SIGNAL(clicked()), SLOT(slotRefresh()));
|
|
|
|
// processes combo box
|
|
cb_processes = new QComboBox(this, "QComboBox::cb_processes");
|
|
cb_processes->setFocusPolicy(StrongFocus);
|
|
MiscUtils::setHint(cb_processes, i18n("List of the running processes"));
|
|
|
|
// kill button
|
|
b_kill = new KPushButton(SmallIcon("editdelete"), i18n("Kill"), this, "KPushButton::b_kill");
|
|
b_kill->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
|
|
MiscUtils::setHint(b_kill, i18n("Kill the selected process"));
|
|
connect(b_kill, SIGNAL(clicked()), SLOT(slotKill()));
|
|
}
|
|
|
|
QString AppObserver::getInfo() const
|
|
{
|
|
int index = cb_processes->currentItem();
|
|
QMapIterator<int, Process> i = _processesMap->find(index);
|
|
|
|
if (i == _processesMap->end())
|
|
return QString::null;
|
|
|
|
return i18n("Waiting for \"%1\"").arg(i.data().command);
|
|
}
|
|
|
|
bool AppObserver::isSelectedProcessRunning() const
|
|
{
|
|
// TODO: 2.0: common code
|
|
int index = cb_processes->currentItem();
|
|
QMapIterator<int, Process> i = _processesMap->find(index);
|
|
|
|
if (i == _processesMap->end())
|
|
return false;
|
|
|
|
if (::kill(i.data().pid, 0)) // check if process exists
|
|
{
|
|
switch (errno)
|
|
{
|
|
case EINVAL: return false;
|
|
case ESRCH: return false;
|
|
case EPERM: return true;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AppObserver::isValid() const
|
|
{
|
|
if (!isSelectedProcessRunning())
|
|
{
|
|
KMessageBox::error(
|
|
ks_main,
|
|
i18n("The selected process does not exist!")
|
|
);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void AppObserver::refresh()
|
|
{
|
|
// kdDebug() << "AppObserver::refresh()" << endl;
|
|
|
|
b_refresh->setEnabled(false);
|
|
cb_processes->setEnabled(false);
|
|
b_kill->setEnabled(false);
|
|
|
|
_buf = QString::null;
|
|
cb_processes->clear();
|
|
_processesMap->clear();
|
|
|
|
if (!_process)
|
|
{
|
|
_process = new QProcess(this);
|
|
connect(_process, SIGNAL(processExited()), SLOT(slotProcessExit()));
|
|
connect(_process, SIGNAL(readyReadStdout()), SLOT(slotReadStdout()));
|
|
}
|
|
else
|
|
{
|
|
if (_process->isRunning())
|
|
_process->kill();
|
|
_process->clearArguments();
|
|
}
|
|
_process->addArgument("ps");
|
|
// show all processes
|
|
_process->addArgument("-A");
|
|
// order: user pid command
|
|
_process->addArgument("-o");
|
|
_process->addArgument("user=");
|
|
_process->addArgument("-o");
|
|
_process->addArgument("pid=");
|
|
_process->addArgument("-o");
|
|
_process->addArgument("comm=");
|
|
// sort by command
|
|
_process->addArgument("--sort");
|
|
_process->addArgument("comm");
|
|
|
|
// start process
|
|
if (!_process->start())
|
|
{
|
|
// error
|
|
KMessageBox::error(
|
|
ks_main,
|
|
MiscUtils::HTML(i18n("Could not execute command<br><br><b>%1</b>").arg(_process->arguments().join(" ")))
|
|
);
|
|
cb_processes->setEnabled(false);
|
|
cb_processes->insertItem(SmallIcon("messagebox_warning"), i18n("Error"));
|
|
b_kill->setEnabled(false);
|
|
b_refresh->setEnabled(true);
|
|
}
|
|
}
|
|
|
|
void AppObserver::setWidgetsEnabled(const bool yes) const
|
|
{
|
|
b_refresh->setEnabled(yes);
|
|
cb_processes->setEnabled(yes);
|
|
b_kill->setEnabled(yes);
|
|
}
|
|
|
|
// private slots
|
|
|
|
void AppObserver::slotKill()
|
|
{
|
|
int index = cb_processes->currentItem();
|
|
QMapIterator<int, Process> i = _processesMap->find(index);
|
|
// FIXME: 2.0: error message
|
|
if (i != _processesMap->end())
|
|
{
|
|
if (KMessageBox::warningYesNo(
|
|
ks_main,
|
|
MiscUtils::HTML(i18n("Are you sure you want to KILL<br><b>%1</b>?<br><br>All unsaved data will be lost!").arg(cb_processes->currentText()))
|
|
) != KMessageBox::Yes)
|
|
return;
|
|
|
|
pid_t pid = i.data().pid;
|
|
if (::kill(pid, SIGKILL))
|
|
{
|
|
switch (errno)
|
|
{
|
|
case EINVAL:
|
|
// kdDebug() << "EINVAL" << endl;
|
|
break;
|
|
case ESRCH:
|
|
KMessageBox::error(
|
|
ks_main,
|
|
MiscUtils::HTML(i18n("Process not found<br><b>%1</b>").arg(cb_processes->currentText()))
|
|
);
|
|
break;
|
|
case EPERM:
|
|
KMessageBox::error(
|
|
ks_main,
|
|
MiscUtils::HTML(i18n("No permissions to kill<br><b>%1</b>").arg(cb_processes->currentText()))
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cb_processes->changeItem(
|
|
*cb_processes->pixmap(index),
|
|
i18n("DEAD: %1").arg(cb_processes->text(index)),
|
|
index
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AppObserver::slotProcessExit()
|
|
{
|
|
// kdDebug() << "AppObserver::slotProcessExit()" << endl;
|
|
|
|
int index = 0;
|
|
int line = 0;
|
|
Process p;
|
|
QStringList list = QStringList::split(" ", _buf.simplifyWhiteSpace());
|
|
for (QStringList::Iterator i = list.begin(); i != list.end(); ++i)
|
|
{
|
|
switch (line)
|
|
{
|
|
// user
|
|
case 0:
|
|
line++; // next is pid
|
|
p = Process();
|
|
p.user = *i;
|
|
break;
|
|
// pid
|
|
case 1:
|
|
line++; // next is command
|
|
p.pid = (*i).toLong();
|
|
break;
|
|
// command
|
|
case 2:
|
|
line = 0; // next is user (wrap around)
|
|
p.command = *i;
|
|
_processesMap->insert(index, p);
|
|
// kdDebug() << "USER=" << p.user << " PID=" << p.pid << " COMMAND=" << p.command << endl;
|
|
QString text = QString("%1 (pid %2, %3)")
|
|
.arg(p.command)
|
|
.arg(p.pid)
|
|
.arg(p.user);
|
|
cb_processes->insertItem(SmallIcon(p.command), text);
|
|
index++;
|
|
break;
|
|
}
|
|
}
|
|
if (cb_processes->count() == 0)
|
|
{
|
|
cb_processes->setEnabled(false);
|
|
cb_processes->insertItem(SmallIcon("messagebox_warning"), i18n("Error"));
|
|
b_kill->setEnabled(false);
|
|
}
|
|
else
|
|
{
|
|
cb_processes->setEnabled(true);
|
|
b_kill->setEnabled(true);
|
|
}
|
|
b_refresh->setEnabled(true);
|
|
}
|
|
|
|
void AppObserver::slotReadStdout()
|
|
{
|
|
// kdDebug() << "AppObserver::slotReadStdout()" << endl;
|
|
|
|
_buf.append(_process->readStdout());
|
|
}
|
|
|
|
void AppObserver::slotRefresh()
|
|
{
|
|
refresh();
|
|
}
|