Systemd, інтерактивні скрипти та таймери

Systemd, інтерактивні скрипти та таймери

Запровадження

Під час розробки під linux виникають завдання створення інтерактивних скриптів, виконуваних під час увімкнення чи завершення роботи системи. У system V це робилося легко, але з systemd вносить корективи. Натомість воно вміє свої таймери.

Навіщо потрібні target

Часто пишуть, що target є аналогом runlevel в системі V -init. Докорінно не згоден. Їх більше і можна розділяти пакети по групам і, наприклад, запускати однією командою групу сервісів, виконувати додаткові дії. Крім того, вони не мають ієрархії, тільки залежності.

Приклад target при включенні (огляд можливості) із запуском інтерактивного скрипту

Опис самого target:

cat installer.target
[Unit]
Description=My installer
Requires=multi-user.target 
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target 
AllowIsolate=yes
Wants=installer.service

Цей target запуститься, коли буде запущено multi-user.target і викличе installer.service. У цьому таких сервісів може бути кілька.

cat installer.service
[Unit]
# описание
Description=installer interactive dialog

[Service]
# Запустить один раз, когда остальное будет запущенно
Type=idle
# Команда запуска - вызов скрипта
ExecStart=/usr/bin/installer.sh
# Интерактивное взаимодействие с пользователем через tty3
StandardInput=tty
TTYPath=/dev/tty3
TTYReset=yes
TTYVHangup=yes

[Install]
WantedBy=installer.target

І нарешті, приклад виконуваного скрипту:

#!/bin/bash
# Переходим в tty3
chvt 3
echo "Install, y/n ?"
read user_answer

Найголовніше – вибрати final.target – target, до якого система має прийти при запуску. У процесі запуску systemd пройде залежностями і запустить все необхідне.
Вибрати final.target можна різними способами, я використав для цього опцію завантажувача.

Підсумковий запуск має такий вигляд:

  1. Стартує завантажувач
  2. Завантажувач починає запуск прошивки, передаючи параметр final.target
  3. Systemd починає запуск системи. Послідовно йде до installer.target або work.target від basic.target через їхню залежність (наприклад, multi-user.target). Останні і приводять систему до роботи у потрібному режимі

Підготовка прошивки до запуску

При створенні прошивок завжди виникає завдання відновлення стану системи при старті та його збереження під час вимкнення. Під станом маються на увазі конфігураційні файли, дампи бази даних, налаштування інтерфейсів тощо.

Systemd запускає процесу в одному таргеті паралельно. Існують залежності, які дозволяють визначити послідовність запуску скриптів.

