Scripts para regular la frecuencia del procesador en función de la temperatura

Nueva versión 8

  • Con o sin GUI. Activado por defecto, se puede desactivar mediante una variable en el script, mediante el parámetro -g o pulsando “No” en el primer diálogo. Se desactiva automáticamente si no detecta ningún escritorio, leyendo la variable $XDG_CURRENT_DESKTOP.
  • Autoinstalación. Si no detecta el script secundario, inicia el script en modo de instalación. Pide permisos mediante pkexec si el GUI está activado o mediante sudo por terminal si no lo está. Crea el script secundario en /usr/bin/setfreq.sh, le asigna propietario root:root y permisos 755, y añade a /etc/sudoers una línea para que deba ejecutarse con sudo pero no pida contraseña. Ya no hace falta usar dos scripts: el script secundario está contenido en el principal, como una cadena de texto.
  • El tamaño del archivo de log se ha limitado a 250 líneas. Se comprueba al final de cada bucle. Cuando se rebasa ese límite, se eliminan las líneas más antiguas.
  • Pequeñas correcciones y optimizaciones.

Falta:

  • Añadir idiomas.
  • El tamaño del log debería ser configurable. En fin, eso es secundario, ya lo haré.
  • Mejorar el formato del log. Ahora mismo no es todo lo legible que me gustaría.

He aquí el monstruo:

temperatura.sh
#!/bin/bash
# -*- ENCODING: UTF-8 -*-

# Default variables. You can change it to run the script easily.
delay=0
seconds=0
t_limit=85
t_normal=75
logfile="/dev/null" # <- File used as log.
notif="yes"
gui=true

# Special argument: if /usr/bin/setfreq.sh is not present, it creates them and stablishes permissions:
if [ "$1" == "install" ]; then
	echo -e '#!/bin/bash\n# -*- ENCODING: UTF-8 -*-\n\n# This script MUST be launched by root\n\n# Setting the governor to "userspace":\nif [ "$1" == "userspace" ]; then\n	echo userspace | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor &> /dev/null\n	test -n "`grep -v "userspace" /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor`" && echo "Failed swithing to the userspace governor! Might not be supported or compiled in." && exit 1\n	exit 0\nfi\n\n# Changing the max speed:\necho "$1" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_setspeed > /dev/null\nexit 0' > /usr/bin/setfreq.sh
	chown root:root /usr/bin/setfreq.sh
	chmod 755 /usr/bin/setfreq.sh
	# This test if sudoers is configured, and configure it if not:
	if [ $(grep -c 'ALL ALL=(ALL) NOPASSWD: /usr/bin/setfreq.sh' /etc/sudoers) -eq 0 ]; then
		echo -e '\n# Users cat execude this with sudo without password:\nALL ALL=(ALL) NOPASSWD: /usr/bin/setfreq.sh' >> /etc/sudoers
	fi
	exit
fi

# Internal variables. Please, do not change.
t_cur=0
t_max_total=0

# Variables assigned by parameters:
if [[ $1 ]]; then
	if [ $1 == "-h" ] || [ $1 == "--help" ]; then
		echo -e "This script monitorize the temperature of the CPU, decreases the maximum frequency when temp is too high, and increase than when temp is low enough, and can create a log with its actions, temperatures, frequencies and date and time."
		echo -e "List of valid parameters:"
		echo -e "-d <seconds> \t Delay before executing the sript."
		echo -e "-t <seconds> \t Time in seconds for check free memory. If 0, the script never stops."
		echo -e "-l <ºC> \t Maximum dessired temperature."
		echo -e "-r <ºC> \t Minimum temperature to restore maximum performance."
		echo -e "-f <file> \t File used as log."
		echo -e "-n <yes | no> \t Enable or disable desktop notifiacions."
		echo -e "-g \t Disable GUI dialogs."
		echo -e "\nExample: Limit frequency when the temperature is 85ºC, restore it when temperature is below 75ºC and show desktop notifications:\n\n ./temperature.sh -l 85 -r 75 -n yes"
		echo -e "\nExample: Limit frequency when the temperature is 70ºC, restore it when temperature is below 50ºC, wait 30 seconds, run the script for 5 minutes (300 seconds), disable GUI dialogs and log the changes in <HOME>/temper.txt:\n\n ./tepmerature.sh -l 70 -r 50 -d 30 -t 300 -g -f ~/temper.txt"
		exit
	fi

