/* appobserver.cpp - An application observer/killer Copyright (C) 2005 Konrad Twardowski 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 #include "appobserver.h" #include "miscutils.h" #include "mmainwindow.h" #include #include #include #include #include #include #include #include #include #include #include // 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(TQWidget *parent) : TQHBox(parent), _processesMap(new TQMap()), _process(0), _buf(TQString::null) { setName("AppObserver"); // refresh button b_refresh = new KPushButton(this, "KPushButton::b_refresh"); b_refresh->setIconSet(SmallIcon("reload")); b_refresh->setSizePolicy(TQSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Preferred)); MiscUtils::setHint(b_refresh, i18n("Refresh the list of processes")); connect(b_refresh, SIGNAL(clicked()), SLOT(slotRefresh())); // processes combo box cb_processes = new TQComboBox(this, "TQComboBox::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(TQSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Preferred)); MiscUtils::setHint(b_kill, i18n("Kill the selected process")); connect(b_kill, SIGNAL(clicked()), SLOT(slotKill())); } TQString AppObserver::getInfo() const { int index = cb_processes->currentItem(); TQMapIterator i = _processesMap->find(index); if (i == _processesMap->end()) return TQString::null; return i18n("Waiting for \"%1\"").arg(i.data().command); } bool AppObserver::isSelectedProcessRunning() const { // TODO: 2.0: common code int index = cb_processes->currentItem(); TQMapIterator 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 = TQString::null; cb_processes->clear(); _processesMap->clear(); if (!_process) { _process = new TQProcess(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

%1").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(); TQMapIterator 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
%1?

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
%1").arg(cb_processes->currentText())) ); break; case EPERM: KMessageBox::error( ks_main, MiscUtils::HTML(i18n("No permissions to kill
%1").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; TQStringList list = TQStringList::split(" ", _buf.simplifyWhiteSpace()); for (TQStringList::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; TQString text = TQString("%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(); }