After my Raspberry PI died, I decided not to get a new one immediately. Instead, I turned the older laptop into a FreeBSD server. I use this server for Git, backup via ZFS snapshots, running Debian/RHEL VM using bhyve, side project web server and jails with ZFS. It works perfectly, but during monsoon season, the electric supply at home goes for hours, and my battery backup UPS only works for 15 minutes. Hence, when my FreeBSD laptop starts to run out of battery juicy, I want to shut it down automatically to avoid sudden filesystem and other corruption issues.

Getting the battery status of your FreeBSD notebook from CLI

The hw.acpi.acline variable gives us AC line state. Value 1 means online, 0 means the system is on battery power. The hw.acpi.battery.life tells reaming battery power for the laptop. We can use the sysctl command as follow to obtain those values:
$ sysctl hw.acpi.acline hw.acpi.battery.life
To hide variable names, pass the -n option:
$ sysctl -n hw.acpi.acline hw.acpi.battery.life

How to get alert before shutdown

For email alerts, you need SMTPD or at least some sort of email routing via the ISP email server. In other words, you need either Sendmail or Postfix on your FreeBSD machine. I utterly use AWS SES on FreeBSD with Postfix for all my side projects. I used the same on my home server. To get alerts on my mobile, I used pushover API. See how to push/send message to iOS and Android from Linux CLI for more info.

Shell script to shutdown FreeBSD laptop when running out of battery power

#!/bin/sh
# Purpose: Shell script to shutdown FreeBSD laptop when running out of battery power at a certain percent
# Modified from: https://forums.freebsd.org/threads/automatic-shutdown-on-a-specific-battery-percent.14074/post-82134
#  + Added pushover ios support by nixCraft
#  + Must be run as root to shutdown the laptop
# --------------------------------------------------
 
# Who to warn
email="vivek@nixcraft.com"
# Battery level critical %
critlevel=10
# Seconds to recheck and eventually act when battery is low
sleeps=60
# Seconds to pause between script runs
loop=180
 
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. -- $(hostname)" | /usr/bin/mail -s "Battery ${battery2} % - Will shutdown in ${sleeps} seconds" "${email}"
     # push notification to my iOS device
     # get API keys
     . /root/bin/push-to-mobile
     # send it
     push_to_mobile "$0" "Insert power plug or kill PID $$ to prevent automatic shutdown. -- $(hostname)"
 
     /bin/sleep ${sleeps}
 
      acpower3=$( /sbin/sysctl -n hw.acpi.acline )
 
      if [ ${acpower3} = "0" ]
       then /sbin/shutdown -p now
      fi
   fi
fi
 
/bin/sleep ${loop}
 
done

How to run shell script in background

I am using the daemon utility on FreeBSD. It detaches itself from the controlling terminal and executes the program specified by its arguments in the background. It is like nohup command line-utility, which allows running a command/process or shell script running in the background after you log out from a shell. I added the following job to cron by running the sudo crontab -e command:

# Shutdown FreeBSD server when running out of laptop battery 
@reboot         /usr/sbin/daemon /root/bin/powerdown-when-battry-low

The result

I am pretty satisfied with my solution. It saved a sudden crash at least twice. I get an email alert as follows:

Also, mobile alert:

Eventually, the laptop will shut down if I don’t correct the situation. I can kill PID, too, from my alert notification. A better solution would be to get a longer battery laptop or UPS, but I am not investing another penny into something that occurs 4 or 5 times during the stormy season. Hence, I crafted this solution. There might be a better solution if you use a desktop-like Gnome or KDE as they got in built power management. But this a server, so no point using a desktop on it.

🐧 Get the latest tutorials on SysAdmin, Linux/Unix, Open Source, and DevOps topics via:

Category List of Unix and Linux commands
Disk space analyzers df ncdu pydf
File Management cat cp mkdir tree
Firewall Alpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
Network Utilities NetHogs dig host ip nmap
OpenVPN CentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
Package Manager apk apt
Processes Management bg chroot cron disown fg jobs killall kill pidof pstree pwdx time
Searching grep whereis which
User Information groups id lastcomm last lid/libuser-lid logname members users whoami who w
WireGuard VPN Alpine CentOS 8 Debian 10 Firewall Ubuntu 20.04
0 comments… add one

Leave a Reply

Your email address will not be published.

Use HTML <pre>...</pre> for code samples. Still have questions? Post it on our forum