SLEEP support development for CM4

This is probably not doing what you think it’s doing. sudo in a script doesn’t really work, because it can’t prompt you for a password. To do this from within a graphical session, you need a setuid binary to write to these files.

Or, you can have a script which runs as root, and watches /dev/input/event0 for power key presses, and use this to toggle the system state.

Sudo can easily be setup to run passwordless, just fyi. based on group or individual user.

1 Like

I knew someone was going to suggest this.

No.

Just no.

Don’t do that.

RPi OS comes setup that way out of the box.

1 Like

They do that for convenience, but it is an insecure default, and you shouldn’t leave it that way. The first thing you should do is set a user password.

And sudo is not appropriate for use in system scripts.

You have a password, when logged in it just doesn’t require it. If you ssh in or use a non-sudoer account a password is required.

1 Like

Sudo is already a security risk, and some admins / distros don’t use it at all. Configuring sudo to not require a password so you can use use it in a shell script is utterly indefensible.

You might get it to work, but you’re leaving your system wide open. And for anyone with a more secure setup, it will not work. I will not be doing this, and I have outlined the alternatives.

Since I’m not very experienced with linux, what should you do if you don’t use sudo? Doas requires another password entry (or not), su requires the same. Are you saying to log out and back in as root, then make the changes, and logout/in as user again?

What is the “proper way” of getting admin level when installing or changing things that are supposed to be system wide?

I would say, if you have autologin to the system (and don’t wear foil cap) then do not worry and use sudo.

or you can create an udev rule, but since you are not very familiar with linux – don’t bother.

So to get this to work with a setup that prompts for sudo password, and actually, also to get the current default Bookworm image to properly prompt for sudo password for the default user, it would be the following:

  1. Edit /etc/sudoers.d/010_pi-nopasswd, which by default will read something like…
<username> ALL=(ALL) NOPASSWD: ALL
  1. Comment out that line and add the absolute path to the script. So something like this…
#<username> ALL=(ALL) NOPASSWD: ALL
<username> ALL=(ALL) NOPASSWD: /home/<username>/.config/qsleep.sh

That way only the sleep script will be allowed to run as sudo without password. Everything else will still prompt for password when invoking “sudo”.

And of course it also means the “sudo tee” part can be executed from within the script, so in which case the mapping in wayfire.ini or labwc xml files can still be used.

If you’re concerned about the “sudo” part being inside of the script, setting up a listener script that listens to the power event is the only way. I have tried to make a non-sudo version of the script and launching it from wayfire or labwc won’t actually work properly for whatever reason.

1 Like

If you followed my instructions above, that should prompt for password for sudo only once per session by default if you’re using the Bookworm image. (maybe I’m wrong but that seems to be the case? Otherwise maybe the timeout is so big that I haven’t been able to trigger it again)

To change that behavior, you can edit /etc/sudoers and add the following lines:

# Prompt for sudo password on every single command
Defaults     timestamp_timeout=0

Which would be the more ideal secure setup.

Alternatively, set that timeout from 0 to any number and that will cause sudo to ask for password again if some minutes have passed. So for instance, asking for sudo password again after 3 minutes would be:

Defaults     timestamp_timeout=3

I would say any number between 3-5 should suffice if you find typing the password every time annoying. Personally I go with 0 to make sure I don’t slip up, but as above, you can add certain scripts or commands to the exception list so you don’t have to keep pumping password into them. I do this for the sleep script and for docker command, for instance.

2 Likes

If you’re concerned about the “sudo” part being inside of the script, setting up a listener script that listens to the power event is the only way. I have tried to make a non-sudo version of the script and launching it from wayfire or labwc won’t actually work properly for whatever reason.

I found I had to do this anyways, since I wanted to lock the screen (via swaylock) in my case. The screen locker prevents the power key from being received by the compositor / window manager to wake back up, and systemd's logind isn’t flexible enough, by itself, to do this.

