Widget KDE Plasma 6 (org.enne2.cpufreq) per pilotare il profilo energetico della CPU: governor, limiti min/max, energy_performance_preference, boost/turbo, quattro profili pronti (Silenzioso, Risparmio, Bilanciato, Performance) e task con profilo dedicato che ripristina lo stato precedente alla fine del comando. Privilegi separati: il widget resta non privilegiato e l'unica operazione root passa da un helper a interfaccia ristretta in /usr/local/libexec/enne2-cpufreq/ (comandi a elenco chiuso, argomenti validati su sysfs, rilettura di verifica), invocato via pkexec con l'azione polkit dedicata org.enne2.cpufreq.set. La regola in /etc/polkit-1/rules.d/ concede il consenso senza prompt a utenti locali in sessione attiva nel gruppo wheel; senza la regola la policy chiede la password. Dettagli verificati su frigate (Plasma 6.7.5, kernel 7.2, amd-pstate-epp): - con boost disattivato cpuinfo_max_freq scende alla frequenza nominale, quindi il tetto hardware si legge da amd_pstate_max_freq e i profili applicano il boost prima dei limiti; - scrittura dei limiti in ordine sicuro (mai min > max intermedio); - baseline e contatore di riferimento per i task paralleli; - syncControls protetto da uiReady e try/catch: un ReferenceError su un id della rappresentazione non deve interrompere l'aggiornamento dello stato; - implicitWidth/Height costanti, altrimenti il containment riduce il widget a 68x68. Include install.sh/uninstall.sh (widget, backend, dry-run), README con modello di sicurezza e diagnostica, screenshot docs/widget.png. Licenza MIT.
176 lines
5.8 KiB
Bash
Executable File
176 lines
5.8 KiB
Bash
Executable File
#!/bin/sh
|
|
# SPDX-License-Identifier: MIT
|
|
# enne2-cpufreq — lettore di stato NON privilegiato per il plasmoide.
|
|
# Legge solo attributi world-readable di sysfs, /proc e hwmon; non scrive nulla.
|
|
# Uscita: righe key=value (formato usato dagli altri widget org.enne2.*).
|
|
set -u
|
|
|
|
SELF_DIR=$(dirname "$0")
|
|
if [ -r "$SELF_DIR/profiles.sh" ]; then
|
|
# shellcheck source=profiles.sh
|
|
. "$SELF_DIR/profiles.sh"
|
|
else
|
|
printf 'helper=missing\nerror=profiles.sh mancante\n'
|
|
exit 2
|
|
fi
|
|
|
|
CPUFREQ=/sys/devices/system/cpu/cpufreq
|
|
HELPER=/usr/local/libexec/enne2-cpufreq/cpufreq-helper
|
|
POLICY_POLKIT=/usr/share/polkit-1/actions/org.enne2.cpufreq.set.policy
|
|
RULE_POLKIT=/etc/polkit-1/rules.d/50-org.enne2.cpufreq.rules
|
|
CONF_DIR=${XDG_CONFIG_HOME:-$HOME/.config}/enne2-cpufreq
|
|
TASKS=$CONF_DIR/tasks.json
|
|
RUN=${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/enne2-cpufreq
|
|
|
|
readf() {
|
|
[ -r "$1" ] || return 1
|
|
_val=
|
|
IFS= read -r _val < "$1" 2>/dev/null || [ -n "$_val" ] || return 1
|
|
printf '%s\n' "$_val"
|
|
}
|
|
|
|
policies() {
|
|
for _p in "$CPUFREQ"/policy[0-9]*; do
|
|
[ -d "$_p" ] && printf '%s\n' "$_p"
|
|
done
|
|
}
|
|
|
|
attr_values() {
|
|
for _p in $(policies); do
|
|
readf "$_p/$1"
|
|
done | sort -u | tr '\n' ' ' | sed 's/ *$//'
|
|
}
|
|
|
|
p0=$(policies | head -n 1)
|
|
if [ -z "$p0" ]; then
|
|
printf 'helper=missing\nerror=nessuna policy cpufreq (driver non cpufreq?)\n'
|
|
exit 2
|
|
fi
|
|
|
|
driver=$(readf "$p0/scaling_driver" || echo unknown)
|
|
hw_min=$(readf "$p0/cpuinfo_min_freq" || echo 0)
|
|
hw_max=$(cf_hw_max_khz "$p0") # indipendente dallo stato del boost
|
|
floor=$(readf "$p0/amd_pstate_lowest_nonlinear_freq" || echo "")
|
|
[ -n "$floor" ] && [ "$floor" -gt "$hw_min" ] || floor=$hw_min
|
|
min_khz=$(readf "$p0/scaling_min_freq" || echo 0)
|
|
max_khz=$(readf "$p0/scaling_max_freq" || echo 0)
|
|
governor=$(attr_values scaling_governor)
|
|
governors=$(readf "$p0/scaling_available_governors" || echo "")
|
|
epp=$(attr_values energy_performance_preference)
|
|
epps=$(readf "$p0/energy_performance_available_preferences" || echo "")
|
|
epp_supported=0
|
|
[ -n "$epps" ] && epp_supported=1
|
|
|
|
# boost: per-policy (kernel recenti) → globale → intel_pstate/no_turbo invertito
|
|
boost=$(readf "$p0/boost" || readf "$CPUFREQ/boost" || echo "")
|
|
boost_supported=0
|
|
[ -n "$boost" ] && boost_supported=1
|
|
if [ "$boost_supported" -eq 0 ] && [ -r /sys/devices/system/cpu/intel_pstate/no_turbo ]; then
|
|
_nt=$(readf /sys/devices/system/cpu/intel_pstate/no_turbo)
|
|
[ "$_nt" = "1" ] && boost=0 || boost=1
|
|
boost_supported=1
|
|
fi
|
|
|
|
# frequenza istantanea: massima fra le policy (core più attivo) e media
|
|
sum=0
|
|
cnt=0
|
|
curmax=0
|
|
for _p in $(policies); do
|
|
_f=$(readf "$_p/scaling_cur_freq") || continue
|
|
case "$_f" in ''|*[!0-9]*) continue ;; esac
|
|
sum=$((sum + _f))
|
|
cnt=$((cnt + 1))
|
|
[ "$_f" -gt "$curmax" ] && curmax=$_f
|
|
done
|
|
[ "$cnt" -gt 0 ] || cnt=1
|
|
cur_avg=$((sum / cnt))
|
|
|
|
temp=$(readf /sys/class/hwmon/hwmon0/temp1_input 2>/dev/null || echo "")
|
|
for _h in /sys/class/hwmon/hwmon*; do
|
|
[ -r "$_h/name" ] || continue
|
|
case "$(readf "$_h/name")" in
|
|
k10temp|coretemp|zenpower)
|
|
temp=$(readf "$_h/temp1_input" || echo "")
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
model=$(sed -n 's/^model name[[:space:]]*: //p' /proc/cpuinfo | head -n 1)
|
|
load1=$(awk '{print $1}' /proc/loadavg)
|
|
|
|
helper_state=missing
|
|
[ -x "$HELPER" ] && helper_state=ok
|
|
polkit_state=missing
|
|
if [ -r "$POLICY_POLKIT" ] && [ -r "$RULE_POLKIT" ]; then
|
|
polkit_state=ok
|
|
elif [ -r "$POLICY_POLKIT" ]; then
|
|
polkit_state=ask
|
|
fi
|
|
|
|
conflicts=""
|
|
for _svc in tuned power-profiles-daemon tlp; do
|
|
if systemctl is-active --quiet "$_svc" 2>/dev/null; then
|
|
conflicts="$conflicts $_svc"
|
|
fi
|
|
done
|
|
conflicts=${conflicts# }
|
|
|
|
profile=$(cf_detect_profile "${governor:-}" "${epp:-}" "${boost:-0}" "$max_khz" "$floor" "$hw_max")
|
|
printf 'driver=%s\n' "$driver"
|
|
printf 'mode=%s\n' "$(readf /sys/devices/system/cpu/amd_pstate/status || echo n/a)"
|
|
printf 'model=%s\n' "$model"
|
|
printf 'policies=%s\n' "$cnt"
|
|
printf 'governor=%s\n' "$governor"
|
|
printf 'governors=%s\n' "$governors"
|
|
printf 'epp=%s\n' "$epp"
|
|
printf 'epps=%s\n' "$epps"
|
|
printf 'epp_supported=%s\n' "$epp_supported"
|
|
printf 'boost=%s\n' "$boost"
|
|
printf 'boost_supported=%s\n' "$boost_supported"
|
|
printf 'hw_min_khz=%s\n' "$hw_min"
|
|
printf 'hw_max_khz=%s\n' "$hw_max"
|
|
printf 'floor_khz=%s\n' "$floor"
|
|
printf 'min_khz=%s\n' "$min_khz"
|
|
printf 'max_khz=%s\n' "$max_khz"
|
|
printf 'cur_khz=%s\n' "$curmax"
|
|
printf 'cur_avg_khz=%s\n' "$cur_avg"
|
|
printf 'temp_mdegc=%s\n' "$temp"
|
|
printf 'load1=%s\n' "$load1"
|
|
printf 'profile=%s\n' "$profile"
|
|
printf 'profile_label=%s\n' "$(cf_profile_label "$profile")"
|
|
printf 'helper=%s\n' "$helper_state"
|
|
printf 'polkit=%s\n' "$polkit_state"
|
|
printf 'conflicts=%s\n' "$conflicts"
|
|
[ -r /var/lib/enne2-cpufreq/baseline ] && printf 'baseline=present\n' || printf 'baseline=absent\n'
|
|
|
|
# ---- task configurati (un file JSON gestito da enne2-cpufreq-task) ----------
|
|
# Formato di ogni riga: task=<indice> <TAB> <running 0|1> <TAB> <oggetto JSON>
|
|
# Il JSON (jq -c, una riga) evita ogni ambiguità su spazi, tabulazioni e pipe
|
|
# dentro i comandi definiti dall'utente.
|
|
if command -v jq >/dev/null 2>&1 && [ -r "$TASKS" ]; then
|
|
_count=$(jq -r '.tasks | length' "$TASKS" 2>/dev/null || echo 0)
|
|
printf 'task_count=%s\n' "${_count:-0}"
|
|
_i=0
|
|
jq -c '.tasks[] | {name: .name, profile: .profile, command: .command, slug: .slug}' \
|
|
"$TASKS" 2>/dev/null |
|
|
while IFS= read -r _line; do
|
|
[ -n "$_line" ] || continue
|
|
_slug=$(printf '%s' "$_line" | jq -r '.slug // ""' 2>/dev/null)
|
|
_running=0
|
|
if [ -n "$_slug" ] && [ -r "$RUN/$_slug.pid" ]; then
|
|
_pid=$(readf "$RUN/$_slug.pid")
|
|
case "$_pid" in
|
|
''|*[!0-9]*) _running=0 ;;
|
|
*) kill -0 "$_pid" 2>/dev/null && _running=1 ;;
|
|
esac
|
|
fi
|
|
printf 'task=%s\t%s\t%s\n' "$_i" "$_running" "$_line"
|
|
_i=$((_i + 1))
|
|
done
|
|
else
|
|
printf 'task_count=0\n'
|
|
fi
|
|
|
|
printf 'ts=%s\n' "$(date +%s)"
|