File indexing completed on 2024-05-12 05:38:19
0001 /* 0002 SPDX-FileCopyrightText: 2009 Jan Gerrit Marker <jangerrit@weiler-marker.com> 0003 SPDX-FileCopyrightText: 2020 Alexander Lohnau <alexander.lohnau@gmx.de> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "killrunner.h" 0009 0010 #include <QAction> 0011 #include <QDebug> 0012 #include <QIcon> 0013 0014 #include <KAuth/Action> 0015 #include <KConfigGroup> 0016 #include <KLocalizedString> 0017 #include <KProcess> 0018 0019 #include <processcore/process.h> 0020 #include <processcore/processes.h> 0021 0022 K_PLUGIN_CLASS_WITH_JSON(KillRunner, "plasma-runner-kill.json") 0023 0024 KillRunner::KillRunner(QObject *parent, const KPluginMetaData &metaData) 0025 : KRunner::AbstractRunner(parent, metaData) 0026 , m_actionList({ 0027 KRunner::Action(QString::number(15), QStringLiteral("application-exit"), i18n("Send SIGTERM")), 0028 KRunner::Action(QString::number(9), QStringLiteral("process-stop"), i18n("Send SIGKILL")), 0029 }) 0030 , m_processes(new KSysGuard::Processes(QString(), this)) 0031 { 0032 connect(this, &KRunner::AbstractRunner::prepare, m_processes, [this]() { 0033 m_needsRefresh = true; 0034 }); 0035 } 0036 0037 void KillRunner::reloadConfiguration() 0038 { 0039 KConfigGroup grp = config(); 0040 m_triggerWord.clear(); 0041 if (grp.readEntry(CONFIG_USE_TRIGGERWORD, true)) { 0042 m_triggerWord = grp.readEntry(CONFIG_TRIGGERWORD, i18n("kill")) + QLatin1Char(' '); 0043 } 0044 m_hasTrigger = !m_triggerWord.isEmpty(); 0045 0046 m_sorting = (Sort)grp.readEntry(CONFIG_SORTING, static_cast<int>(Sort::NONE)); 0047 QList<KRunner::RunnerSyntax> syntaxes; 0048 syntaxes << KRunner::RunnerSyntax(m_triggerWord + QStringLiteral(":q:"), i18n("Terminate running applications whose names match the query.")); 0049 setSyntaxes(syntaxes); 0050 if (m_hasTrigger) { 0051 setTriggerWords({m_triggerWord}); 0052 setMinLetterCount(minLetterCount() + 2); // Requires two characters after trigger word 0053 } else { 0054 setMinLetterCount(2); 0055 setMatchRegex(QRegularExpression()); 0056 } 0057 } 0058 0059 void KillRunner::match(KRunner::RunnerContext &context) 0060 { 0061 // Only refresh the matches when we are matching. If we were to call it in the prepare slot, we would waste resources 0062 // because very likely the runner will not be used during the current match session 0063 if (m_needsRefresh) { 0064 m_processes->updateAllProcesses(); 0065 if (!context.isValid()) { 0066 return; 0067 } 0068 } 0069 QString term = context.query(); 0070 term = term.right(term.length() - m_triggerWord.length()); 0071 0072 QList<KRunner::QueryMatch> matches; 0073 const QList<KSysGuard::Process *> processlist = m_processes->getAllProcesses(); 0074 for (const KSysGuard::Process *process : processlist) { 0075 if (!context.isValid()) { 0076 return; 0077 } 0078 const QString name = process->name(); 0079 if (!name.contains(term, Qt::CaseInsensitive)) { 0080 continue; 0081 } 0082 0083 const quint64 pid = process->pid(); 0084 KRunner::QueryMatch match(this); 0085 match.setText(i18n("Terminate %1", name)); 0086 match.setSubtext(i18n("Process ID: %1", QString::number(pid))); 0087 match.setIconName(QStringLiteral("application-exit")); 0088 match.setData(pid); 0089 match.setId(name); 0090 match.setActions(m_actionList); 0091 0092 // Set the relevance 0093 switch (m_sorting) { 0094 case Sort::CPU: 0095 match.setRelevance((process->userUsage() + process->sysUsage()) / 100.0); 0096 break; 0097 case Sort::CPUI: 0098 match.setRelevance(1.0 - (process->userUsage() + process->sysUsage()) / 100.0); 0099 break; 0100 case Sort::NONE: 0101 match.setRelevance(name.compare(term, Qt::CaseInsensitive) == 0 ? 1 : 9); 0102 break; 0103 } 0104 0105 matches << match; 0106 } 0107 0108 context.addMatches(matches); 0109 } 0110 0111 void KillRunner::run(const KRunner::RunnerContext & /*context*/, const KRunner::QueryMatch &match) 0112 { 0113 const quint64 pid = match.data().toUInt(); 0114 const int signal = match.selectedAction() ? match.selectedAction().id().toInt() : 9; // default: SIGKILL 0115 0116 int returnCode = KProcess::execute(QStringLiteral("kill"), {QStringLiteral("-%1").arg(signal), QString::number(pid)}); 0117 if (returnCode == 0) { 0118 return; 0119 } 0120 0121 KAuth::Action killAction = QStringLiteral("org.kde.ksysguard.processlisthelper.sendsignal"); 0122 killAction.setHelperId(QStringLiteral("org.kde.ksysguard.processlisthelper")); 0123 killAction.addArgument(QStringLiteral("pid0"), pid); 0124 killAction.addArgument(QStringLiteral("pidcount"), 1); 0125 killAction.addArgument(QStringLiteral("signal"), signal); 0126 killAction.execute(); 0127 } 0128 0129 #include "killrunner.moc"