systemctl проверьте, существует ли юнит (сервис или цель)

Вопрос или проблема

Я хотел бы узнать, существует ли определенное systemd единица.

Это должно работать для:

  • любого типа единицы (сервис, цель, монтирование и т.д.)
  • работающей, отключенной или заблокированной единицы

Я знаю, что могу сделать это:

systemctl list-unit-files | grep "^my.target"

Но мне кажется, что должен быть лучший способ.

Опционально, я хотел бы иметь возможность выполнить эту проверку, просто указав my, без необходимости указывать “.service” (как для других systemctl команд), что-то вроде

systemctl exists my

Я не знаю о нативном способе сделать это с помощью systemd, но вы могли бы (зло)использовать systemctl list-unit-files:

systemctl-exists() {
  [ $(systemctl list-unit-files "${1}*" | wc -l) -gt 3 ]
}

Это создает функцию “тестирования”, которую вы могли бы использовать так:

systemctl-exists my && echo my существует как systemd единица

Суффикс * нужен для того, чтобы позволить systemd сопоставить данный аргумент с любым “типом” (сервис, цель или монтирование). Эта функция жестко закодирована для текущего вывода systemctl list-unit-files, который включает как минимум три строки вывода (когда совпадающих единиц нет); больше, когда есть совпадающие единицы:

1. UNIT FILE            STATE
   (один или несколько соответствующих файлов единиц)
2. (пустая строка)
3. "%d файлов единиц перечислены."

Также имейте в виду, что подстановочный знак в конце может привести к ложноположительным результатам, если у вас есть файлы единиц с аналогичными префиксами — поиск “au” найдет “дурное золото” с “auditd”, “autofs” и другими, даже если вы ожидали только реальную вещь “au.service”. Уточняйте больше название сервиса, если вы его знаете: systemctl-exists au.service сделает правильное.

Сначала я думал, что systemctl cat будет работать как фильтр, но, похоже, он предполагает, что аргумент является сервисом, и поэтому не фильтрует соответствующим образом для других типов (например, цель или монтирование).

Обновленный ответ

Судя по всему, systemctl list-unit-files "$systemd_unit_name" вернет статус выхода 0, если хотя бы одна единица* соответствующая "$systemd_unit_name" существует, иначе вернет статус выхода 1.

* Примечание: systemctl list-unit-files ... явно не перечисляет устройства (.device).

Обновленные однострочники

Эти (обновленные) однострочники могут проверять существование единиц для любого типа единицы, включая сервисные единицы, целевые единицы, единицы сокетов, а также шаблонные единицы (например, [email protected]) и шаблонные экземпляры единиц (например, [email protected]).

### точное тестирование существования не-устройств
### тестирование, существует ли какая-либо единица (не включая устройства) с именем 'foo.service':

systemctl list-unit-files foo.service &>/dev/null && echo "эта единица существует" || echo "эта единица не существует"


### шаблонное сопоставление теста существования не-устройств
### тестирование, существует ли хотя бы одна единица (не включая устройства) с именем, соответствующим 'ssh*':

systemctl list-unit-files ssh* &>/dev/null && echo "хотя бы одна единица совпадает" || echo "нет совпадений единиц"

Для устройств используется команда systemctl status. Несмотря на то, что systemctl‘s документация упоминает, что статус выхода в пределах 0 и 3 указывает на существование единицы, а статус выхода 4 указывает на “такой единицы нет”, я заметил, что для имен, заканчивающихся на .device, systemctl status doesntexist.device по-прежнему возвращает статус выхода 3. Поэтому следующий тест, специфичный для юнитов устройств, рассматривает статус выхода 0 как указание на то, что указанная единица устройства существует:

### точное тестирование существования устройства
# тестирует, существует ли какая-либо единица устройства с именем 'sys-module-fuse.device':

systemctl status sys-module-fuse.device &>/dev/null && echo "эта единица устройства существует" || echo "эта единица устройства не существует"

Ошибочный ответ

(По крайней мере с systemd версией 247, предыдущая версия этого ответа будет давать ненадежные результаты при тестировании шаблонной единицы или экземпляра шаблонной единицы (т.е. имя единицы как @, [1] [2]))

Если статус выхода systemctl равен 4, то указанное имя единицы неизвестно systemd (т.е. запрашиваемая единица не существует).

Ошибочный однострочник

# пример однострочника для тестирования существования какой-либо единицы с именем 'foo.service':
# !!! НЕ РАБОТАЕТ НАДЕЖНО ДЛЯ ШАБЛОННЫХ ЕДИНИЦ ИЛИ ЭКЗЕМПЛЯРОВ ШАБЛОННЫХ ЕДИНИЦ

systemctl status foo.service &>/dev/null; if [[ $? == 4 ]]; then echo "эта единица не существует"; else echo "эта единица существует"; fi

Ошибочный скрипт на Bash

#!/bin/bash
set -euo pipefail

# файл test-systemd-unit-existence.sh
#
# этот скрипт проверяет, существует ли системная единица systemd под указанным именем.
#
# !!! НЕ РАБОТАЕТ НАДЕЖНО ДЛЯ ШАБЛОННЫХ ЕДИНИЦ ИЛИ ЭКЗЕМПЛЯРОВ ШАБЛОННЫХ ЕДИНИЦ
#
# если она существует, этот скрипт выводит "'$SYSTEMD_UNIT' существует под полным именем единицы '$SYSTEMD_UNIT_FULL_NAME'",
# иначе ("единица неизвестна"/"такой единицы нет") выводит "'$SYSTEMD_UNIT' не существует".
#
# Проверка осуществляется выполнением "systemctl status $SYSTEMD_UNIT",
# затем проверкой его статуса выхода
#
# см. https://www.freedesktop.org/software/systemd/man/systemctl.html#Exit%20status
#
# примеры использования:
# ./test-systemd-unit-existence.sh ssh.service
# ./test-systemd-unit-existence.sh ssh
# ./test-systemd-unit-existence.sh basic.target
# ./test-systemd-unit-existence.sh doesntexist.service
# ./test-systemd-unit-existence.sh uuidd
# ./test-systemd-unit-existence.sh uuidd.service
# ./test-systemd-unit-existence.sh uuidd.socket
# ./test-systemd-unit-existence.sh uuidd.target

