Solved sleeping laptop without a gui

I am using FreeBSD 12.1 a lot more these days and I find the console, sans gui, to be a more and more comfortable place to work from. When I want to multitask, I tend to startx with twm. My question is, how can I get the system to sleep/hibernate automatically when the battery is low, if I'm not using a desktop environment? I keep forgetting to shut down my laptop after using it and setting it aside, only to come back to it and it having precipitously shut down, requiring a restart after attaching power. It's a Thinkpad T430 and ACPI works (hit the power button for a sec and it sends a shutdown signal).

Ideally, this would be a background task that monitors the battery and when it hits 10% sleeps or hibernates or warns then sleeps... It would work even when only logged into the console, but if it worked in twm, that'd be ok, too.
 
Yes, you can configure devd(8) to suspend when the battery gets very low energy.

1. Check what battery type you have with: sysctl dev.battery
2. For a control method battery, add this to your /etc/devd.conf file:
Code:
# test battery events
notify 10 {
    match "system" "ACPI";
    match "subsystem" "CMBAT";
    action "apm -z";
};

For a smart battery, it should be:
Code:
notify 10 {
    match "system" "ACPI";
    match "subsystem" "SMBAT";
    action "apm -z";
};

You can choose another "action", of course, e.g. start a shell script..
3. Restart devd(), or reboot.

On my laptop, the event gets triggered once the battery reaches 5%. It's not possible to change this value manually, because the CMBAT driver doesn't implement the optional _BTP acpi method..
 
The zzz(8) utility checks for ACPI or APM support and then suspends the system appropriately.
Maybe you're victim to try IRST hibernation and report success? Very comfortable way to save battery power. No dump yet to IRST partition, though. Besides that, it works fine for me.
 
Not sure whether my solution from above works, because a CMBAT notify event might also be triggered once the battery changes from "charging" to "not charging". We don't want to suspend in those cases..
 
Elazar well, I was about to write that it didn't go to sleep...

apm
APM version: 1.2
APM Management: Disabled
AC Line status: off-line
Battery Status: low
Remaining battery life: 4%
Remaining battery time: 0:15:00
Number of batteries: 1
Battery 0:
Battery Status: low
Remaining battery life: 4%
Remaining battery time: 0:15:00
 
OK. I used DutchDaemon's approach, but instead of shutting down, I used apm -z to put the system to sleep. The script is run as root, in the background and checks battery level every couple of minutes. If it hits 10%, it gets nervous and checks again 2 minutes later, if it's still losing battery, it emails root and checks again. If it's still going down, it puts the system to sleep. I'm forwarding root's email in aliases and biff lets me know it's got mail. So, biff goes reverse video and I get warned. If I'm not paying attention, it goes to sleep and I can wake it, if I really need to or whatever.

Works great!

Just gotta remember to run that script. If I wanted to run it as a regular user would I be able to let apm run as my user or what?
 
Don't know for shure but it should be sufficient for the user to call zzz(8) to be in group operator. Maybe even any user can suspend the system? Didn't try.
 
Here is what I wound up doing. Let me know if it looks reasonable, or if I've broken faith with sysadmins everywhere:

Do all of this as root

sudo -i

Create a script (thanks to DutchDaemon) to check the battery level, warn the user, and sleep before battery seppuku

vi /usr/sbin/sleepd

Bash:
#!/bin/sh

# who to warn
email=root
# battery level critical %
critlevel=10
# seconds to recheck and eventually act when battery is low
sleeps=120
# seconds to pause between script runs
loop=300

while true

do

# battery %
battery1=$( /sbin/sysctl -n hw.acpi.battery.life )
# AC plugged in?
acpower1=$( /sbin/sysctl -n hw.acpi.acline )

if [ ${battery1} -le ${critlevel} ] && [ ${acpower1} = "0" ]
 then
  /bin/sleep ${sleeps}

  battery2=$( /sbin/sysctl -n hw.acpi.battery.life  )
  acpower2=$( /sbin/sysctl -n hw.acpi.acline )

   if [ ${battery2} -lt ${battery1} ] && [ ${acpower2} = "0" ]
    then
     echo "Insert power plug or kill PID $$ to prevent automatic shutdown." | /usr/bin/mail -s "Battery ${battery2} % - Will shutdown in ${sleeps} seconds" ${email}
     /bin/sleep ${sleeps}

      acpower3=$( /sbin/sysctl -n hw.acpi.acline )

      if [ ${acpower3} = "0" ]
       then /usr/sbin/zzz
      fi
   fi
fi
/bin/sleep ${loop}
done

Change permissions on the script

chmod 555 /usr/sbin/sleepd

Create an rc script to run my sleep daemon

vi /etc/rc.d/sleepd

Bash:
#!/bin/sh
#
# PROVIDE: sleepd
# REQUIRE:
# KEYWORD:

. /etc/rc.subr

name="sleepd"
rcvar="sleepd_enable"

sleepd_command="/usr/sbin/sleepd"
pidfile="/var/run/${name}.pid"
command="/usr/sbin/daemon"
command_args="-P ${pidfile} -r -f ${sleepd_command}"

load_rc_config $name
: ${sleepd_enable:=no}

run_rc_command "$1"

Change permissions on the script

/etc/rc.d/sleepd

Enable the daemon in /etc/rc.conf

sysrc sleepd_enable="YES"

Start the daemon

service sleepd start

Check the status

service sleepd status

Stop the daemon

service sleepd stop
 
Oh, yeah, and if you don't want to have to keep logging in as root to check the mail...


Add an Alias for root that points to your account

sudo vi /etc/aliases

Bash:
# ...
# root:    me@my.domain
root: yourusername

Notify the system that aliases has changed
sudo newaliases
 
  • Thanks
Reactions: 0mp
IMHO it looks reasonable, didn't try it though. This belongs into powerd(8), or maybe even into some UPSd. Please make a link to here in Useful Scripts. My laptop has two batteries... I think that's not very unusual.

Done. I made into a zip file and included a simple script to install the service. The post is here.

Oh, and it works! I'm sure you can confuse it by plugging it back in when it's critical and then continue to use it. But I think the worst thing it would do, would be to go back to sleep after you plug it in and unsleep it, at least until the battery gets back to 10% (I'd just stop the service temporarily..). But it's a pretty small price to pay for the peace of mind you gain overall :)
 
Back
Top