Продолжаем разбирать systemd на кирпичики. Сегодня про кандишены (условия/ситуации).

Директива ConditionPathExists используется в юнит-файлах systemd и позволяет задать условие для запуска юнита: он будет запущен только если указанный файл или директория существует.

Пример:

[Unit]
Description=Special Service
ConditionPathExists=/etc/bashdays-config

[Service]
ExecStart=/usr/local/bin/bashdays-handler

Этот юнит не запустится, если файл /etc/bashdays-config не существует. В статусе сервиса ты увидишь:

ConditionPathExists=/etc/bashdays-config was not met

Если путь НЕ существует, systemd не будет запускать юнит. Вместо этого он будет считаться пропущенным (skipped), а не проваленным.

Нахуя это надо?

  1. Например, если bashdays-config существует, запускается сервис с особым поведением.

  2. Можно создавать один юнит, который активируется только при наличии определённого модуля или плагина.

  3. Иногда это используют в early boot-юнитах, чтобы запускать их только если что-то доступно (например, том LUKS).

Список основных Condition’s

  • ConditionPathExists — файл или каталог существует
  • ConditionPathIsDirectory — путь существует и это каталог
  • ConditionPathIsMountPoint — путь является точкой монтирования
  • ConditionFileIsExecutable — файл существует и он исполняемый
  • ConditionKernelCommandLine — есть ли параметр ядра с указанным значением
  • ConditionPathExistsGlob — совпадает ли хотя бы один путь по glob-шаблону
  • ConditionPathIsSymbolicLink — является ли путь символической ссылкой
  • ConditionFileNotEmpty — существует ли файл и не пуст ли он
  • ConditionEnvironment — установлена ли переменная окружения
  • ConditionArchitecture — архитектура CPU (например, x86_64, aarch64)
  • ConditionVirtualization — nип виртуализации (например, kvm, docker)
  • ConditionHost — имя хоста
  • ConditionMachineID — cовпадает ли machine-id
  • ConditionControlGroupController — есть ли указанный cgroup controller (например, cpu, memory)
  • ConditionNeedsUpdate — нуждается ли в обновлении (/usr или /etc)
  • ConditionFirstBoot — Первый ли это запуск после установки
  • ConditionACPower — Подключено ли питание от сети (для ноутбуков)
  • ConditionSecurity — Активен ли определённый LSM (например, selinux)
  • ConditionUser — Запускается ли от указанного пользователя
  • ConditionGroup — Запускается ли от указанной группы
  • ConditionCapability — Имеет ли процесс определённую capability
  • ConditionNetwork — Есть ли сеть (online, configured)
  • ConditionMemory — Есть ли минимум указанного объёма памяти

Дополнительно

Если заменить Condition на Assert, условие не выполнено — юнит считается проваленным, а не пропущенным.

То есть берем к примеру директиву ConditionPathExists и меняем её на AssertPathExists.

AssertPathExists=/etc/bashdays.conf

И получается:

ConditionPathExists — юнит пропускается (не считается ошибкой)

AssertPathExists — юнит падает (считается ошибкой)

Assert полезен, когда ты строго требуешь, чтобы ресурс (например, внешний диск, NFS, или другой том) был смонтирован перед запуском сервиса. Если его нет — это ошибка, а не «ну и похуй».

[Unit]
Description=Start backup script only if /mnt/backup is mounted
AssertPathIsMountPoint=/mnt/backup

[Service]
ExecStart=/usr/local/bin/backup.sh

Если /mnt/backup не смонтирован, systemd выдаст ошибку, и сервис не запустится.

Статус будет такой:

systemd[1]: Starting backup.service...
systemd[1]: backup.service: Failed with result 'assert'.

Ну и все это дело можно комбинировать:

ConditionPathExists=/mnt/backup
AssertPathIsMountPoint=/mnt/backup

Если /mnt/backup не существует — юнит пропускается.

Если существует, но не смонтирован — юнит заваливается.

Роман Шубин
Роман Шубин
CEO & CTO, Главред в «Цифровой улей»
Задать вопрос
Короче systemd не такой уж простой, как кажется с первого взгляда. На нём можно прям заебись логику построить и получить желаемое. Так что недооценивать его явно не стоит, это прям заебись комбайн.