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