Як це працює у мене в проекті ( https://habr.com/ru/post/477008/ https://github.com/skif-web/monitor)

  1. Система стартує
  2. Запускається сервіс settings_restore.service.Він перевіряє наявність файлу settings.txt у розділі даних. Якщо його немає, то на його місце кладеться еталонний файл. Далі відбувається відновлення налаштувань системи:
    • пароля адміністратора
    • ім'я хоста,
    • часовий пояс
    • локаль
    • Визначення, чи використовується весь носій. За замовчуванням розмір образу невеликий – для зручності копіювання та запису на носій. При старті перевіряється — чи є місце, яке ще не використовується. Якщо є диск перерозбивається.
    • Генерація machine-id з MAC-адреси. Це важливо для отримання однієї і тієї ж адреси по DHCP
    • налаштування мережі
    • Обмежується розмір балок
    • Підготовляється до роботи зовнішній диск (якщо включена відповідна опція та новий диск)
  3. Запускати postgresq
  4. запускається обслуговування restore. Він потрібен для підготовки самого zabbix та його бази даних:
    • Перевіряється, чи вже є база даних zabbix. Якщо ні - створюється з ініціалізують дампів (йдуть у поставці zabbix)
    • створюється список часових поясів (потрібно їх відображення в web-интерфейсе)
    • Знаходиться поточний IP, він виводиться в issue (запрошення для входу до консолі)
  5. Змінюється запрошення - з'являється фраза Ready to work
  6. Прошивка готова до роботи

Важливими є файли сервісів, саме вони виставляють послідовність їх запуску.

[Unit]
Description=restore system settings
Before=network.service prepare.service postgresql.service systemd-networkd.service systemd-resolved.service

[Service]
Type=oneshot
ExecStart=/usr/bin/settings_restore.sh

[Install]
WantedBy=multi-user.target

Як видно, я поставив залежності, щоб спочатку відпрацював мій скрипт, а тільки потім піднімалася мережа і стартувала СУБД.

І другий сервіс (підготовка zabbix)

#!/bin/sh
[Unit]
Description=monitor prepare system
After=postgresql.service settings_restore.service
Before=zabbix-server.service zabbix-agent.service

[Service]
Type=oneshot
ExecStart=/usr/bin/prepare.sh

[Install]
WantedBy=multi-user.target

Тут трохи складніше.Запуск так само в multi-user.target, але ПІСЛЯ запуску СУБД postgresql і мого setting_restore. Але перед запуском служб zabbix.

Сервіс з таймером для logrotate

Systemd може замінити CRON. Серйозно. А можна створити монотонний таймер, що викликається по таймууту від події.
Саме монотонний таймер, який рахує час від запуску машини, я й створив.
Для цього потрібно 2 файли
logrotateTimer.service - власне опис сервісу:

[Unit]
Description=run logrotate

[Service]
ExecStart=logrotate /etc/logrotate.conf
TimeoutSec=300

Все просто опис команди запуску.
Другий файл logrotateTimer.timer - ось він і задає роботу таймерів:

[Unit]
Description=Run logrotate

[Timer]
OnBootSec=15min
OnUnitActiveSec=15min

[Install]
WantedBy=timers.target

Що тут є:

  • опис таймера
  • Час першого запуску, починаючи від завантаження систем
  • період подальших запусків
  • Залежність від служби таймерів. Фактично, це рядок і робить таймер

Інтерактивний скрипт при вимкненні та свій таргет вимкнення

В іншій розробці мені довелося робити складніший варіант вимкнення машини - через власний таргет, щоб виконати безліч дій. Зазвичай рекомендується створити oneshot з опцією RemainAfterExit, але це не дає створити інтерактивний скрипт.

А справа в тому, що команди, що запускаються опцією ExecOnStop, виконуються поза TTY! Перевірити просто – вставте команду tty та збережіть її висновок.

Тому я реалізував вимкнення через свій таргет. На 100% правильність не претендую, але це працює!
Як це робилося (загалом):
Створив таргет my_shutdown.target, який ні від кого не залежав:
my_shutdown.target

[Unit]
Description=my shutdown
AllowIsolate=yes
Wants=my_shutdown.service 

При переході в цей таргет (через systemctl isolate my_shutdwn.target), він запускав сервіс my_shutdown.service, завдання якого просте - виконати скрипт my_shutdown.sh:

[Unit]
Description=MY shutdown

[Service]
Type=oneshot
ExecStart=/usr/bin/my_shutdown.sh
StandardInput=tty
TTYPath=/dev/tty3
TTYReset=yes
TTYVHangup=yes

WantedBy=my_shutdown.target

  • Усередині цього скрипту виконую потрібні дії. Можна в таргет додати багато скриптів, для гнучкості та зручності:

my_shutdown.sh

#!/bin/bash --login
if [ -f /tmp/reboot ];then
    command="systemctl reboot"
elif [ -f /tmp/shutdown ]; then
    command="systemctl poweroff"
fi
#Вот здесь нужные команды
#Например, cp /home/user/data.txt /storage/user/
    $command

Примітка. Використання файлів /tmp/reboot та /tmp/shutdown. Не можна викликати target із параметрами. Можна лише сервіс.

Але я використовую target, щоб мати гнучкість у роботі та гарантований порядок виконання дій.

Однак найцікавіше було потім. Машину треба вимкнути/перезавантажити. І тут є 2 варіанти:

  • Замінити команди reboot, shutdown та інші (вони все одно є симлінками на systemctl) на свій скрипт. Усередині скрипта – перехід у my_shutdown.target. А скрипти всередині таргета потім викликають безпосередньо systemctl, наприклад, systemctl reboot
  • Простіший, але мені не подобається варіант. У всіх інтерфейсах викликати не shutdown/reboot/інші, а безпосередньо викликати таргет systemctl isolate my_shutdown.target

Я вибрав перший варіант. У systemd reboot(як і poweroff) є симлінками на systemd.

ls -l /sbin/poweroff 
lrwxrwxrwx 1 root root 14 сен 30 18:23 /sbin/poweroff -> /bin/systemctl

Тому їх можна замінити на свої скрипти:
перезавантажувати

#!/bin/sh
    touch /tmp/reboot
    sudo systemctl isolate my_shutdown.target
fi

Джерело: habr.com

Додати коментар або відгук