KShutdown – graphical shutdown utility
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.

292 lines
7.0KB

  1. /*
  2. appobserver.cpp - An application observer/killer
  3. Copyright (C) 2005 Konrad Twardowski <kdtonline@poczta.onet.pl>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  15. */
  16. #include <sys/types.h>
  17. #include "appobserver.h"
  18. #include "miscutils.h"
  19. #include "mmainwindow.h"
  20. #include <errno.h>
  21. #include <signal.h>
  22. #include <tqcombobox.h>
  23. #include <tqlistbox.h>
  24. #include <tqprocess.h>
  25. #include <tqwhatsthis.h>
  26. #include <kdebug.h>
  27. #include <kiconloader.h>
  28. #include <tdelocale.h>
  29. #include <tdemessagebox.h>
  30. #include <kpushbutton.h>
  31. // TODO: 2.0: bigger combo box list
  32. // http://ariya.blogspot.com/2006/03/resize-pop-up-list-of-combo-box_15.html
  33. // public
  34. AppObserver::AppObserver(TQWidget *parent)
  35. : TQHBox(parent),
  36. _processesMap(new TQMap<int, Process>()),
  37. _process(0),
  38. _buf(TQString::null)
  39. {
  40. setName("AppObserver");
  41. // refresh button
  42. b_refresh = new KPushButton(this, "KPushButton::b_refresh");
  43. b_refresh->setIconSet(SmallIcon("reload"));
  44. b_refresh->setSizePolicy(TQSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Preferred));
  45. MiscUtils::setHint(b_refresh, i18n("Refresh the list of processes"));
  46. connect(b_refresh, SIGNAL(clicked()), SLOT(slotRefresh()));
  47. // processes combo box
  48. cb_processes = new TQComboBox(this, "TQComboBox::cb_processes");
  49. cb_processes->setFocusPolicy(StrongFocus);
  50. MiscUtils::setHint(cb_processes, i18n("List of the running processes"));
  51. // kill button
  52. b_kill = new KPushButton(SmallIcon("edit-delete"), i18n("Kill"), this, "KPushButton::b_kill");
  53. b_kill->setSizePolicy(TQSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Preferred));
  54. MiscUtils::setHint(b_kill, i18n("Kill the selected process"));
  55. connect(b_kill, SIGNAL(clicked()), SLOT(slotKill()));
  56. }
  57. TQString AppObserver::getInfo() const
  58. {
  59. int index = cb_processes->currentItem();
  60. TQMapIterator<int, Process> i = _processesMap->find(index);
  61. if (i == _processesMap->end())
  62. return TQString::null;
  63. return i18n("Waiting for \"%1\"").arg(i.data().command);
  64. }
  65. bool AppObserver::isSelectedProcessRunning() const
  66. {
  67. // TODO: 2.0: common code
  68. int index = cb_processes->currentItem();
  69. TQMapIterator<int, Process> i = _processesMap->find(index);
  70. if (i == _processesMap->end())
  71. return false;
  72. if (::kill(i.data().pid, 0)) // check if process exists
  73. {
  74. switch (errno)
  75. {
  76. case EINVAL: return false;
  77. case ESRCH: return false;
  78. case EPERM: return true;
  79. }
  80. }
  81. return true;
  82. }
  83. bool AppObserver::isValid() const
  84. {
  85. if (!isSelectedProcessRunning())
  86. {
  87. KMessageBox::error(
  88. ks_main,
  89. i18n("The selected process does not exist!")
  90. );
  91. return false;
  92. }
  93. return true;
  94. }
  95. void AppObserver::refresh()
  96. {
  97. // kdDebug() << "AppObserver::refresh()" << endl;
  98. b_refresh->setEnabled(false);
  99. cb_processes->setEnabled(false);
  100. b_kill->setEnabled(false);
  101. _buf = TQString::null;
  102. cb_processes->clear();
  103. _processesMap->clear();
  104. if (!_process)
  105. {
  106. _process = new TQProcess(this);
  107. connect(_process, SIGNAL(processExited()), SLOT(slotProcessExit()));
  108. connect(_process, SIGNAL(readyReadStdout()), SLOT(slotReadStdout()));
  109. }
  110. else
  111. {
  112. if (_process->isRunning())
  113. _process->kill();
  114. _process->clearArguments();
  115. }
  116. _process->addArgument("ps");
  117. // show all processes
  118. _process->addArgument("-A");
  119. // order: user pid command
  120. _process->addArgument("-o");
  121. _process->addArgument("user=");
  122. _process->addArgument("-o");
  123. _process->addArgument("pid=");
  124. _process->addArgument("-o");
  125. _process->addArgument("comm=");
  126. // sort by command
  127. _process->addArgument("--sort");
  128. _process->addArgument("comm");
  129. // start process
  130. if (!_process->start())
  131. {
  132. // error
  133. KMessageBox::error(
  134. ks_main,
  135. MiscUtils::HTML(i18n("Could not execute command<br><br><b>%1</b>").arg(_process->arguments().join(" ")))
  136. );
  137. cb_processes->setEnabled(false);
  138. cb_processes->insertItem(SmallIcon("messagebox_warning"), i18n("Error"));
  139. b_kill->setEnabled(false);
  140. b_refresh->setEnabled(true);
  141. }
  142. }
  143. void AppObserver::setWidgetsEnabled(const bool yes) const
  144. {
  145. b_refresh->setEnabled(yes);
  146. cb_processes->setEnabled(yes);
  147. b_kill->setEnabled(yes);
  148. }
  149. // private slots
  150. void AppObserver::slotKill()
  151. {
  152. int index = cb_processes->currentItem();
  153. TQMapIterator<int, Process> i = _processesMap->find(index);
  154. // FIXME: 2.0: error message
  155. if (i != _processesMap->end())
  156. {
  157. if (KMessageBox::warningYesNo(
  158. ks_main,
  159. 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()))
  160. ) != KMessageBox::Yes)
  161. return;
  162. pid_t pid = i.data().pid;
  163. if (::kill(pid, SIGKILL))
  164. {
  165. switch (errno)
  166. {
  167. case EINVAL:
  168. // kdDebug() << "EINVAL" << endl;
  169. break;
  170. case ESRCH:
  171. KMessageBox::error(
  172. ks_main,
  173. MiscUtils::HTML(i18n("Process not found<br><b>%1</b>").arg(cb_processes->currentText()))
  174. );
  175. break;
  176. case EPERM:
  177. KMessageBox::error(
  178. ks_main,
  179. MiscUtils::HTML(i18n("No permissions to kill<br><b>%1</b>").arg(cb_processes->currentText()))
  180. );
  181. break;
  182. }
  183. }
  184. else
  185. {
  186. cb_processes->changeItem(
  187. *cb_processes->pixmap(index),
  188. i18n("DEAD: %1").arg(cb_processes->text(index)),
  189. index
  190. );
  191. }
  192. }
  193. }
  194. void AppObserver::slotProcessExit()
  195. {
  196. // kdDebug() << "AppObserver::slotProcessExit()" << endl;
  197. int index = 0;
  198. int line = 0;
  199. Process p;
  200. TQStringList list = TQStringList::split(" ", _buf.simplifyWhiteSpace());
  201. for (TQStringList::Iterator i = list.begin(); i != list.end(); ++i)
  202. {
  203. switch (line)
  204. {
  205. // user
  206. case 0:
  207. line++; // next is pid
  208. p = Process();
  209. p.user = *i;
  210. break;
  211. // pid
  212. case 1:
  213. line++; // next is command
  214. p.pid = (*i).toLong();
  215. break;
  216. // command
  217. case 2:
  218. line = 0; // next is user (wrap around)
  219. p.command = *i;
  220. _processesMap->insert(index, p);
  221. // kdDebug() << "USER=" << p.user << " PID=" << p.pid << " COMMAND=" << p.command << endl;
  222. TQString text = TQString("%1 (pid %2, %3)")
  223. .arg(p.command)
  224. .arg(p.pid)
  225. .arg(p.user);
  226. cb_processes->insertItem(SmallIcon(p.command), text);
  227. index++;
  228. break;
  229. }
  230. }
  231. if (cb_processes->count() == 0)
  232. {
  233. cb_processes->setEnabled(false);
  234. cb_processes->insertItem(SmallIcon("messagebox_warning"), i18n("Error"));
  235. b_kill->setEnabled(false);
  236. }
  237. else
  238. {
  239. cb_processes->setEnabled(true);
  240. b_kill->setEnabled(true);
  241. }
  242. b_refresh->setEnabled(true);
  243. }
  244. void AppObserver::slotReadStdout()
  245. {
  246. // kdDebug() << "AppObserver::slotReadStdout()" << endl;
  247. _buf.append(_process->readStdout());
  248. }
  249. void AppObserver::slotRefresh()
  250. {
  251. refresh();
  252. }
  253. #include "appobserver.moc"