SYSTEMD_UNIT="$1"

# использование "&>/dev/null" для подавления вывода stdout и stderr ("--quiet" только подавляет stdout).
# из-за "set -e" используется конструкция "... && true", чтобы избежать немедленного выхода скрипта при ожидаемом ненулевом коде выхода.
#
#   "[...] Оболочка не выходит, если команда, которая завершилась неудачно,
#    [...] часть любой команды, выполненной в списке && или || 
#    [пока это не последняя команда в этом списке]"
#
# см. https://www.gnu.org/software/bash/manual/html_node/Lists.html
# см. "-e" на https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html

systemctl status "$SYSTEMD_UNIT" &>/dev/null && true
SYSTEMCTL_EXIT_STATUS="$?"

if [[ "$SYSTEMCTL_EXIT_STATUS" == 4 ]]; then
  echo "'${SYSTEMD_UNIT}' не существует"
else
  SYSTEMD_UNIT_FULL_NAME="$(systemctl show ${SYSTEMD_UNIT} --property=Id --value)"
  echo "'${SYSTEMD_UNIT}' существует под полным именем единицы '${SYSTEMD_UNIT_FULL_NAME}'"
fi

Это проверит, загружен ли сервис или нет:

systemctl show --no-pager service_name.service | grep LoadState=loaded
echo $?

grep вернет 1, если искомый термин найден.

Согласно руководству по systemctl, состояние загрузки может быть loaded, not-found, bad-setting, error, masked.

Также существует LoadError, который появится при ошибке загрузки, например:

LoadError=org.freedesktop.systemd1.NoSuchUnit "Единица service_name.service не найдена."

Используя systemctl cat, вы получите ошибку, если единица не существует, примерно так:

# systemctl cat ntp >/dev/null; echo $?
Файлы для ntp.service не найдены.
1
# systemctl cat ntp  >/dev/null 2>&1; echo $?
1
# systemctl cat ntpd  >/dev/null 2>&1; echo $?
0
# systemctl cat ntpd.service  >/dev/null 2>&1; echo $?
0
# systemctl cat ntpd.timer  >/dev/null 2>&1; echo $?
1

Ответ или решение

Чтобы проверить, существует ли определенное системное единичное (unit) объект в systemd, можно использовать несколько различных подходов. Важно понимать, что systemd управляет различными типами единичных объектов, таких как службы (.service), цели (.target), точки монтирования (.mount) и другие. Ниже приведены различные методы и рекомендации по проверке наличия единицы, включая специальные случаи для шаблонов.

Проверка существования единицы

  1. Использование команды systemctl list-unit-files

    Для проверки существования единицы можно использовать команду systemctl list-unit-files. Эта команда возвращает список всех доступных единиц и их состояний, что позволяет вам легко искать нужные единицы. Например, следующий подход легко использовать для поиска конкретной единицы:

    systemctl list-unit-files | grep "^my.target"

    Однако, нужно понимать, что такой метод может быть не самым эффективным, особенно если вы хотите проверить наличие единиц быстрее и проще.

  2. Проверка с помощью ассоциативных команд

    В более современных версиях systemd, таких как версия 247 и выше, можно просто выполнить:

    systemctl list-unit-files my* &>/dev/null && echo "единица существует" || echo "единица не существует"

    Эта команда возвращает статус 0, если хотя бы одна единица, соответствующая шаблону my*, существует, и статус 1 в противном случае.

  3. Тестирование с помощью systemctl status

    Для проверки загрузки конкретной единицы можно использовать systemctl status. Это более надежный способ, поскольку он учитывает различные состояния единиц (например, загруженный, не найден и т.д.).

    systemctl status my.service &>/dev/null
    if [[ $? -eq 0 ]]; then
       echo "единица существует"
    else
       echo "единица не существует"
    fi

    Здесь возвращаемое значение 0 указывает на то, что единица существует, в то время как любое другое значение будет значить, что единица не найдена.

  4. Проверка доменных единиц

    Для проверки существования доменных единиц (например, device units) следует использовать команду systemctl status, как показано ниже:

    systemctl status my.device &>/dev/null && echo "устройство существует" || echo "устройство не существует"

    Несмотря на то что команда может возвращать различные статусы, важно учитывать, что статус 4 указывает на то, что единица не существует.

Проверка всех типов единиц

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

systemctl-exists() {
    unit=$1
    if systemctl list-unit-files "$unit*" &>/dev/null; then
        echo "единица $unit существует"
    else
        echo "единица $unit не существует"
    fi
}

Эта функция воспользуется существующими инструментами systemctl для обеспечения простоты проверки.

Заключение

В заключение, systemctl предоставляет множество методов для проверки существования системных единиц. Выбор конкретного метода зависит от ваших потребностей и уровня требуемой детальности. Будь то прямой вызов systemctl status, использование grep для фильтрации вывода команды list-unit-files, или написание собственной функции, все это поможет эффективно управлять системными единицами и обеспечивать их доступность на вашей системе.

Оцените материал
Добавить комментарий

Капча загружается...