Управляем systemd в Fedora

В качестве системы инициализации в Fedora применяется systemd, поэтому в этой статье мы подробно рассмотрим управление, настройку и создание собственных юнитов и таймеров.

Введение

Основная единица конфигурации systemd — юнит. Основные типы юнитов:

  • target — целевой юнит, описывает цели, по достижении которых могут срабатывать другие юниты;
  • service — используется для запуска системных служб;
  • timer — используется для запуска конкретных действий по заранее составленному расписанию;
  • socket — применяется для активации служб посредством обращения к Unix-сокетам;
  • device — позволяет запускать какое-либо действие или сервис при подключении или отключении устройств;
  • mount и automount — используется для (автоматического) монтирования файловых систем;
  • swap — применяется для работы с файлами или устройствами (например разделами) подкачки.

В Fedora все стандартные предустановленные юниты размещаются в каталоге /usr/lib/systemd/, который имеет следующие подкаталоги:

  • system — стандартные общесистемные юниты для общих целей, выполняющиеся при запуске системы или при наступлении определённых условий, прописанных в них;
  • user — запускаются при входе (или выходе) пользователя в систему от его имени;
  • system-shutdown — содержит скрипты, которые выполняются автоматически при завершении работы системы;
  • systemd-sleep — содержит скрипты, которые выполняются автоматически при уходе системы в спящий (hybernate) режим.

Создание обыкновенных юнитов

Начнём мы конечно же с создания собственных юнитов. Как уже сообщалось выше, общесистемные юниты общего назначения располагаются в каталоге /usr/lib/systemd/system/ в виде файлов $name.$suffix, где $suffix явно указывает его тип.

Сразу рассмотрим простой пример foobar.service:

[Unit]
Description=Sample standard unit
After=network.target

[Service]
Type=simple
Restart=always
RestartSec=30
ExecStartPre=/usr/bin/foobar prehook
ExecStart=/usr/bin/foobar
ExecStartPost=/usr/bin/foobar posthook

[Install]
WantedBy=multi-user.target

Теперь разберёмся с основными директивами:

  • Description — описание юнита;
  • After — используется для указания зависимостей, после которых данный юнит должен запускаться при активации;
  • Type — тип юнита. Возможны варианты:
    • simple — простой запуск указанного процесса или сценария. Такие процессы не должны создавать своих дочерних процессов (форков);
    • forking — то же, что и simple, но с возможностью запуска форков. Внутри юнита обязательно должна использоваться директива PIDFile, чтобы systemd мог знать PID родительского процесса и корректно проверять его состояние;
    • oneshot — юнит, который должен запустить процесс или сценарий и сразу завершить свою работу вместе с запущенным приложением;
    • dbus — используется для активации посредством получения определённого D-Bus события;
  • Restart — включает или отключает механизм автоматического перезапуска в случае падения запущенного приложения;
  • RestartSec — задаёт интервал времени, по истечении которого упавший процесс будет автоматически перезапущен (если это разрешено предыдущей директивой);
  • ExecStartPre — путь к бинарнику или сценарию, который будет запущен до непосредственного запуска главного приложения;
  • ExecStart — главный бинарник или сценарий юнита;
  • ExecStartPost — путь к бинарнику или сценарию, который будет запущен после запуска главного приложения;
  • WantedBy — задаёт цель, при активации которой юнит будет автоматически запускаться.

Полный список директив можно найти в официальной документации к systemd.

Создание юнита, который будет запускаться с правами указанного пользователя

По умолчанию все общесистемные юниты запускаются с правами суперпользователя (root), что может быть небезопасно, поэтому systemd предоставляет возможность тонкой настройки запуска с правами любого доступного в системе пользователя:

[Unit]
Description=Sample unit running from specified user
After=network.target

[Service]
Type=simple
Restart=always
RestartSec=30
User=nobody
Group=nobody
ExecStart=/usr/bin/foobar

[Install]
WantedBy=multi-user.target

В данном примере мы добавили директивы User и Group, задающие пользователя и группу, от имени которых будет запущен процесс.

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

Создание юнита, который будет выполняться при завершении работы системы

Если требуется выполнять какой-то процесс при завершении работы системы, мы должны создать unit-файл такого вида:

