
Въведение
При разработка на Linux възникват задачи за създаване на интерактивни скриптове, които се изпълняват при включване или изключване на системата. В System V беше лесно, но с Systemd нещата се променят. Сега то съ支持ва свои таймери.
За какво са нужни target
Често пишат, че target действат като аналог на runlevel в System 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 може да се направи по различни начини, аз използвах за целта опция на зареждача.
Крайното стартиране изглежда така:
- Стартира зареждача
- Зареждачът започва стартирането на фърмуера, предавайки параметър final.target
- Systemd започва старта на системата. Последователно преминава към installer.target или work.target от basic.target през зависимостите им (например, multi-user.target). Последните водят системата до работа в необходимия режим.
Подготовка на фърмуера за стартиране
При създаването на фърмуери винаги възниква задача за възстановяване на състоянието на системата при стартиране и запазването му при изключване. Под състояние се имат предвид конфигурационни файлове, дампове на бази данни, настройки на интерфейси и т.н.
Systemd стартира процеси в един target паралелно. Има зависимости, които определят последователността на стартиране на скриптовете.
Как това работи в моя проект ( )
- Системата стартира
- Стартира услугата settings_restore.service. Тя проверява наличието на файла settings.txt в данните. Ако го няма, на негово място се поставя еталонен файл. Следва възстановяване на системните настройки:
- административна парола
- hostname,
- часова зона
- локализация
- Определяне на това, дали цялото устройство се използва. По подразбиране размерът на образа е малък — за улеснение на копирането и записа на устройството. При стартиране се проверява дали има неизползвано място. Ако има — дискът се преформатира.
- Генериране на machine-id от MAC адреса. Това е важно за получаване на един и същ адрес по DHCP
- Настройки на мрежата
- Ограничаване на размера на логовете
- Подготвя се външен диск (ако е активирана съответната опция и дискът е нов)
- Стартира се postgresq
- стартира услугата restore. Тя е нужна за подготовка на самия zabbix и неговата база данни:
- Проверката дали базата данни zabbix вече съществува. Ако не — тя се създава от инициализиращите дампове (поставят се с доставка на zabbix)
- създава се списък с часовите зони (нужно за показването им в уеб интерфейса)
- Намира се текущият IP, той се показва в issue (приглашение за влизане в конзолата)
- Променя се поканата — появява се фразата Ready to work
- Прошивката е готова за работа
Важни са файловете на услугите, именно те определят последователността на стартирането им.
[Unit]
Description=възстановяване на системните настройки
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=изпълни logrotate
[Service]
ExecStart=logrotate \/etc\/logrotate.conf
TimeoutSec=300Всичко е просто — описание на командата за стартиране.
Вторият файл logrotateTimer.timer — е той, който задава работата на таймерите:
[Unit]
Description=Изпълни 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_shutdown.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. Не могат да се извикат таргети с параметри. Могат да се извикат само услуги.
Но използвам таргет, за да имам гъвкавост в работата и гарантиран ред на изпълнение на действията.
Все пак, най-интересното дойде след това. Машината трябва да бъде изключена/перезаредена. И тук има 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Затова могат да бъдат заменени със собствените скриптове:
reboot
#!/bin/sh
touch /tmp/reboot
sudo systemctl isolate my_shutdown.target
fiИзточник: habr.com