# Reading arguments. Add "####" for priority over GUI diaglogs; it will be removed after that.
	while getopts d:t:l:r:f:n:g flag
	do
		case "${flag}" in
		    d) delay=${OPTARG}"####";;
		    t) seconds=${OPTARG}"####";;
		    l) t_limit=${OPTARG}"####";;
		    r) t_normal=${OPTARG}"####";;
		    f) logfile=${OPTARG}"####";;
		    n) notif=${OPTARG}"####";;
		    g) gui=false;;
			*) echo -e "Valid arguments: -d <seconds> -t <seconds> -l <Cº> -n <Cº> -f <file> -n <yes | no>\nUse -h for help."
			exit;;
		esac
	done
fi

# Active or disable notifications.
# Maximum priority are:
# 1º Must exist a desktop.
# 2º Argument from console ( -g ).
# 3º Default variable at the beggining of the script ( notif= ).
# 4º If not disabled yet, show a dialog to acivate or not.

if [ -z "$XDG_CURRENT_DESKTOP" ]; then
 	# There are no desktop.
	notif="no"
	gui=false
elif [ $gui == true ]; then
	# There are desktop and default gui variable is true
	zenity --question --text "¿Configuración gráfica?" --no-wrap --ok-label "Vale" --cancel-label "No" 2>/dev/null
	if [ $? == 0 ]; then
		gui=true
	else
		gui=false
	fi
fi

# AUTOINSTALL
# Test if the superuser part of this script is installed. If not, it launches the script on installation mode.
if ! [ -f /usr/bin/setfreq.sh ]; then
	if [ -z "$XDG_CURRENT_DESKTOP" ] || [ $gui == false ]; then
 		# There are no desktop, or GUI disabled.
		read -r -p "Script /usr/bin/setfreq not found. ¿Install? (y/n)" a
		if [ "$a" == "y" ]; then
			sudo ./temperatura.sh install
		else
			echo "Exiting..."
			exit
		fi
	else
		# GUI enabled.
		zenity --question --text "/usr/bin/setfreq not found. ¿Install?\nIt needs root access." --no-wrap --ok-label "Install" --cancel-label "Cancel" 2>/dev/null
		if [ $? == 0 ]; then
			pkexec ./temperatura.sh install
		else
			exit
		fi
	fi
fi

# GUI: Setting variables not stablished by parameters:
if [ $gui == true ]; then
	if [[ $delay != *"####"* ]]; then
		delay=$(zenity --entry --title "Delay (s): " --text "Time waiting for start the srcipt: " --entry-text="$delay") 2>/dev/null
	fi

	if [[ $seconds != *"####"* ]]; then
		seconds=$(zenity --entry --title "Time (s): " --text "Time running the srcipt: " --entry-text="$seconds") 2>/dev/null
	fi

	if [[ $t_limit != *"####"* ]]; then
		t_limit=$(zenity --entry --title "Max temperature (ºC): " --text "Temperature to start reducing frequency: " --entry-text="$t_limit") 2>/dev/null
	fi

	if [[ $t_normal != *"####"* ]]; then
		t_normal=$(zenity --entry --title "Normal temperature (ºC): " --text "Temperature to start increasing frequency: " --entry-text="$t_normal") 2>/dev/null
	fi
	
	if [[ $notif != *"####"* ]]; then
		zenity --question  --title "Notifiactions" --text "Do tou want desktop notifications?" --no-wrap --ok-label "Yes" --cancel-label "No" 2>/dev/null
		if [ $? == 0 ]; then
			notif="yes"
		else
			notif="no"
		fi
	fi

	if [[ $logfile != *"####"* ]]; then
		wlf=$(zenity --question  --title "Log file" --text "Select a file or create a new one?" --switch --extra-button "Select" --extra-button "Create" --extra-button "None") 2>/dev/null
		if [ $wlf = "Select" ]; then
			logfile_gui=$(zenity --file-selection --text "Location of new file: " --filename "${HOME}/") 2>/dev/null
			if [ $? == 0 ]; then
				logfile=$logfile_gui
			fi
		elif [ $wlf = "Create" ]; then
			logfile_gui=$(zenity --entry --title "Log file" --text "Write the route: " --entry-text="${HOME}/temp_log.txt") 2>/dev/null
			if [ $? == 0 ]; then
				logfile=$logfile_gui
			fi
		else
			logfile=/dev/null
		fi
	fi