[Unit]
Description=Sample unit running on shutdown
Before=shutdown.target reboot.target halt.target

[Service]
Type=oneshot
ExecStart=/usr/bin/foobar

В данном примере будет запускаться приложение /usr/bin/foobar при каждом завершении работы системы (условие указано внутри директивы Before).

Следует обратить внимание на тип данного юнита — oneshot и отсутствие секции Install.

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

Данный тип ничем не отличается от стандартных юнитов, однако размещаются в каталоге /usr/lib/systemd/user/. При активации всегда выполняются с правами вошедшего в систему пользователя.

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

Также должны располагаться в /usr/lib/systemd/user/, но внутри директивы Before должно быть указано exit.target:

[Unit]
Description=Sample unit running on logoff
Before=exit.target

[Service]
Type=oneshot
ExecStart=/usr/bin/foobar

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

Создание таймеров

Имя файла таймера должно иметь суффикс timer, например foobar.timer. Таймер всегда является дополнением к service, т.к. при его срабатывании запускается service с тем же именем:

[Unit]
Description=Sample standard timer

[Timer]
OnUnitActiveSec=1w

[Install]
WantedBy=timers.target

Условия срабатывания прописываются в секции Timer. В нашем примере применяется директива OnUnitActiveSec, означающая, что данный таймер будет срабатывать автоматически каждую неделю (отсчёт начнётся в момент его активации). Это т.н. неточный таймер.

Наряду с неточными существуют ещё и точные таймеры, в которых применяется особая директива OnCalendar, где, в свою очередь, указывается время и дата его срабатывания (например по вторникам, в 16:20).

Редактирование и переопределение стандартных юнитов и таймеров

В случае если нужно отредактировать какой-то из уже установленных в системе юнитов или таймеров, ни в коем случае не следует непосредственно править файл внутри каталога /usr/lib/systemd/, т.к. при следующем обновлении системы все изменения в нём будут перезаписаны.

Вместо этого следует использовать штатную возможность systemd:

sudo systemctl edit --full foobar.service

Здесь foobar.service — имя нужного нам юнита/таймера, который мы будем редактировать. Systemd автоматически сделает копию файла в пользовательский каталог настроек /etc/systemd/system/, загрузит его в системном редакторе по умолчанию, а по окончании внесения изменений заставит systemd применить их.

Чтобы изменить редактор по умолчанию, можно воспользоваться системой изменения альтернатив:

sudo update-alternatives --config editor

Для отката любых внесённых изменений следует использовать:

sudo systemctl revert foobar.service

Управление юнитами systemd

Перезагрузка базы данных установленных юнитов (рекомендуется выполнять при любом внесении изменений):

sudo systemctl daemon-reload

Вывод информации о доступных юнитах:

systemctl list-units

Вывод информации о доступных таймерах:

systemctl list-timers

Запуск:

sudo systemctl start foobar.service

Остановка:

sudo systemctl stop foobar.service

Перезапуск:

sudo systemctl restart foobar.service

Активация (юнит начнёт запускаться автоматически при возникновении указанных в его файле условий):

sudo systemctl enable foobar.service

Отключение:

sudo systemctl disable foobar.service

Проверка текущего статуса:

systemctl status foobar.service

Полная блокировка возможности запуска и работы юнита:

sudo systemctl mask foobar.service

Разблокировка:

sudo systemctl unmask foobar.service

Показать весь журнал работы:

journalctl -u foobar.service

Показать только ошибки:

journalctl -u foobar.service -p err..alert

Создание пользовательских юнитов systemd

Systemd позволяет каждому пользователю создавать собственные юниты и управлять ими. Для хранения таких файлов применяются следующие каталоги (при отсутствии нужно создать самостоятельно):

  • ~/.local/share/systemd/user/ (аналог системного /usr/lib/systemd/user/ для установленных пользовательскими приложениями юнитов);
  • ~/.config/systemd/user/ (каталог для пользовательских файлов конфигурации и юнитов).

Управление ничем не отличается от стандарта за исключением необходимости использования параметра —user. Пример:

systemctl --user start foobar.service

Литература

При написании данной статьи использовались следующие источники: