Changeset View
Changeset View
Standalone View
Standalone View
runners/kill/killrunner.cpp
1 | /* Copyright 2009 Jan Gerrit Marker <jangerrit@weiler-marker.com> | 1 | /* Copyright 2009 Jan Gerrit Marker <jangerrit@weiler-marker.com> | ||
---|---|---|---|---|---|
2 | * Copyright 2020 Alexander Lohnau <alexander.lohnau@gmx.de> | ||||
2 | * | 3 | * | ||
3 | * This library is free software; you can redistribute it and/or | 4 | * This library is free software; you can redistribute it and/or | ||
4 | * modify it under the terms of the GNU Lesser General Public | 5 | * modify it under the terms of the GNU Lesser General Public | ||
5 | * License as published by the Free Software Foundation; either | 6 | * License as published by the Free Software Foundation; either | ||
6 | * version 2.1 of the License, or (at your option) version 3, or any | 7 | * version 2.1 of the License, or (at your option) version 3, or any | ||
7 | * later version accepted by the membership of KDE e.V. (or its | 8 | * later version accepted by the membership of KDE e.V. (or its | ||
8 | * successor approved by the membership of KDE e.V.), which shall | 9 | * successor approved by the membership of KDE e.V.), which shall | ||
9 | * act as a proxy defined in Section 6 of version 3 of the license. | 10 | * act as a proxy defined in Section 6 of version 3 of the license. | ||
Show All 10 Lines | |||||
20 | #include "killrunner.h" | 21 | #include "killrunner.h" | ||
21 | 22 | | |||
22 | #include <QAction> | 23 | #include <QAction> | ||
23 | #include <QDebug> | 24 | #include <QDebug> | ||
24 | #include <QIcon> | 25 | #include <QIcon> | ||
25 | 26 | | |||
26 | #include <KProcess> | 27 | #include <KProcess> | ||
27 | #include <KUser> | 28 | #include <KUser> | ||
28 | #include <kauth.h> | 29 | #include <KAuth> | ||
29 | 30 | | |||
30 | #include "processcore/processes.h" | 31 | #include <processcore/processes.h> | ||
31 | #include "processcore/process.h" | 32 | #include <processcore/process.h> | ||
32 | 33 | | |||
33 | #include "killrunner_config.h" | 34 | #include "killrunner_config.h" | ||
34 | 35 | | |||
35 | K_EXPORT_PLASMA_RUNNER(kill, KillRunner) | 36 | K_EXPORT_PLASMA_RUNNER(kill, KillRunner) | ||
36 | 37 | | |||
37 | KillRunner::KillRunner(QObject *parent, const QVariantList& args) | 38 | KillRunner::KillRunner(QObject *parent, const QVariantList& args) | ||
38 | : Plasma::AbstractRunner(parent, args), | 39 | : Plasma::AbstractRunner(parent, args), m_processes(nullptr) | ||
39 | m_processes(nullptr) | | |||
40 | { | 40 | { | ||
41 | Q_UNUSED(args); | 41 | setObjectName(QStringLiteral("Kill Runner")); | ||
42 | setObjectName( QLatin1String("Kill Runner") ); | 42 | | ||
43 | reloadConfiguration(); | 43 | addAction(QStringLiteral("SIGTERM"), QIcon::fromTheme(QStringLiteral("application-exit")), i18n("Send SIGTERM"))->setData(15); | ||
44 | addAction(QStringLiteral("SIGKILL"), QIcon::fromTheme(QStringLiteral("process-stop")), i18n("Send SIGKILL"))->setData(9); | ||||
45 | m_actionList = {action(QStringLiteral("SIGTERM")), action(QStringLiteral("SIGKILL"))}; | ||||
44 | 46 | | |||
45 | connect(this, &Plasma::AbstractRunner::prepare, this, &KillRunner::prep); | 47 | connect(this, &Plasma::AbstractRunner::prepare, this, &KillRunner::prep); | ||
46 | connect(this, &Plasma::AbstractRunner::teardown, this, &KillRunner::cleanup); | 48 | connect(this, &Plasma::AbstractRunner::teardown, this, &KillRunner::cleanup); | ||
47 | 49 | | |||
48 | m_delayedCleanupTimer.setInterval(50); | 50 | m_delayedCleanupTimer.setInterval(50); | ||
49 | m_delayedCleanupTimer.setSingleShot(true); | 51 | m_delayedCleanupTimer.setSingleShot(true); | ||
50 | connect(&m_delayedCleanupTimer, &QTimer::timeout, this, &KillRunner::cleanup); | 52 | connect(&m_delayedCleanupTimer, &QTimer::timeout, this, &KillRunner::cleanup); | ||
51 | } | 53 | } | ||
52 | 54 | | |||
53 | KillRunner::~KillRunner() | 55 | KillRunner::~KillRunner() | ||
54 | { | 56 | { | ||
55 | } | 57 | } | ||
56 | 58 | | |||
57 | 59 | | |||
58 | void KillRunner::reloadConfiguration() | 60 | void KillRunner::reloadConfiguration() | ||
59 | { | 61 | { | ||
60 | KConfigGroup grp = config(); | 62 | KConfigGroup grp = config(); | ||
61 | m_triggerWord.clear(); | 63 | m_triggerWord.clear(); | ||
62 | if (grp.readEntry(CONFIG_USE_TRIGGERWORD, true)) { | 64 | if (grp.readEntry(CONFIG_USE_TRIGGERWORD, true)) { | ||
63 | m_triggerWord = grp.readEntry(CONFIG_TRIGGERWORD, i18n("kill")) + QLatin1Char(' '); | 65 | m_triggerWord = grp.readEntry(CONFIG_TRIGGERWORD, i18n("kill")) + QLatin1Char(' '); | ||
64 | } | 66 | } | ||
67 | m_hasTrigger = !m_triggerWord.isEmpty(); | ||||
65 | 68 | | |||
66 | m_sorting = (KillRunnerConfig::Sort) grp.readEntry(CONFIG_SORTING, 0); | 69 | m_sorting = (KillRunnerConfig::Sort) grp.readEntry(CONFIG_SORTING, 0); | ||
67 | QList<Plasma::RunnerSyntax> syntaxes; | 70 | QList<Plasma::RunnerSyntax> syntaxes; | ||
68 | syntaxes << Plasma::RunnerSyntax(m_triggerWord + QStringLiteral(":q:"), | 71 | syntaxes << Plasma::RunnerSyntax(m_triggerWord + QStringLiteral(":q:"), | ||
69 | i18n("Terminate running applications whose names match the query.")); | 72 | i18n("Terminate running applications whose names match the query.")); | ||
70 | setSyntaxes(syntaxes); | 73 | setSyntaxes(syntaxes); | ||
71 | } | 74 | } | ||
72 | 75 | | |||
Show All 16 Lines | 82 | { | |||
89 | } else { | 92 | } else { | ||
90 | m_delayedCleanupTimer.stop(); | 93 | m_delayedCleanupTimer.stop(); | ||
91 | } | 94 | } | ||
92 | } | 95 | } | ||
93 | 96 | | |||
94 | void KillRunner::match(Plasma::RunnerContext &context) | 97 | void KillRunner::match(Plasma::RunnerContext &context) | ||
95 | { | 98 | { | ||
96 | QString term = context.query(); | 99 | QString term = context.query(); | ||
97 | const bool hasTrigger = !m_triggerWord.isEmpty(); | 100 | if (m_hasTrigger && !term.startsWith(m_triggerWord, Qt::CaseInsensitive)) { | ||
98 | if (hasTrigger && !term.startsWith(m_triggerWord, Qt::CaseInsensitive)) { | | |||
99 | return; | 101 | return; | ||
100 | } | 102 | } | ||
101 | 103 | | |||
102 | m_prepLock.lockForRead(); | 104 | m_prepLock.lockForRead(); | ||
103 | if (!m_processes) { | 105 | if (!m_processes) { | ||
104 | m_prepLock.unlock(); | 106 | m_prepLock.unlock(); | ||
105 | m_prepLock.lockForWrite(); | 107 | m_prepLock.lockForWrite(); | ||
106 | if (!m_processes) { | 108 | if (!m_processes) { | ||
107 | suspendMatching(true); | 109 | suspendMatching(true); | ||
108 | m_processes = new KSysGuard::Processes(); | 110 | m_processes = new KSysGuard::Processes(); | ||
109 | m_processes->updateAllProcesses(); | 111 | m_processes->updateAllProcesses(); | ||
110 | suspendMatching(false); | 112 | suspendMatching(false); | ||
111 | } | 113 | } | ||
112 | } | 114 | } | ||
113 | m_prepLock.unlock(); | 115 | m_prepLock.unlock(); | ||
114 | 116 | | |||
115 | term = term.right(term.length() - m_triggerWord.length()); | 117 | term = term.right(term.length() - m_triggerWord.length()); | ||
116 | 118 | | |||
117 | if (term.length() < 2) { | 119 | if (term.length() < 2 || !context.isValid()) { | ||
118 | return; | 120 | return; | ||
119 | } | 121 | } | ||
120 | 122 | | |||
121 | QList<Plasma::QueryMatch> matches; | 123 | QList<Plasma::QueryMatch> matches; | ||
122 | const QList<KSysGuard::Process *> processlist = m_processes->getAllProcesses(); | 124 | const QList<KSysGuard::Process *> processlist = m_processes->getAllProcesses(); | ||
123 | for (const KSysGuard::Process *process : processlist) { | 125 | for (const KSysGuard::Process *process : processlist) { | ||
124 | if (!context.isValid()) { | | |||
125 | return; | | |||
126 | } | | |||
127 | | ||||
128 | const QString name = process->name(); | 126 | const QString name = process->name(); | ||
129 | if (!name.contains(term, Qt::CaseInsensitive)) { | 127 | if (!name.contains(term, Qt::CaseInsensitive)) { | ||
130 | //Process doesn't match the search term | | |||
131 | continue; | 128 | continue; | ||
132 | } | 129 | } | ||
133 | 130 | | |||
134 | const quint64 pid = process->pid(); | 131 | const quint64 pid = process->pid(); | ||
135 | const qlonglong uid = process->uid(); | | |||
136 | const QString user = getUserName(uid); | | |||
137 | | ||||
138 | QVariantList data; | | |||
139 | data << pid << user; | | |||
140 | 132 | | |||
141 | Plasma::QueryMatch match(this); | 133 | Plasma::QueryMatch match(this); | ||
142 | match.setText(i18n("Terminate %1", name)); | 134 | match.setText(i18n("Terminate %1", name)); | ||
143 | match.setSubtext(i18n("Process ID: %1\nRunning as user: %2", QString::number(pid), user)); | 135 | match.setSubtext(i18n("Process ID: %1", QString::number(pid))); | ||
144 | match.setIconName(QStringLiteral("application-exit")); | 136 | match.setIconName(QStringLiteral("application-exit")); | ||
145 | match.setData(data); | 137 | match.setData(pid); | ||
146 | match.setId(name); | 138 | match.setId(name); | ||
147 | 139 | | |||
148 | // Set the relevance | 140 | // Set the relevance | ||
149 | switch (m_sorting) { | 141 | switch (m_sorting) { | ||
150 | case KillRunnerConfig::CPU: | 142 | case KillRunnerConfig::CPU: | ||
151 | match.setRelevance((process->userUsage() + process->sysUsage()) / 100); | 143 | match.setRelevance((process->userUsage() + process->sysUsage()) / 100); | ||
152 | break; | 144 | break; | ||
153 | case KillRunnerConfig::CPUI: | 145 | case KillRunnerConfig::CPUI: | ||
154 | match.setRelevance(1 - (process->userUsage() + process->sysUsage()) / 100); | 146 | match.setRelevance(1 - (process->userUsage() + process->sysUsage()) / 100); | ||
155 | break; | 147 | break; | ||
156 | case KillRunnerConfig::NONE: | 148 | case KillRunnerConfig::NONE: | ||
157 | match.setRelevance(name.compare(term, Qt::CaseInsensitive) == 0 ? 1 : 9); | 149 | match.setRelevance(name.compare(term, Qt::CaseInsensitive) == 0 ? 1 : 9); | ||
158 | break; | 150 | break; | ||
159 | } | 151 | } | ||
160 | 152 | | |||
161 | matches << match; | 153 | matches << match; | ||
162 | } | 154 | } | ||
163 | 155 | | |||
164 | qDebug() << "match count is" << matches.count(); | | |||
165 | context.addMatches(matches); | 156 | context.addMatches(matches); | ||
166 | } | 157 | } | ||
167 | 158 | | |||
168 | void KillRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match) | 159 | void KillRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match) | ||
169 | { | 160 | { | ||
170 | Q_UNUSED(context) | 161 | Q_UNUSED(context) | ||
171 | 162 | | |||
172 | QVariantList data = match.data().value<QVariantList>(); | 163 | quint64 pid = match.data().toUInt(); | ||
173 | quint64 pid = data[0].toUInt(); | | |||
174 | // QString user = data[1].toString(); | | |||
175 | 164 | | |||
176 | int signal; | 165 | int signal; | ||
177 | if (match.selectedAction() != nullptr) { | 166 | if (match.selectedAction()) { | ||
178 | signal = match.selectedAction()->data().toInt(); | 167 | signal = match.selectedAction()->data().toInt(); | ||
179 | } else { | 168 | } else { | ||
180 | signal = 9; //default: SIGKILL | 169 | signal = 9; //default: SIGKILL | ||
181 | } | 170 | } | ||
182 | 171 | | |||
183 | QStringList args; | 172 | QStringList args = {QStringLiteral("-%1").arg(signal), QStringLiteral("%1").arg(pid)}; | ||
184 | args << QStringLiteral("-%1").arg(signal) << QStringLiteral("%1").arg(pid); | 173 | int returnCode = KProcess::execute(QStringLiteral("kill"), args); | ||
185 | KProcess process; | 174 | if (returnCode == 0) { | ||
186 | int returnCode = process.execute(QStringLiteral("kill"), args); | | |||
187 | | ||||
188 | if (returnCode == 0) | | |||
189 | { | | |||
190 | return; | 175 | return; | ||
191 | } | 176 | } | ||
192 | 177 | | |||
193 | KAuth::Action killAction = QStringLiteral("org.kde.ksysguard.processlisthelper.sendsignal"); | 178 | KAuth::Action killAction = QStringLiteral("org.kde.ksysguard.processlisthelper.sendsignal"); | ||
194 | killAction.setHelperId(QStringLiteral("org.kde.ksysguard.processlisthelper")); | 179 | killAction.setHelperId(QStringLiteral("org.kde.ksysguard.processlisthelper")); | ||
195 | killAction.addArgument(QStringLiteral("pid0"), pid); | 180 | killAction.addArgument(QStringLiteral("pid0"), pid); | ||
196 | killAction.addArgument(QStringLiteral("pidcount"), 1); | 181 | killAction.addArgument(QStringLiteral("pidcount"), 1); | ||
197 | killAction.addArgument(QStringLiteral("signal"), signal); | 182 | killAction.addArgument(QStringLiteral("signal"), signal); | ||
198 | killAction.execute(); | 183 | killAction.execute(); | ||
199 | } | 184 | } | ||
200 | 185 | | |||
201 | QList<QAction*> KillRunner::actionsForMatch(const Plasma::QueryMatch &match) | 186 | QList<QAction*> KillRunner::actionsForMatch(const Plasma::QueryMatch &match) | ||
202 | { | 187 | { | ||
203 | Q_UNUSED(match) | 188 | Q_UNUSED(match) | ||
204 | 189 | | |||
205 | QList<QAction*> ret; | 190 | return m_actionList; | ||
206 | | ||||
207 | if (!action(QStringLiteral("SIGTERM"))) { | | |||
208 | (addAction(QStringLiteral("SIGTERM"), QIcon::fromTheme(QStringLiteral("application-exit")), i18n("Send SIGTERM")))->setData(15); | | |||
209 | (addAction(QStringLiteral("SIGKILL"), QIcon::fromTheme(QStringLiteral("process-stop")), i18n("Send SIGKILL")))->setData(9); | | |||
210 | } | | |||
211 | ret << action(QStringLiteral("SIGTERM")) << action(QStringLiteral("SIGKILL")); | | |||
212 | return ret; | | |||
213 | } | | |||
214 | | ||||
215 | QString KillRunner::getUserName(qlonglong uid) | | |||
216 | { | | |||
217 | KUser user(uid); | | |||
218 | if (user.isValid()) { | | |||
219 | return user.loginName(); | | |||
220 | } | | |||
221 | qDebug() << QStringLiteral("No user with UID %1 was found").arg(uid); | | |||
222 | return QStringLiteral("root");//No user with UID uid was found, so root is used | | |||
223 | } | 191 | } | ||
224 | 192 | | |||
225 | #include "killrunner.moc" | 193 | #include "killrunner.moc" |