Выключение ноутбука по таймеру в ждущем режиме

5fb17cfef59adb063c971b6591df415b

В systemd есть режим suspend-then-hibernate который выполняет гибернацию (режим сна) через указанное время простоя в ждущем режиме.
Настроим подобный режим, только с автоматическим выключение ноутбука при длительном нахождении в ждущем режиме. Это может быть полезно если нет желания использовать гибернацию (большой объём RAM, SSD или медленный HDD), но хочется обезопасить аккумулятор от полного разряда.

Режимы сна

SystemD поддерживает следующие режимы сна:

  • suspend »Ждущий режим.» Приостанавливает работу системы, данные продолжают хранится в оперативной памяти. Активирует цель suspend.target.

  • hibernate »Спящий режим.» Приостанавливает работу системы, сохраняет оперативную память на жёсткий диск. Активирует цель hibernate.target. Выключает питания.

  • hybrid-sleep »Гибридный режим.» Приостанавливает работу системы, сохраняет оперативную память на жёсткий диск, но не выключает питания. Активирует цель hybrid-sleep.target.

  • suspend-then-hibernate Переход в ждущий режим (suspend) и последующий переход в спящий режим (hibernate) через указанное время. Активирует цель suspend-then-hibernate.target.

Детально параметры сна настраиваются в файле sleep.conf.

Обоаботка переход в ждущий режим

Создадим сервис /etc/systemd/system/sleep-auto-poweroff.service со следующим содержимым:

[Unit]
Before=sleep.target
StopWhenUnneeded=yes
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c "echo Start pending-poweroff.timer"
ExecStop=/bin/bash -c "echo Stop pending-poweroff.timer"
[Install]
WantedBy=sleep.targe

Этот сервис зависит от цели sleep.target и будет запускаться при переходе в ждущий и спящий режимы.
Здесь важно пояснить два параметра:
RemainAfterExit=yes Сервис будет считаться активным пока не будет явно остановлен, даже если команда в ExecStart завершится.
StopWhenUnneeded=yes Если служба активна, то она будет остановлена, когда все зависящие от неё юниты остановлены. В данном случае когда остановлен sleep.target.
За счёт этого, команда в ExecStart будет выполнена до переходя в ждущий сна, а в ExecStop после выхода из этого режима.
Перед переходом в ждущий будем запускать таймер, а после возобновления работы — останавливать.

Также для обработки перехода в ждущий режим можно использовать пару сервисов:

#/etc/systemd/system/suspend.service
[Unit]
Before=sleep.target
[Service]
Type=oneshot
ExecStart=-before_sleep.sh
[Install]
WantedBy=sleep.target

#/etc/systemd/system/resume.service
[Unit]
After=sleep.target
[Service]
Type=oneshot
ExecStart=-after_sleep.sh
[Install]
WantedBy=sleep.target

Таймер обратного отсчёта

Создаём таймер /etc/systemd/system/pending-poweroff.timer:

[Timer]
AccuracySec=1h
OnActiveSec=72h
WakeSystem=true
RemainAfterElapse=false
Unit=systemd-poweroff.service
[Install]
WantedBy=timers.target

Разберём важные параметры:
AccuracySec — допустимое отклонение таймера, точность, по умолчанию 1 минута.
OnActiveSec — время от активации таймера до срабатывания.
WakeSystem — таймер будет работать и в ждущем режиме, при срабатывании компьютер возобновит работу.
RemainAfterElapse=false — таймер будет деактивирован после срабатывания
Unit юнит который будет запущен при срабатывании таймера


Таймер запускается вручную командой systemctl start pending-poweroff.timer, ждёт время указанное в OnActiveSec (72 часа) и выполняет юнит systemd-poweroff.service. Остановить таймер можно командой systemctl stop pending-poweroff.timer.

Для отладки удобно создать сервис, который пишет сообщение в журнал, и указать его в параметре Unit таймера pending-poweroff.timer.

#/etc/systemd/system/nop.service
[Unit]
Description=Test
[Service]
Type=oneshot
ExecStart=-/usr/bin/echo "Hello! The system is shutdowning."

#/etc/systemd/system/pending-poweroff.timer
...
[Timer]
Unit=nop.service
...

Готовый вариант

 #/etc/systemd/system/sleep-auto-poweroff.service
[Unit]
Description=Automatic shutdown after waiting
Before=sleep.target
StopWhenUnneeded=yes
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c "systemctl start pending-poweroff.timer && echo Start pending-poweroff.timer"
ExecStop=/bin/bash -c "sleep 3 && systemctl stop pending-poweroff.timer && echo Stop pending-poweroff.timer"
[Install]
#suspend.target, hibernate.target and hybrid-sleep.target and because sleep.target
WantedBy=sleep.target

#/etc/systemd/system/pending-poweroff.timer
[Unit]
Description=Poweroff the system after this timer is stoped.
[Timer]
AccuracySec=1h
OnActiveSec=72h
WakeSystem=true
RemainAfterElapse=false
Unit=systemd-poweroff.service
[Install]
WantedBy=timers.target

Вызов sleep в ExecStop нужен чтобы таймер успел выполнится, когда выход из режима сна вызван самим таймером.

© Habrahabr.ru