File indexing completed on 2024-05-05 17:45:01
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, const QVariantList &args) 0025 : Plasma::AbstractRunner(parent, metaData, args) 0026 , m_processes(nullptr) 0027 { 0028 setObjectName(QStringLiteral("Kill Runner")); 0029 0030 auto *sigterm = new QAction(QIcon::fromTheme(QStringLiteral("application-exit")), i18n("Send SIGTERM"), this); 0031 sigterm->setData(15); 0032 auto *sigkill = new QAction(QIcon::fromTheme(QStringLiteral("process-stop")), i18n("Send SIGKILL"), this); 0033 sigkill->setData(9); 0034 m_actionList = {sigterm, sigkill}; 0035 0036 connect(this, &Plasma::AbstractRunner::prepare, this, &KillRunner::prep); 0037 connect(this, &Plasma::AbstractRunner::teardown, this, &KillRunner::cleanup); 0038 0039 m_delayedCleanupTimer.setInterval(50); 0040 m_delayedCleanupTimer.setSingleShot(true); 0041 connect(&m_delayedCleanupTimer, &QTimer::timeout, this, &KillRunner::cleanup); 0042 } 0043 0044 KillRunner::~KillRunner() = default; 0045 0046 void KillRunner::reloadConfiguration() 0047 { 0048 KConfigGroup grp = config(); 0049 m_triggerWord.clear(); 0050 if (grp.readEntry(CONFIG_USE_TRIGGERWORD, true)) { 0051 m_triggerWord = grp.readEntry(CONFIG_TRIGGERWORD, i18n("kill")) + QLatin1Char(' '); 0052 } 0053 m_hasTrigger = !m_triggerWord.isEmpty(); 0054 0055 m_sorting = (Sort)grp.readEntry(CONFIG_SORTING, static_cast<int>(Sort::NONE)); 0056 QList<Plasma::RunnerSyntax> syntaxes; 0057 syntaxes << Plasma::RunnerSyntax(m_triggerWord + QStringLiteral(":q:"), i18n("Terminate running applications whose names match the query.")); 0058 setSyntaxes(syntaxes); 0059 if (m_hasTrigger) { 0060 setTriggerWords({m_triggerWord}); 0061 setMinLetterCount(minLetterCount() + 2); // Requires two characters after trigger word 0062 } else { 0063 setMinLetterCount(2); 0064 setMatchRegex(QRegularExpression()); 0065 } 0066 } 0067 0068 void KillRunner::prep() 0069 { 0070 m_delayedCleanupTimer.stop(); 0071 } 0072 0073 void KillRunner::cleanup() 0074 { 0075 if (!m_processes) { 0076 return; 0077 } 0078 0079 if (m_prepLock.tryLockForWrite()) { 0080 delete m_processes; 0081 m_processes = nullptr; 0082 0083 m_prepLock.unlock(); 0084 } else { 0085 m_delayedCleanupTimer.stop(); 0086 } 0087 } 0088 0089 void KillRunner::match(Plasma::RunnerContext &context) 0090 { 0091 QString term = context.query(); 0092 m_prepLock.lockForRead(); 0093 if (!m_processes) { 0094 m_prepLock.unlock(); 0095 m_prepLock.lockForWrite(); 0096 if (!m_processes) { 0097 suspendMatching(true); 0098 m_processes = new KSysGuard::Processes(); 0099 m_processes->updateAllProcesses(); 0100 suspendMatching(false); 0101 } 0102 } 0103 m_prepLock.unlock(); 0104 0105 term = term.right(term.length() - m_triggerWord.length()); 0106 0107 QList<Plasma::QueryMatch> matches; 0108 const QList<KSysGuard::Process *> processlist = m_processes->getAllProcesses(); 0109 for (const KSysGuard::Process *process : processlist) { 0110 if (!context.isValid()) { 0111 return; 0112 } 0113 const QString name = process->name(); 0114 if (!name.contains(term, Qt::CaseInsensitive)) { 0115 continue; 0116 } 0117 0118 const quint64 pid = process->pid(); 0119 Plasma::QueryMatch match(this); 0120 match.setText(i18n("Terminate %1", name)); 0121 match.setSubtext(i18n("Process ID: %1", QString::number(pid))); 0122 match.setIconName(QStringLiteral("application-exit")); 0123 match.setData(pid); 0124 match.setId(name); 0125 match.setActions(m_actionList); 0126 0127 // Set the relevance 0128 switch (m_sorting) { 0129 case Sort::CPU: 0130 match.setRelevance((process->userUsage() + process->sysUsage()) / 100); 0131 break; 0132 case Sort::CPUI: 0133 match.setRelevance(1 - (process->userUsage() + process->sysUsage()) / 100); 0134 break; 0135 case Sort::NONE: 0136 match.setRelevance(name.compare(term, Qt::CaseInsensitive) == 0 ? 1 : 9); 0137 break; 0138 } 0139 0140 matches << match; 0141 } 0142 0143 context.addMatches(matches); 0144 } 0145 0146 void KillRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match) 0147 { 0148 Q_UNUSED(context) 0149 0150 const quint64 pid = match.data().toUInt(); 0151 0152 int signal; 0153 if (match.selectedAction()) { 0154 signal = match.selectedAction()->data().toInt(); 0155 } else { 0156 signal = 9; // default: SIGKILL 0157 } 0158 0159 const QStringList args = {QStringLiteral("-%1").arg(signal), QString::number(pid)}; 0160 int returnCode = KProcess::execute(QStringLiteral("kill"), args); 0161 if (returnCode == 0) { 0162 return; 0163 } 0164 0165 KAuth::Action killAction = QStringLiteral("org.kde.ksysguard.processlisthelper.sendsignal"); 0166 killAction.setHelperId(QStringLiteral("org.kde.ksysguard.processlisthelper")); 0167 killAction.addArgument(QStringLiteral("pid0"), pid); 0168 killAction.addArgument(QStringLiteral("pidcount"), 1); 0169 killAction.addArgument(QStringLiteral("signal"), signal); 0170 killAction.execute(); 0171 } 0172 0173 #include "killrunner.moc"