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.
kshutdown/kshutdown/appobserver.cpp

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();
}