Запуск скрипта при выключении linux

Запуск скрипта при выключении компьютера

Задача: Сделать автоматический запуск нашего скрипта перед перезагрузкой и выключением компьютера.

Для примера создадим скрипт выполняющий обновление системы перед выключением.

Для управления загрузкой и работой сервисов в kubuntu начиная с версии 15.04 используется systemd.

Systemd хранит свои юниты в папке /etc/systemd/system/ туда мы и положим свой новый юнит.

Создаем файл сервиса с именем poweroff_dist_upgrade.service

sudo nano /etc/systemd/system/poweroff_dist_upgrade.service

добавляем в него содержимое

[Unit]
Description=Dist Upgrade before restart and power off
After=systemd-user-sessions.service

[Install]
WantedBy=multi-user.target reboot.target poweroff.target

Unit — общая информация.
Description — описание сервиса.
After — задаёт порядок загрузки. В этом случае после старта сессии пользователя.

Service — содержит информацию о службе
ExecStop — Какой скрипт будет выполнен
Type — Типы службы. oneshot для скриптов, которые выполняют одно задание и завершаются

Install — когда юнит должен быть активирован.

multi-user.target или runlevel3.target соответствует runlevel=3 «Многопользовательский режим без графики»

Назначаем права на запуск
sudo chmod +x /etc/systemd/system/poweroff_dist_upgrade.service

Создаем сам скрипт который будет выполняться
sudo nano /usr/local/bin/distupgrade.sh

#!/bin/bash
apt update && apt dist-upgrade -f -y && apt autoremove -y
echo ‘Обновление ‘$(date ‘+%m.%d %H:%M’) >> /var/log/dist-upgrade_before_power_off.log
exit 0

Делаем скрипты выполняемым
sudo chmod +x /usr/local/bin/distupgrade.sh

Перезагружаем systemd для поиска новых или измененных юнитов:
sudo systemctl daemon-reload

Включаем наш новый юнит
sudo systemctl enable poweroff_dist_upgrade.service

Всё, теперь после этого при выключении и перезагрузки компьютера будет автоматически запускаться наш скрипт /usr/local/bin/distupgrade.sh

Для отключения используем
sudo systemctl disable poweroff_dist_upgrade.service

Источник

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

При разработке под linux возникают задачи создания интерактивных скриптов, выполняемых при включении или завершении работы системы. В system V это делалось легко, но с systemd вносит коррективы. Зато оно умеет свои таймеры.

Читайте также:  Linux and uefi secure boot

Зачем нужны target

Часто пишут, что target служат аналогом runlevel в system V -init. В корне не согласен. Их больше и можно разделять пакеты по группам и, к примеру, запускать одной командой группу сервисов, выполнять дополнительные действия. Кроме того, у них нет иерархии, только зависимости.

Пример 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 запускает процессу в одном таргете параллельно. Есть зависимости, которые позволяют определить последовательность запуска скриптов.

  1. Система стартует
  2. Запускается сервис settings_restore.service.Он проверяет наличие файла settings.txt в разделе с данными. Если его нет, то на его место кладётся эталонный файл.Далее происходит восстановление настроек системы:
    • пароля администратора
    • hostname,
    • часового пояс
    • локаль
    • Определение, весь ли носитель используется. По умолчанию размер образа небольшой — для удобства копирования и записи на носитель. При старте проверяется — есть ли ещё неиспользуемое место. Если есть — диск переразбивается.
    • Генерация machine-id из MAC-адреса. Это важно для получения одного и того же адреса по DHCP
    • Настройки сети
    • Ограничивается размер логов
    • Подготавливается к работа внешний диск(если включена соответствующая опция и диск новый)
  3. Запускаться postgresq
  4. запускается сервис restore. Он нужен для подготовки самого zabbix и его базы данных:
    • Проверяется, есть ли уже база данных zabbix. Если нет — создается из инициализирующих дампов(идут в поставке zabbix)
    • создается список часовых поясов (нужно для их отображения в web-интерфейсе)
    • Находится текущий IP, он выводится в issue (приглашение для входа в консоли)
  5. Меняется приглашение — появляется фраза Ready to work
  6. Прошивка готова к работе
Читайте также:  Linux найти все символьные ссылки

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

[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
  • Внутри этого скрипта я выполняю нужные действия. Можно в таргет добавить много скриптов, для гибкости и удобства:
#!/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 с параметрами. Можно только service.

Читайте также:  Linux nvidia control panel

Но я использую 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

Поэтому их можно заменить на свои скрипты:
reboot

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

Источник

Оцените статью
Adblock
detector