File indexing completed on 2024-04-28 05:31:37
0001 /* 0002 SPDX-FileCopyrightText: 2019 Arjen Hiemstra <ahiemstra@heimr.nl> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "process_controller.h" 0008 0009 #include <functional> 0010 0011 #include <QWindow> 0012 0013 #include <KAuth/Action> 0014 #include <KAuth/ExecuteJob> 0015 0016 #include <KLocalizedString> 0017 0018 #include "processcore_debug.h" 0019 #include "processes_local_p.h" 0020 0021 using namespace KSysGuard; 0022 0023 struct ApplyResult { 0024 ProcessController::Result resultCode = ProcessController::Result::Success; 0025 QList<int> unchanged; 0026 }; 0027 0028 class ProcessController::Private 0029 { 0030 public: 0031 ApplyResult applyToPids(const QList<int> &pids, const std::function<Processes::Error(int)> &function); 0032 ProcessController::Result runKAuthAction(const QString &actionId, const QList<int> &pids, const QVariantMap &options); 0033 QList<int> listToVector(const QList<long long> &list); 0034 QList<int> listToVector(const QVariantList &list); 0035 0036 QWindow *window = nullptr; 0037 }; 0038 0039 // Note: This instance is only to have access to the platform-specific code 0040 // for sending signals, setting priority etc. Therefore, it should never be 0041 // used to access information about processes. 0042 Q_GLOBAL_STATIC(ProcessesLocal, s_localProcesses); 0043 0044 ProcessController::ProcessController(QObject *parent) 0045 : QObject(parent) 0046 , d(new Private) 0047 { 0048 } 0049 0050 KSysGuard::ProcessController::~ProcessController() 0051 { 0052 // Empty destructor needed for std::unique_ptr to incomplete class. 0053 } 0054 0055 QWindow *KSysGuard::ProcessController::window() const 0056 { 0057 return d->window; 0058 } 0059 0060 void KSysGuard::ProcessController::setWindow(QWindow *window) 0061 { 0062 d->window = window; 0063 } 0064 0065 ProcessController::Result ProcessController::sendSignal(const QList<int> &pids, int signal) 0066 { 0067 qCDebug(LIBKSYSGUARD_PROCESSCORE) << "Sending signal" << signal << "to" << pids; 0068 0069 auto result = d->applyToPids(pids, [signal](int pid) { 0070 return s_localProcesses->sendSignal(pid, signal); 0071 }); 0072 if (result.unchanged.isEmpty()) { 0073 return result.resultCode; 0074 } 0075 0076 return d->runKAuthAction(QStringLiteral("org.kde.ksysguard.processlisthelper.sendsignal"), result.unchanged, {{QStringLiteral("signal"), signal}}); 0077 } 0078 0079 KSysGuard::ProcessController::Result KSysGuard::ProcessController::sendSignal(const QList<long long> &pids, int signal) 0080 { 0081 return sendSignal(d->listToVector(pids), signal); 0082 } 0083 0084 KSysGuard::ProcessController::Result KSysGuard::ProcessController::sendSignal(const QVariantList &pids, int signal) 0085 { 0086 return sendSignal(d->listToVector(pids), signal); 0087 } 0088 0089 ProcessController::Result ProcessController::setPriority(const QList<int> &pids, int priority) 0090 { 0091 auto result = d->applyToPids(pids, [priority](int pid) { 0092 return s_localProcesses->setNiceness(pid, priority); 0093 }); 0094 if (result.unchanged.isEmpty()) { 0095 return result.resultCode; 0096 } 0097 0098 return d->runKAuthAction(QStringLiteral("org.kde.ksysguard.processlisthelper.renice"), result.unchanged, {{QStringLiteral("nicevalue"), priority}}); 0099 } 0100 0101 KSysGuard::ProcessController::Result KSysGuard::ProcessController::setPriority(const QList<long long> &pids, int priority) 0102 { 0103 return setPriority(d->listToVector(pids), priority); 0104 } 0105 0106 KSysGuard::ProcessController::Result KSysGuard::ProcessController::setPriority(const QVariantList &pids, int priority) 0107 { 0108 return setPriority(d->listToVector(pids), priority); 0109 } 0110 0111 ProcessController::Result ProcessController::setCPUScheduler(const QList<int> &pids, Process::Scheduler scheduler, int priority) 0112 { 0113 if (scheduler == KSysGuard::Process::Other || scheduler == KSysGuard::Process::Batch) { 0114 priority = 0; 0115 } 0116 0117 auto result = d->applyToPids(pids, [scheduler, priority](int pid) { 0118 return s_localProcesses->setScheduler(pid, scheduler, priority); 0119 }); 0120 if (result.unchanged.isEmpty()) { 0121 return result.resultCode; 0122 } 0123 0124 return d->runKAuthAction(QStringLiteral("org.kde.ksysguard.processlisthelper.changecpuscheduler"), 0125 result.unchanged, // 0126 {{QStringLiteral("cpuScheduler"), scheduler}, {QStringLiteral("cpuSchedulerPriority"), priority}}); 0127 } 0128 0129 KSysGuard::ProcessController::Result KSysGuard::ProcessController::setCPUScheduler(const QList<long long> &pids, Process::Scheduler scheduler, int priority) 0130 { 0131 return setCPUScheduler(d->listToVector(pids), scheduler, priority); 0132 } 0133 0134 KSysGuard::ProcessController::Result KSysGuard::ProcessController::setCPUScheduler(const QVariantList &pids, Process::Scheduler scheduler, int priority) 0135 { 0136 return setCPUScheduler(d->listToVector(pids), scheduler, priority); 0137 } 0138 0139 ProcessController::Result ProcessController::setIOScheduler(const QList<int> &pids, Process::IoPriorityClass priorityClass, int priority) 0140 { 0141 if (!s_localProcesses->supportsIoNiceness()) { 0142 return Result::Unsupported; 0143 } 0144 0145 if (priorityClass == KSysGuard::Process::None) { 0146 priorityClass = KSysGuard::Process::BestEffort; 0147 } 0148 0149 if (priorityClass == KSysGuard::Process::Idle) { 0150 priority = 0; 0151 } 0152 0153 auto result = d->applyToPids(pids, [priorityClass, priority](int pid) { 0154 return s_localProcesses->setIoNiceness(pid, priorityClass, priority); 0155 }); 0156 if (result.unchanged.isEmpty()) { 0157 return result.resultCode; 0158 } 0159 0160 return d->runKAuthAction(QStringLiteral("org.kde.ksysguard.processlisthelper.changeioscheduler"), 0161 result.unchanged, // 0162 {{QStringLiteral("ioScheduler"), priorityClass}, {QStringLiteral("ioSchedulerPriority"), priority}}); 0163 } 0164 0165 KSysGuard::ProcessController::Result 0166 KSysGuard::ProcessController::setIOScheduler(const QList<long long> &pids, Process::IoPriorityClass priorityClass, int priority) 0167 { 0168 return setIOScheduler(d->listToVector(pids), priorityClass, priority); 0169 } 0170 0171 KSysGuard::ProcessController::Result 0172 KSysGuard::ProcessController::setIOScheduler(const QVariantList &pids, Process::IoPriorityClass priorityClass, int priority) 0173 { 0174 return setIOScheduler(d->listToVector(pids), priorityClass, priority); 0175 } 0176 0177 QString ProcessController::resultToString(Result result) 0178 { 0179 switch (result) { 0180 case Result::Success: 0181 return i18n("Success"); 0182 case Result::InsufficientPermissions: 0183 return i18n("Insufficient permissions."); 0184 case Result::NoSuchProcess: 0185 return i18n("No matching process was found."); 0186 case Result::Unsupported: 0187 return i18n("Not supported on the current system."); 0188 case Result::UserCancelled: 0189 return i18n("The user cancelled."); 0190 case Result::Error: 0191 return i18n("An unspecified error occurred."); 0192 default: 0193 return i18n("An unknown error occurred."); 0194 } 0195 } 0196 0197 ApplyResult KSysGuard::ProcessController::Private::applyToPids(const QList<int> &pids, const std::function<Processes::Error(int)> &function) 0198 { 0199 ApplyResult result; 0200 0201 for (auto pid : pids) { 0202 auto error = function(pid); 0203 switch (error) { 0204 case KSysGuard::Processes::InsufficientPermissions: 0205 case KSysGuard::Processes::Unknown: 0206 result.unchanged << pid; 0207 result.resultCode = Result::InsufficientPermissions; 0208 break; 0209 case Processes::InvalidPid: 0210 case Processes::ProcessDoesNotExistOrZombie: 0211 case Processes::InvalidParameter: 0212 result.resultCode = Result::NoSuchProcess; 0213 break; 0214 case Processes::NotSupported: 0215 result.resultCode = Result::Unsupported; 0216 break; 0217 case Processes::NoError: 0218 break; 0219 } 0220 } 0221 return result; 0222 } 0223 0224 ProcessController::Result ProcessController::Private::runKAuthAction(const QString &actionId, const QList<int> &pids, const QVariantMap &options) 0225 { 0226 KAuth::Action action(actionId); 0227 if (!action.isValid()) { 0228 qCWarning(LIBKSYSGUARD_PROCESSCORE) << "Executing KAuth action" << actionId << "failed because it is an invalid action"; 0229 return Result::InsufficientPermissions; 0230 } 0231 action.setParentWindow(window); 0232 action.setHelperId(QStringLiteral("org.kde.ksysguard.processlisthelper")); 0233 0234 const int processCount = pids.count(); 0235 for (int i = 0; i < processCount; ++i) { 0236 action.addArgument(QStringLiteral("pid%1").arg(i), pids.at(i)); 0237 } 0238 action.addArgument(QStringLiteral("pidcount"), processCount); 0239 0240 for (auto itr = options.cbegin(); itr != options.cend(); ++itr) { 0241 action.addArgument(itr.key(), itr.value()); 0242 } 0243 0244 KAuth::ExecuteJob *job = action.execute(); 0245 if (job->exec()) { 0246 return Result::Success; 0247 } else { 0248 if (job->error() == KAuth::ActionReply::UserCancelledError) { 0249 return Result::UserCancelled; 0250 } 0251 0252 if (job->error() == KAuth::ActionReply::AuthorizationDeniedError) { 0253 return Result::InsufficientPermissions; 0254 } 0255 0256 qCWarning(LIBKSYSGUARD_PROCESSCORE) << "Executing KAuth action" << actionId << "failed with error code" << job->error(); 0257 qCWarning(LIBKSYSGUARD_PROCESSCORE) << job->errorString(); 0258 return Result::Error; 0259 } 0260 } 0261 0262 QList<int> KSysGuard::ProcessController::Private::listToVector(const QList<long long> &list) 0263 { 0264 QList<int> vector; 0265 std::transform(list.cbegin(), list.cend(), std::back_inserter(vector), [](long long entry) { 0266 return entry; 0267 }); 0268 return vector; 0269 } 0270 0271 QList<int> KSysGuard::ProcessController::Private::listToVector(const QVariantList &list) 0272 { 0273 QList<int> vector; 0274 std::transform(list.cbegin(), list.cend(), std::back_inserter(vector), [](const QVariant &entry) { 0275 return entry.toInt(); 0276 }); 0277 return vector; 0278 }