В 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. Ещё можно изменить значения AccuracySec и OnActiveSec.

#/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 нужен чтобы таймер успел выполнится, когда выход из режима сна вызван самим таймером.

Комментарии (7)


  1. Revertis
    15.11.2021 18:00
    +1

    до переходя в ждущий сна

    Может хоть прочитаете сами что понаписали, а? Там куча ошибок, даже нет желания слать всё в личку (как я обычно делаю).


    1. ExIngus Автор
      15.11.2021 18:34

      Спасибо, проверю


  1. StraNNicK
    15.11.2021 19:02
    +1

    Хорошая статья, полезная. У меня как раз ноут не постоянный рабочий инструмент, бывает, что оставляю его в гибернации и он разряжается.

    Не задумывался, что это можно поправить с помощью SystemD.
    Буду пробовать, спасибо!


    1. ExIngus Автор
      15.11.2021 19:07
      +1

      Если гебернация устраивает, то попробуйте suspend-then-hibernate.
      Только нужно задать параметр HibernateDelaySec в /etc/systemd/sleep.conf или /etc/systemd/sleep.conf.d/.

      #/etc/systemd/sleep.conf                                                                                                                          848/848               100%
      
      [Sleep]
      HibernateDelaySec=4000min
      


    1. RaymanOne
      15.11.2021 20:20

      Скорее всего в сне оставляете? При использовании гибернации подача электроэнергии полностью прекращается.


      1. StraNNicK
        15.11.2021 20:52

        вы правы. Когда не забываю в гибернацию ткнуть, то и проблемы нет.
        А вот если тупо захлопнул и в рюкзак кинул — то да, аккум высаживается.


        1. RaymanOne
          15.11.2021 21:02

          Можно на захлапывание гибернацию поставить тоже, и проблема думаю у вас уйдет.