fi


# Cleaning variables:
		notif=${notif%"####"}
		delay=${delay%"####"}
		seconds=${seconds%"####"}
		t_limit=${t_limit%"####"}
		t_normal=${t_normal%"####"}
		logfile=${logfile%"####"}
# If used character ~ to use the /home/user folder, change it to ${HOME} to use in redirections:
logfile=${logfile//\~/"${HOME}"}



 echo -e "Delay: $delay\nseconds: $seconds\nt_limit: $t_limit\nt_normal: $t_normal\nlogfile: $logfile\nnotif: $notif\ngui: $gui" ############### FOR DEBUGGIN ############


# For unlimited execution:
if [ $seconds == 0 ]; then
	infinite=1
	seconds=1000
else
	infinite=0	
fi

echo -e "\n"$(date)"\n___________________ Nueva sesión ___________________" >> "$logfile"
sleep $delay

# Setting the governor to "userspace":
sudo /usr/bin/setfreq.sh userspace

# Obtain current, max and min frequency:
f_min0=`cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq 2>/dev/null`
f_max0=`cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq 2>/dev/null`
f_cur0=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 2>/dev/null`
f_newlimit=$f_max0
[ $notif == "yes" ] && zenity --notification --text="-Frecuencia máxima: $f_newlimit" 2>/dev/null

# Setting the current temperature to maximum:
sudo /usr/bin/setfreq.sh $f_newlimit


# ----------------------- Principal loop -----------------------

until [ $seconds -lt 1 ]; do
	# Read the highest temperature from all sensors:
	t_cur=$(sensors | grep °C | awk -F '+' '{print $2}' | awk -F '°C' '{print $1}' | awk -F '.' '{print $1}' | sort -nr | head -n1)

	# Decrease frequency when temperature is too high
	if [ $t_cur -gt $t_limit ] && [ $f_newlimit -gt $f_min0 ]; then
		# Decease frequency in proportion to excesive temperature
		diff=$((t_cur - t_limit))
		step=$((diff * 50000))
		step=$(( step > 400000 ? 400000 : step ))
		f_newlimit=$((f_newlimit - step))
		if [ $f_newlimit -lt $f_min0 ]; then
			f_newlimit=$f_min0
		fi
		sudo /usr/bin/setfreq.sh $f_newlimit
		f_cur0=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq  2>/dev/null`
		echo -e "\n"$(date)"\n-> Bajando frecuencia máxima: "$f_newlimit"\nFrecuencia acual: "$f_cur0"\n Temperatura actual: "$t_cur >> $logfile
		[ $notif == "yes" ] && zenity --notification --text="-> Bajando frecuencia máxima: $f_newlimit" 2>/dev/null
		sleep 4
		((seconds-=4))

	# Icrease frequency when temperature is acceptable
	elif [ $t_cur -lt $t_normal ] && [ $f_newlimit -lt $f_max0 ]; then
		f_newlimit=$((f_newlimit + 200000))
		if [ $f_newlimit -gt $f_max0 ]; then
			f_newlimit=$f_max0
		fi
		sudo /usr/bin/setfreq.sh $f_newlimit
		echo -e "\n"$(date)"\n-> Subiendo frecuencia máxima: "$f_newlimit"\nFrecuencia acual: "$f_cur0"\n Temperatura actual: "$t_cur >> $logfile
		[ $notif == "yes" ] && zenity --notification --text="-> Subiendo frecuencia máxima: $f_newlimit" 2>/dev/null
		f_cur0=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq  2>/dev/null`
		sleep 4
		((seconds-=4))
	fi

	if [ $t_cur -gt $t_max_total ]; then
		t_max_total=$t_cur
		echo -e "\nTemperatura máxima del intervalo: "$t_max_total >> $logfile
	fi
	sleep 1
	((seconds--))
	if [[ $infinite -eq 1 ]]; then
		seconds=1000
	fi

# Limits log file size
if [ $(wc -l < $logfile) -gt 250 ]; then
	templog=$(tail -n 250 $logfile)
	cat <<<$templog > $logfile
fi

done
1 me gusta