[plasma/libksysguard] processcore: processcore: add ProcessController::applyScheduling for one-shot rescheduling

Méven Car <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 2c03761295571c8dd081af4b3a58a2cec70e0cdd by Méven Car.
Committed on 09/08/2026 at 07:12.
Pushed by meven into branch 'master'.

processcore: add ProcessController::applyScheduling for one-shot rescheduling

setPriority, setCpuScheduler and setIoScheduler each request privilege
escalation on their own, so applying all three at once (for example from
a renice dialog) can ask the user to authenticate up to three times.

Add applyScheduling, which applies niceness, CPU scheduler and IO
scheduler together. It applies each change locally first and escalates
the union of the processes that still need privileges through a single
new KAuth action (applyscheduling), so the user is asked to authenticate
at most once.

M  +4    -0    processcore/actions.actions
M  +40   -0    processcore/helper.cpp
M  +1    -0    processcore/helper.h
M  +64   -0    processcore/process_controller.cpp
M  +20   -0    processcore/process_controller.h

https://invent.kde.org/plasma/libksysguard/-/commit/2c03761295571c8dd081af4b3a58a2cec70e0cdd

diff --git a/processcore/actions.actions b/processcore/actions.actions
index 9eb92742..1db1493b 100644
--- a/processcore/actions.actions
+++ b/processcore/actions.actions
@@ -616,3 +616,7 @@ Description[zh_TW]=變更要使用哪個 CPU 排程器來排程指定的行程
 Policy=auth_admin
 
 
+[org.kde.ksysguard.processlisthelper.applyscheduling]
+Name=Change process scheduling
+Description=Change the niceness, CPU scheduler and IO scheduler of a given process in one operation
+Policy=auth_admin
diff --git a/processcore/helper.cpp b/processcore/helper.cpp
index 6ca4ec0b..3ef88ef9 100644
--- a/processcore/helper.cpp
+++ b/processcore/helper.cpp
@@ -149,4 +149,44 @@ ActionReply KSysGuardProcessListHelper::changecpuscheduler(const QVariantMap &pa
     }
 }
 
+ActionReply KSysGuardProcessListHelper::applyscheduling(const QVariantMap &parameters)
+{
+    if (!parameters.contains(QLatin1String("pidcount"))) {
+        return ActionReply(ActionReply::HelperErrorType);
+    }
+
+    KSysGuard::ProcessesLocal processes;
+    const int numProcesses = parameters.value(QStringLiteral("pidcount")).toInt();
+
+    const bool hasNice = parameters.contains(QLatin1String("nicevalue"));
+    const bool hasCpu = parameters.contains(QLatin1String("cpuScheduler")) && parameters.contains(QLatin1String("cpuSchedulerPriority"));
+    const bool hasIo = parameters.contains(QLatin1String("ioScheduler")) && parameters.contains(QLatin1String("ioSchedulerPriority"));
+
+    const int niceValue = qvariant_cast<int>(parameters.value(QStringLiteral("nicevalue")));
+    const int cpuScheduler = qvariant_cast<int>(parameters.value(QStringLiteral("cpuScheduler")));
+    const int cpuSchedulerPriority = qvariant_cast<int>(parameters.value(QStringLiteral("cpuSchedulerPriority")));
+    const int ioScheduler = qvariant_cast<int>(parameters.value(QStringLiteral("ioScheduler")));
+    const int ioSchedulerPriority = qvariant_cast<int>(parameters.value(QStringLiteral("ioSchedulerPriority")));
+
+    bool success = true;
+    for (int i = 0; i < numProcesses; ++i) {
+        qlonglong pid = GET_PID(i);
+        if (hasNice) {
+            success &= (processes.setNiceness(pid, niceValue) == KSysGuard::Processes::NoError);
+        }
+        if (hasCpu) {
+            success &= (processes.setScheduler(pid, cpuScheduler, cpuSchedulerPriority) == KSysGuard::Processes::NoError);
+        }
+        if (hasIo) {
+            success &= (processes.setIoNiceness(pid, ioScheduler, ioSchedulerPriority) == KSysGuard::Processes::NoError);
+        }
+    }
+
+    if (success) {
+        return ActionReply::SuccessReply();
+    } else {
+        return ActionReply(ActionReply::HelperErrorType);
+    }
+}
+
 KAUTH_HELPER_MAIN("org.kde.ksysguard.processlisthelper", KSysGuardProcessListHelper)
diff --git a/processcore/helper.h b/processcore/helper.h
index 2900a074..d17c9525 100644
--- a/processcore/helper.h
+++ b/processcore/helper.h
@@ -31,6 +31,7 @@ public Q_SLOTS:
     ActionReply setAffinity(const QVariantMap &parameters);
     ActionReply changeioscheduler(const QVariantMap &parameters);
     ActionReply changecpuscheduler(const QVariantMap &parameters);
+    ActionReply applyscheduling(const QVariantMap &parameters);
 };
 
 Q_DECLARE_METATYPE(QList<long long>)
