Changeset View
Changeset View
Standalone View
Standalone View
processcore/process_controller.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * This file is part of KSysGuard. | ||||
3 | * Copyright 2019 Arjen Hiemstra <ahiemstra@heimr.nl> | ||||
4 | * | ||||
5 | * This program is free software; you can redistribute it and/or | ||||
6 | * modify it under the terms of the GNU General Public License as | ||||
7 | * published by the Free Software Foundation; either version 2 of | ||||
8 | * the License or (at your option) version 3 or any later version | ||||
9 | * accepted by the membership of KDE e.V. (or its successor approved | ||||
10 | * by the membership of KDE e.V.), which shall act as a proxy | ||||
11 | * defined in Section 14 of version 3 of the license. | ||||
12 | * | ||||
13 | * This program is distributed in the hope that it will be useful, | ||||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
16 | * GNU General Public License for more details. | ||||
17 | * | ||||
18 | * You should have received a copy of the GNU General Public License | ||||
19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
20 | */ | ||||
21 | | ||||
22 | #include "process_controller.h" | ||||
23 | | ||||
24 | #include <functional> | ||||
25 | | ||||
26 | #include <KAuth/KAuthAction> | ||||
27 | #include <KAuth/KAuthExecuteJob> | ||||
28 | | ||||
29 | #include "processes_local_p.h" | ||||
30 | #include "processcore_debug.h" | ||||
31 | | ||||
32 | using namespace KSysGuard; | ||||
33 | | ||||
34 | struct ApplyResult | ||||
35 | { | ||||
36 | ProcessController::Result resultCode = ProcessController::Result::Success; | ||||
37 | QVector<int> unchanged; | ||||
38 | }; | ||||
39 | | ||||
40 | class ProcessController::Private | ||||
41 | { | ||||
42 | public: | ||||
43 | ApplyResult applyToPids(const QVector<int> &pids, const std::function<bool(int)> &function); | ||||
44 | ProcessController::Result runKAuthAction(const QString &actionId, const QVector<int> &pids, const QVariantMap &options); | ||||
45 | QVector<int> listToVector(const QList<long long> &list); | ||||
46 | | ||||
47 | QWidget *widget; | ||||
48 | | ||||
49 | // Note: This instance is only to have access to the platform-specific code | ||||
50 | // for sending signals, setting priority etc. Therefore, it should never be | ||||
51 | // used to access information about processes. | ||||
52 | static std::unique_ptr<ProcessesLocal> localProcesses; | ||||
53 | }; | ||||
54 | | ||||
55 | std::unique_ptr<ProcessesLocal> ProcessController::Private::localProcesses; | ||||
56 | | ||||
57 | ProcessController::ProcessController(QObject* parent) | ||||
58 | : QObject(parent), d(new Private) | ||||
59 | { | ||||
60 | if (!d->localProcesses) { | ||||
61 | d->localProcesses = std::make_unique<ProcessesLocal>(); | ||||
62 | } | ||||
63 | } | ||||
64 | | ||||
65 | KSysGuard::ProcessController::~ProcessController() | ||||
66 | { | ||||
67 | // Empty destructor needed for std::unique_ptr to incomplete class. | ||||
68 | } | ||||
69 | | ||||
70 | QWidget * KSysGuard::ProcessController::widget() const | ||||
71 | { | ||||
72 | return d->widget; | ||||
73 | } | ||||
74 | | ||||
75 | void KSysGuard::ProcessController::setWidget(QWidget* widget) | ||||
76 | { | ||||
77 | d->widget = widget; | ||||
78 | } | ||||
79 | | ||||
80 | ProcessController::Result ProcessController::sendSignal(const QVector<int>& pids, int signal) | ||||
81 | { | ||||
82 | qCDebug(LIBKSYSGUARD_PROCESSCORE) << "Sending signal" << signal << "to" << pids; | ||||
83 | | ||||
84 | auto result = d->applyToPids(pids, [this, signal](int pid) { return d->localProcesses->sendSignal(pid, signal); }); | ||||
85 | if (result.unchanged.isEmpty()) { | ||||
86 | return result.resultCode; | ||||
87 | } | ||||
88 | | ||||
89 | return d->runKAuthAction( | ||||
90 | QStringLiteral("org.kde.ksysguard.processlisthelper.sendsignal"), | ||||
91 | result.unchanged, | ||||
92 | { {QStringLiteral("signal"), signal} } | ||||
93 | ); | ||||
94 | } | ||||
95 | | ||||
96 | KSysGuard::ProcessController::Result KSysGuard::ProcessController::sendSignal(const QList<long long>& pids, int signal) | ||||
97 | { | ||||
98 | return sendSignal(d->listToVector(pids), signal); | ||||
99 | } | ||||
100 | | ||||
101 | ProcessController::Result ProcessController::setPriority(const QVector<int>& pids, int priority) | ||||
102 | { | ||||
103 | auto result = d->applyToPids(pids, [this, priority](int pid) { return d->localProcesses->setNiceness(pid, priority); }); | ||||
104 | if (result.unchanged.isEmpty()) { | ||||
105 | return result.resultCode; | ||||
106 | } | ||||
107 | | ||||
108 | return d->runKAuthAction( | ||||
109 | QStringLiteral("org.kde.ksysguard.processlisthelper.renice"), | ||||
110 | result.unchanged, | ||||
111 | { { QStringLiteral("nicevalue"), priority } } | ||||
112 | ); | ||||
113 | } | ||||
114 | | ||||
115 | KSysGuard::ProcessController::Result KSysGuard::ProcessController::setPriority(const QList<long long>& pids, int priority) | ||||
116 | { | ||||
117 | return setPriority(d->listToVector(pids), priority); | ||||
118 | } | ||||
119 | | ||||
120 | ProcessController::Result ProcessController::setCPUScheduler(const QVector<int>& pids, Process::Scheduler scheduler, int priority) | ||||
121 | { | ||||
122 | if (scheduler == KSysGuard::Process::Other || scheduler == KSysGuard::Process::Batch) { | ||||
123 | priority = 0; | ||||
124 | } | ||||
125 | | ||||
126 | auto result = d->applyToPids(pids, [this, scheduler, priority](int pid) { | ||||
127 | return d->localProcesses->setScheduler(pid, scheduler, priority); | ||||
128 | }); | ||||
129 | if (result.unchanged.isEmpty()) { | ||||
130 | return result.resultCode; | ||||
131 | } | ||||
132 | | ||||
133 | return d->runKAuthAction( | ||||
134 | QStringLiteral("org.kde.ksysguard.processlisthelper.changecpuscheduler"), | ||||
135 | result.unchanged, | ||||
136 | {{QStringLiteral("cpuScheduler"), scheduler}, {QStringLiteral("cpuSchedulerPriority"), priority}} | ||||
137 | ); | ||||
138 | } | ||||
139 | | ||||
140 | KSysGuard::ProcessController::Result KSysGuard::ProcessController::setCPUScheduler(const QList<long long>& pids, Process::Scheduler scheduler, int priority) | ||||
141 | { | ||||
142 | return setCPUScheduler(d->listToVector(pids), scheduler, priority); | ||||
143 | } | ||||
144 | | ||||
145 | ProcessController::Result ProcessController::setIOScheduler(const QVector<int>& pids, Process::IoPriorityClass priorityClass, int priority) | ||||
146 | { | ||||
147 | if (!d->localProcesses->supportsIoNiceness()) { | ||||
148 | return Result::Unsupported; | ||||
149 | } | ||||
150 | | ||||
151 | if (priorityClass == KSysGuard::Process::None) { | ||||
152 | priorityClass = KSysGuard::Process::BestEffort; | ||||
153 | } | ||||
154 | | ||||
155 | if (priorityClass == KSysGuard::Process::Idle) { | ||||
156 | priority = 0; | ||||
157 | } | ||||
158 | | ||||
159 | auto result = d->applyToPids(pids, [this, priorityClass, priority](int pid) { | ||||
160 | return d->localProcesses->setIoNiceness(pid, priorityClass, priority); | ||||
161 | }); | ||||
162 | if (result.unchanged.isEmpty()) { | ||||
163 | return result.resultCode; | ||||
164 | } | ||||
165 | | ||||
166 | return d->runKAuthAction( | ||||
167 | QStringLiteral("org.kde.ksysguard.processlisthelper.changeioscheduler"), | ||||
168 | result.unchanged, | ||||
169 | {{QStringLiteral("ioScheduler"), priorityClass}, {QStringLiteral("ioSchedulerPriority"), priority}} | ||||
170 | ); | ||||
171 | } | ||||
172 | | ||||
173 | KSysGuard::ProcessController::Result KSysGuard::ProcessController::setIOScheduler(const QList<long long>& pids, Process::IoPriorityClass priorityClass, int priority) | ||||
174 | { | ||||
175 | return setIOScheduler(d->listToVector(pids), priorityClass, priority); | ||||
176 | } | ||||
177 | | ||||
178 | ApplyResult KSysGuard::ProcessController::Private::applyToPids(const QVector<int>& pids, const std::function<bool(int)>& function) | ||||
179 | { | ||||
180 | ApplyResult result; | ||||
181 | for (auto pid : pids) { | ||||
182 | auto success = function(pid); | ||||
183 | if (!success | ||||
184 | && (localProcesses->errorCode == KSysGuard::Processes::InsufficientPermissions | ||||
185 | || localProcesses->errorCode == KSysGuard::Processes::Unknown)) { | ||||
186 | result.unchanged << pid; | ||||
187 | result.resultCode = Result::InsufficientPermissions; | ||||
188 | } else if (result.resultCode == Result::Success) { | ||||
189 | switch (localProcesses->errorCode) { | ||||
190 | case Processes::InvalidPid: | ||||
191 | case Processes::ProcessDoesNotExistOrZombie: | ||||
192 | case Processes::InvalidParameter: | ||||
193 | result.resultCode = Result::NoSuchProcess; | ||||
194 | break; | ||||
195 | case Processes::NotSupported: | ||||
196 | result.resultCode = Result::Unsupported; | ||||
197 | break; | ||||
198 | default: | ||||
199 | result.resultCode = Result::Unknown; | ||||
200 | break; | ||||
201 | } | ||||
202 | } | ||||
203 | } | ||||
204 | return result; | ||||
205 | } | ||||
206 | | ||||
207 | | ||||
208 | ProcessController::Result ProcessController::Private::runKAuthAction(const QString& actionId, const QVector<int> &pids, const QVariantMap& options) | ||||
209 | { | ||||
210 | KAuth::Action action(actionId); | ||||
211 | if (!action.isValid()) { | ||||
212 | qCWarning(LIBKSYSGUARD_PROCESSCORE) << "Executing KAuth action" << actionId << "failed because it is an invalid action"; | ||||
213 | return Result::InsufficientPermissions; | ||||
214 | } | ||||
215 | action.setParentWidget(widget); | ||||
216 | action.setHelperId(QStringLiteral("org.kde.ksysguard.processlisthelper")); | ||||
217 | | ||||
218 | const int processCount = pids.count(); | ||||
219 | for (int i = 0; i < processCount; ++i) { | ||||
220 | action.addArgument(QStringLiteral("pid%1").arg(i), pids.at(i)); | ||||
221 | } | ||||
222 | action.addArgument(QStringLiteral("pidcount"), processCount); | ||||
223 | | ||||
224 | for (auto itr = options.cbegin(); itr != options.cend(); ++itr) { | ||||
225 | action.addArgument(itr.key(), itr.value()); | ||||
226 | } | ||||
227 | | ||||
228 | KAuth::ExecuteJob *job = action.execute(); | ||||
229 | if(job->exec()) { | ||||
230 | return Result::Success; | ||||
231 | } else { | ||||
232 | if (job->error() == KAuth::ActionReply::UserCancelledError) { | ||||
233 | return Result::UserCancelled; | ||||
234 | } | ||||
235 | | ||||
236 | if (job->error() == KAuth::ActionReply::AuthorizationDeniedError) { | ||||
237 | return Result::InsufficientPermissions; | ||||
238 | } | ||||
239 | | ||||
240 | qCWarning(LIBKSYSGUARD_PROCESSCORE) << "Executing KAuth action" << actionId << "failed with error code" << job->error(); | ||||
241 | qCWarning(LIBKSYSGUARD_PROCESSCORE) << job->errorString(); | ||||
242 | return Result::Error; | ||||
243 | } | ||||
244 | } | ||||
245 | | ||||
246 | QVector<int> KSysGuard::ProcessController::Private::listToVector(const QList<long long>& list) | ||||
247 | { | ||||
248 | QVector<int> vector; | ||||
249 | std::transform(list.cbegin(), list.cend(), std::back_inserter(vector), [](long long entry) { return entry; }); | ||||
250 | return vector; | ||||
251 | } |