It wasn’t too hard, it turned out. I found that the libinputs command, which is available via libinput-tools or libinput-utils depending on your distro, will watch for events on the devices you specify, and the output is simple to work with. I found that /dev/input/event0 shows up as a separate keyboard device, and is where the power button events come from on the uConsole.

So, I now have, on my device, a modified version of qsleep.sh that looks like this:

function toggle {
  if test "$(cat /sys/class/drm/card1-DSI-1/enabled)" = "enabled"
  then
    ...
  else
    ...
  fi
}

function watch {
  declare -a event
  libinput debug-events --device /dev/input/event0 | while read -a event
  do
    case "${event[1]} ${event[3]} ${event[5]}" in
	  "KEYBOARD_KEY KEY_POWER released")
        toggle
        ;;
    esac
  done
}

...

The reason for only responding to released is because I still let logind handle a long key press by shutting down the machine, so I can still power off cleanly. It’s nice, when I need to do this, that the screen is still powered on, so that I can see that the machine is actually powering off.

I run my script from within my session, so that wlr-randr and swaylock work without me having to set any specific variables, and I wrote a simple C program that is setuid root to set the governor (at least until I figure out a portable way to do all of this from the session manager).

The full code is here.

2 Likes

As a new user, just use sudo. It’s definitely better than logging in as root. FYI can use sudo -k when you are done, and want to expire your auth token, rather than just letting it time out.

As for alternatives, there’s the su command, where you need the root password rather than your user password. BSD provides doas. There are finer-grained approaches to authentication, like PolicyKit (now Polkit). I’m not sure if any of these things is better in this case, I just wanted to point out that in some settings, sudo isn’t available.

Sometimes, you can avoid uses of sudo by adding your user to the right groups – consult distro-specific documentation to learn which groups are available and what they do.

1 Like

Hi, can that script applied to Bookworm image? I can put device to “sleep” but when pressing Power key again to “wake”, the screen stay dark. At that point, if I type in the password and Enter (still nothing on the screen), then press Pwr Key again, then screen is back to life.

probably…I don’t have time to try right now.

If you want to test it out, I recommend starting a tmux session from within the uconsole’s gui environment, then to ssh in, and tmux attach before you try to sleep. This way you can keep working even if the screen and / or keyboard aren’t functioning.

1 Like

Here’s a new version I have been using for a while. This one will turn off bluetooth + WIFI and will also emit a resume.sh file in the same folder as the script that can be ran on startup to recover bluetooth + WIFI in case the device died during sleep. Comment those lines out if turning off bluetooth or wifi is not desirable, but it may save a bit on battery life. Also the system has an issue with clock sync when wifi is off during sleep so I have also tried to address that.

Last but not least, I have found that sleep in the previous scripts can take a bit of time (approx. 3-4 seconds) due to trying to put many processes to sleep. Thus waking can be ignored if a previous sleep script was still in progress. This one will save the PID of the previous script and then will kill it if user initiated wake while a previous sleep script was still in progress. So in short: it wakes faster than before.

Please note that my setup is default Bookworm image without a login prompt or sudo password so this most likely won’t work with a login prompt or sudo password. Please look at @dotsony’s work for reference on how to address that.

#!/bin/bash
# Run below command in terminal first to set things up
# sudo apt install cpulimit sysstat wlr-randr procps

# Properly use filename for exclusion instead of predefined
BASE_NAME=$(basename "$0" | awk -F'.' '{print $1}')

# Base directory for script (can be anywhere)
BASE_DIR=$(dirname "$0")

# Add any process that needs to run in background during "sleep"
EXCLUDED_PROCS="systemd|pipewire|wireplumber|wayfire|labwc|cpulimit|$BASE_NAME"

# Automatically lock to min and max reported CPU clock on sleep
CPU_MAX_FREQ=$(cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq)
CPU_MIN_FREQ=$(cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq)

# File names can be changed here but they should be all default
# Please add absolute path to resume.sh on startup to recover network functions
PID_FILE=$BASE_DIR/sleep.pid
WAKE_FILE=$BASE_DIR/resume.sh
LOG_FILE=$BASE_DIR/log.txt