diff --git a/processcore/process_controller.cpp b/processcore/process_controller.cpp
index 2082ef70..3558d680 100644
--- a/processcore/process_controller.cpp
+++ b/processcore/process_controller.cpp
@@ -267,6 +267,70 @@ ProcessController::Result ProcessController::setIoScheduler(const QVariantList &
     return setIoScheduler(d->listToVector(pids), priorityClass, priority);
 }
 
+ProcessController::Result
+ProcessController::applyScheduling(const QVariantList &pids, int niceValue, Scheduler cpuScheduler, int cpuPriority, IoPriority ioPriorityClass, int ioPriority)
+{
+    const QList<int> pidList = d->listToVector(pids);
+
+    // Mirror the per-property setters: some scheduler classes ignore the priority.
+    if (cpuScheduler == Scheduler::Other || cpuScheduler == Scheduler::Batch) {
+        cpuPriority = 0;
+    }
+    const bool ioSupported = s_localProcesses->supportsIoNiceness();
+    if (ioPriorityClass == IoPriority::Idle) {
+        ioPriority = 0;
+    }
+
+    // Apply each change locally where possible. Each applyToPids call collects the pids it could not
+    // change without privileges; we then escalate the union of those once, instead of three times.
+    auto niceResult = d->applyToPids(pidList, [niceValue](int pid) {
+        return s_localProcesses->setNiceness(pid, niceValue);
+    });
+    auto cpuResult = d->applyToPids(pidList, [cpuScheduler, cpuPriority](int pid) {
+        return s_localProcesses->setScheduler(pid, cpuScheduler, cpuPriority);
+    });
+    ApplyResult ioResult;
+    if (ioSupported) {
+        ioResult = d->applyToPids(pidList, [ioPriorityClass, ioPriority](int pid) {
+            return s_localProcesses->setIoNiceness(pid, ioPriorityClass, ioPriority);
+        });
+    }
+
+    QList<int> unchanged = niceResult.unchanged;
+    for (int pid : std::as_const(cpuResult.unchanged)) {
+        if (!unchanged.contains(pid)) {
+            unchanged << pid;
+        }
+    }
+    for (int pid : std::as_const(ioResult.unchanged)) {
+        if (!unchanged.contains(pid)) {
+            unchanged << pid;
+        }
+    }
+
+    if (unchanged.isEmpty()) {
+        // Nothing needed privilege escalation; report the first non-success result, if any.
+        for (auto code : {niceResult.resultCode, cpuResult.resultCode, ioResult.resultCode}) {
+            if (code != Result::Success) {
+                return code;
+            }
+        }
+        return Result::Success;
+    }
+
+    QVariantMap options = {
+        {QStringLiteral("nicevalue"), niceValue},
+        {QStringLiteral("cpuScheduler"), cpuScheduler},
+        {QStringLiteral("cpuSchedulerPriority"), cpuPriority},
+    };
+    if (ioSupported) {
+        options.insert(QStringLiteral("ioScheduler"), ioPriorityClass);
+        options.insert(QStringLiteral("ioSchedulerPriority"), ioPriority);
+    }
+
+    return d->runKAuthAction(QStringLiteral("org.kde.ksysguard.processlisthelper.applyscheduling"), unchanged, options);
+}
+
 int ProcessController::ioPriority(long long pid)
 {
     return s_localProcesses->getIoNiceness(pid);
diff --git a/processcore/process_controller.h b/processcore/process_controller.h
index 73892422..9dc3ef4c 100644
--- a/processcore/process_controller.h
+++ b/processcore/process_controller.h
@@ -231,6 +231,26 @@ public:
      */
     Q_INVOKABLE Result setIoScheduler(const QVariantList &pids, IoPriority priorityClass, int priority);
 
+    /**
+     * Apply niceness, CPU scheduler and IO scheduler to a number of processes in one go.
+     *
+     * This is equivalent to calling setPriority(), setCpuScheduler() and setIoScheduler() in
+     * sequence, except that when privilege escalation is required it is requested only once for all
+     * three changes instead of once per change. Use this when applying several scheduling changes
+     * at the same time (for example from a renice dialog) to avoid repeated authentication prompts.
+     *
+     * \param pids A vector of pids to change.
+     * \param niceValue The new niceness value. See setPriority().
+     * \param cpuScheduler The new CPU scheduler. See setCpuScheduler().
+     * \param cpuPriority The new CPU scheduler priority. See setCpuScheduler().
+     * \param ioPriorityClass The new IO scheduling class. See setIoScheduler().
+     * \param ioPriority The new IO scheduler priority. See setIoScheduler().
+     *
+     * \return A Result value that indicates whether the action succeeded.
+     */
+    Q_INVOKABLE Result
+    applyScheduling(const QVariantList &pids, int niceValue, Scheduler cpuScheduler, int cpuPriority, IoPriority ioPriorityClass, int ioPriority);
+
     /**
      * Get the IO priority of a process.
      *
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.