# Create wakefile for resume from sleep/bootup
CreateWakefile ()
{
  echo '#!/bin/bash' | tee $WAKE_FILE
  
  # Turn on bluetooth
  echo 'bluetoothctl power on' | tee -a $WAKE_FILE
  
  # Turn on wifi
  echo 'nmcli radio all on' | tee -a $WAKE_FILE
  
  # Turn on time sync (also will retrigger time sync when network is connected)
  echo "sudo timedatectl set-ntp on" | tee -a $WAKE_FILE
  
  # Cleanup once called
  echo "rm -rf $WAKE_FILE" | tee -a $WAKE_FILE
  echo "rm -rf $PID_FILE" | tee -a $WAKE_FILE
  
  chmod +x $WAKE_FILE
}

# Set CPU clock
SetCPU ()
{
  echo $1 | sudo tee /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
  echo $1 | sudo tee /sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq
}

Log ()
{
  echo "$1: $2" | tee -a $LOG_FILE
}

# Check if wake file exists
if [ ! -f $WAKE_FILE ];

# Enter sleep if wake file does not exist
then
  # Write PID file
  echo "$$" | tee $PID_FILE
  
  # Make wakefile
  CreateWakefile
  
  # Turn off display
  wlr-randr --output DSI-1 --off
  
  # Turn off bluetooth
  bluetoothctl power off
  
  # Turn off wifi
  nmcli radio all off
  
  # Disable time sync (will re-enable on wake/boot)
  sudo timedatectl set-ntp off
  
  # Stop all current running processes
  ps -A | egrep -v $EXCLUDED_PROCS | awk '{print $1}' | while read pid
  do
    # will not stop system processes
    kill -STOP $pid
  done
  
  # Limit performance of existing processes
  nohup bash -c '
  while true; do 
    for pid in $(pidstat 1 1 | grep "^Average:" | awk "BEGIN {FS=\"[[:space:]]+\"} \$8 > 5 {print \$3}"); do 
      cpulimit -z -b -l 1 --pid=$pid; 
    done; 
    exec -a qsleep_bg sleep 60; 
  done' > /dev/null 2>&1 &
  disown
  
  # Reduce CPU to min freq
  SetCPU $CPU_MIN_FREQ
  
# Otherwise wake up using wake file
else
  # Stop previous script
  kill -9 $(<"$PID_FILE")
  
  # Log current frequencies & voltage for debug purposes
  rm -rf $LOG_FILE
  Log "CPU" "$(echo "scale=2; $(vcgencmd measure_clock arm | awk -F'=' '{print $2}') / 1000000" | bc -l)MHz"
  Log "GPU" "$(echo "scale=2; $(vcgencmd measure_clock core | awk -F'=' '{print $2}') / 1000000" | bc -l)MHz"
  Log "Voltage" "$(vcgencmd measure_volts core | awk -F'=' '{print $2}')"
  
  # Wake CPU to max freq
  SetCPU $CPU_MAX_FREQ
  
  # Clean up previous scripts
  killall pidstat
  killall cpulimit
  pkill -f qsleep_bg
  
  # Launch wake file
  . $WAKE_FILE &
  
  # Resume all stopped processes
  kill -CONT -1
  
  # Turn on display and apply rotation (needed by Wayfire)
  wlr-randr --output DSI-1 --on --transform 270
  
fi

exit 0

I’m using wayfire, so to set this up on wayfire, add this extra line to wayfire.ini:

[autostart]
resume=/path/to/resume.sh

For labwc, I’d guess the script can also be added to /etc/rc.local or some other script location on boot.

In the same folder as the script, a new log.txt file will also be emitted on every wake that has this content basically:

CPU: 200.00MHz
GPU: 100.00MHz
Voltage: 0.8950V

The clocks above can be some really big value if wake was initiated while another sleep script was in progress. So this was basically how I caught that situation. I left this in for debugging purposes since different setups can behave differently.

Bonus: here’s how to reduce GPU and CPU clocks on idle. Open up /boot/firmware/config.txt and add the following:

over_voltage_min=-5
arm_freq_min=200
gpu_freq_min=100

I have found these values to be generally stable with my CM4. If you experience instability, either remove or raise over_voltage_min. These lines can be added in conjunction with over_voltage, arm_freq and gpu_freq that overclock the GPU. Those will change the min/max clocks for CPU and GPU. Personally I run with a mild overclock to 1.6GHz CPU and have found no issues at all.

Even with the extreme underclock and slight undervolt, I have found there to be not much benefit to battery life (roughly 8-10 hours of use with/without this script to sleep) so this may or may not be needed. But still, it at least reduces temperature during idle, too.

2 Likes

With some shame, I have to admit i don’t know where to place the qsleep.sh file. I cant find the wayfire.

In post 31 it is said " To use with wayfire, edit ~/.config/wayfire.ini" but I can’t find the .config folder in my home directory. am I supposed to make it?

I’m using Rex’s OS image, gdm3 and P
Wayland (Plasma). I also modified the ~/etc/gdm3/daemon.conf to enable automatic login.

nevermind. i don’t know why I didn’t see the folder the first times I listed them even with -a. can’t believe it.

Sorry for the dumb post.

you can add to your ~/.bashrc next line:

alias ls="ls -lah --group-directories-first"

and next time you will call ls you directories always will be on the top so you won’t miss them

1 Like

Been trying to make it work on KDE-Plasma (Wayland), but I’ve been unsuccessful quite probably because my knowledge in Linux is very limited.

The first issue I found, is that I don’t know where the file with the “Power Button” behaviour is, so I can’t add the [command] part men tioned on post 31.
After some research, I found that I can just map a ShellScript to a key combination through the Add Commnad button in the Settings → Shortcuts option in KDE.

I did it and the script executed successfully… sort of. I lost control of the keyboard and the screen stays on. I decided to tackle the second issue because the script has a very specific line to turn off the screen.
After some investigation, I found out that wlr-randr does not work on KDE-Plasma and that KDE uses the a compositor named KWin that it’s still not compatible with wlroots (and I;m almost copypasting text here because the way Wayland+Plasma works is still out of my knowledgebase. I am still struggling to understand what a composirto is)
Using Multiple Monitors with Different Resolutions on Wayland Linux | by Wainaina Gichuhi | Medium

wlr-randr(1) — wlr-randr — Debian testing — Debian Manpages

How to manage Displays (Screens) in Wayland KDE Plasma? wlr-randr gives error : r/kde

The third link mentions I can use kscreen-doctor so I tried. Theoretically, it should turn off my screen with kscreen-doctor --output.DSI-1.disable and it should turn it on with kscreen-doctor --output.DSI-1.enable but the first one didn’t seem to work, so the later didn’t do a thing. As a matter of fact, if I execute kscreen-doctor -o, my DSI-1 was always shown as enabled even after calling the disable option.

I tried kscreen-doctor --dpms off and it turns off the screen, but as soon as I touch anything, it turns on.

This is pretty much where I hit the wall because I can use x11 Plasma and I can turn off and on the screen successfully with xrandr --output DSI-1 --off and xrandr --output DSI-1 --auto --rotate righ respectively, still I am unable make the keyboard work while the uConsole is in qsleep mode.
All keyboard inputs are ignored, so I assumed I could add the keyboard daemon/proccess (not sure what it is) in the EXCLUDED_PROCS section, but I don’t know what is it called. I’m assuming that If I can add it, I can enter and exit qsleep mode with the keyboard shortcut.

So yeah. Sorry for the wall of text but I wanted to document as best as I could my process in order to ask for some help. I lean towards Wayland+Plasma, but if I have to use X11+Plasma, that’s ok.
I don’t want to use gnome-based yet, I like plasma and I’m also a believer in the “use the OS the way you want” :slight_smile:

TL;DR

  • How can I turn off and on the screen in KDE-Plasma (Wayland)
  • How can I add the keyboard to the excluded procecess in the shell script so that I can map qsleep.sh to a key shortcut?