Files
ARSV/Docs/more/rpmquery-alt-linux.md
T
2026-07-20 21:58:04 +03:00

21 KiB
Raw Blame History

rpmquery в ALT Linux

Краткий вывод

rpmquery — отдельная точка входа в тот же механизм запросов RPM, что и rpm -q. В ALT Linux p11 /usr/bin/rpmquery является символьной ссылкой на ELF-программу /usr/lib/rpm/rpmq; имя запуска заранее включает режим запроса, поэтому -q обычно не нужен:

rpmquery rpm
rpmquery -q rpm
rpm -q rpm

Все три команды дают один и тот же результат. При этом rpmquery не является интерфейсом к репозиторию APT: без -p он читает только установленную базу RPM, по умолчанию /var/lib/rpm. Для поиска доступных, но не установленных пакетов нужны apt-cache, apt-get или веб/RDB-интерфейсы ALT.

Исследование проверено на официальном контейнере alt:p11, обновлённом до rpm-4.13.0.1-alt45.x86_64, и по ветке p11 исходников RPM, commit 7e71ed3da438289d916f1cccc9c0a83e594455dc.

Где ищет rpmquery

Вызов Источник данных
rpmquery NAME установленная база RPM
rpmquery -a все пакеты из установленной базы RPM
rpmquery -p FILE.rpm заголовок указанного RPM-файла, установка не требуется
rpmquery --root ROOT ... база и файловая система относительно ROOT
rpmquery --dbpath DIR ... явно указанная база RPM

Практическая граница была проверена так: apt-cache show strace находил пакет в p11, тогда как rpmquery strace отвечал package strace is not installed и завершался с кодом 1.

Работа с APT-репозиторием

APT-RPM загружает индексы подключённых репозиториев в /var/lib/apt/lists. Обычный порядок работы:

# Показать настроенные источники ALT, если установлен пакет apt-repo.
apt-repo

# Обновить локальные индексы после изменения источников.
apt-get update

В минимальных контейнерах apt-repo может отсутствовать; источники всё равно задаются файлами /etc/apt/sources.list и /etc/apt/sources.list.d/*.list.

Сами pkglist.* являются бинарными header-list файлами RPM, поэтому искать в них обычным grep не следует. Для доступа к кэшу предназначен apt-cache:

Задача Команда
Поиск по имени и описанию apt-cache search REGEX
Перечень имён apt-cache pkgnames
Выбранная версия и приоритеты источников apt-cache policy PACKAGE
Полная доступная запись, включая версии зависимостей apt-cache show PACKAGE
Граф разрешения зависимостей apt-cache depends PACKAGE
Обратные зависимости apt-cache whatdepends PACKAGE
Низкоуровневое представление версий и зависимостей apt-cache showpkg PACKAGE
Метаданные исходного пакета apt-cache showsrc PACKAGE
Все доступные записи репозиториев apt-cache dumpavail
Проверка плана установки без изменений apt-get -s install PACKAGE
Только скачать бинарный пакет и зависимости apt-get -d install PACKAGE
Скачать исходники apt-get source PACKAGE

apt-cache depends удобен для просмотра найденных провайдеров, но в его обычном выводе set-версия зависимости теряется. Для полных set-строк нужны apt-cache show, apt-cache showpkg, скачанный RPM или RDB API.

Set-строки пакета из APT-репозитория

Способ 1: прямо из локального кэша APT

Это самый короткий способ, установка и скачивание RPM не нужны:

pkg=libdw

# Сначала проверить, какую версию и из какого источника выбрал APT.
apt-cache policy "$pkg"

# Полные поля с set-Requires и set-Provides.
apt-cache show "$pkg" |
sed -n -E '/^(Pre-Depends|Depends|Provides):/p' |
grep -F 'set:'

В проверенном p11 поля имели вид:

Depends: ... liblzma.so.5()(64bit) (>= set:kiyIz7cr3p0), ...
Provides: libdw.so.1()(64bit) (= set:ldm5ZH1pjPBqjZ9bdJbLDnKGbC1hkJSX...)

Depends и Pre-Depends соответствуют требованиям пакета, Provides — тому, что пакет предоставляет. apt-cache show может вывести несколько записей, если в подключённых источниках доступны разные версии; выбранный кандидат виден в apt-cache policy.

Для выгрузки всего подключённого репозитория можно использовать:

apt-cache dumpavail >available.txt
grep -E '^(Pre-Depends|Depends|Provides):.*set:' available.txt

В отличие от HTML-страниц packages.altlinux.org, кэш APT содержит полные set-строки без визуального многоточия.

Способ 2: скачать RPM через APT и запросить его заголовок

Это наиболее точный локальный способ: выводятся исходные массивы тегов RPM и их удобно преобразовать в TSV. apt-get -d скачивает также отсутствующие зависимости, но ничего не устанавливает.

pkg=strace
cache=$(mktemp -d)
trap 'rm -rf "$cache"' EXIT
mkdir -p "$cache/partial"

apt-get -y -d \
  -o "Dir::Cache::archives=$cache" \
  install "$pkg"

rpmfile=$(find "$cache" -maxdepth 1 -type f \
  -name "${pkg}_*.rpm" -print -quit)

Set-Requires выбранного пакета:

rpmquery -p --qf \
  '[%{REQUIRENAME}\t%{REQUIREFLAGS:depflags}\t%{REQUIREVERSION}\n]' \
  "$rpmfile" |
awk -F '\t' '$3 ~ /^set:/'

Set-Provides выбранного пакета:

rpmquery -p --qf \
  '[%{PROVIDENAME}\t%{PROVIDEFLAGS:depflags}\t%{PROVIDEVERSION}\n]' \
  "$rpmfile" |
awk -F '\t' '$3 ~ /^set:/'

Для strace-7.0-alt1.x86_64.rpm в p11 получены два требования:

libdw.so.1()(64bit)       >=  set:lgHyMNBkvSTjjY0en8Yi3vFojgjVM7jRsRF4zCFBzNVmhhGF
libselinux.so.1()(64bit)  >=  set:liZ0N709Wr2dbKPs2

Set-Provides у этого пакета нет: исполняемый strace требует библиотеки, но не экспортирует собственную разделяемую библиотеку. У скачанного вместе с ним пакета libdw присутствует длинный Provide libdw.so.1()(64bit) = set:....

Если пакет уже установлен и APT не скачивает его повторно, его заголовок можно прочитать напрямую командой rpmquery PACKAGE; для гарантированного получения конкретного репозиторного RPM удобнее использовать RDB API.

Способ 3: структурированные данные RDB API

RDB удобен для автоматизации, другой ветки/архитектуры и больших выборок. Он возвращает полные строки и явно различает provide, require и conflict:

branch=p11
arch=x86_64
pkg=libdw

hash=$(curl -fsS \
  "https://rdb.altlinux.org/api/site/pkghash_by_binary_name?branch=$branch&name=$pkg&arch=$arch" |
  jq -r '.pkghash')

curl -fsS \
  "https://rdb.altlinux.org/api/dependencies/binary_package_dependencies/$hash" |
jq -r '
  .dependencies[]
  | select(
      (.type == "provide" or .type == "require") and
      (.version | startswith("set:"))
    )
  | [.type, .name, .version]
  | @tsv
'

Проверенный результат для libdw содержит один set-Provide и четыре set-Requires. Поле flag_decoded того же JSON позволяет восстановить оператор: RPMSENSE_EQUAL для обычного Provide и RPMSENSE_GREATER + RPMSENSE_EQUAL для >= Require.

Через RDB можно также получить URL самого RPM:

curl -fsS \
  "https://rdb.altlinux.org/api/site/package_downloads_bin/$hash?branch=$branch&arch=$arch" |
jq -r '.downloads[].packages[].url'

Основные запросы

Пакет и его метаданные

# Версия установленного пакета в стандартном формате NVRA.
rpmquery rpm

# Подробная информация. В ALT вывод включает DistTag.
rpmquery -i rpm
rpmquery --info rpm

# Стабильный формат для скрипта.
rpmquery --qf '%{NAME}|%{EPOCHNUM}|%{VERSION}|%{RELEASE}|%{DISTTAG}|%{ARCH}\n' rpm

# Доступные имена тегов заголовка.
rpmquery --querytags

В rpmquery короткая опция -i означает --info, а не установку пакета. Это ALT-алиас из rpmpopt; он появился в changelog RPM в версии 4.13.0-alt7. Для установки используется другой режим программы rpm, но в обычной работе с ALT зависимости и репозитории следует поручать APT.

Файлы

# Какому установленному пакету принадлежит файл.
rpmquery -f /usr/bin/rpmquery

# Все файлы пакета.
rpmquery -l rpm

# Только конфигурационные или документационные файлы.
rpmquery -c rpm
rpmquery -d rpm

# Пакет из файла без установки.
rpmquery -p ./package.rpm
rpmquery -lp ./package.rpm
rpmquery -ip ./package.rpm

Для аргумента-пути --whatprovides сначала обращается к индексу установленных файлов, затем к индексу capability. Поэтому для существующего установленного файла эти команды обычно совпадают:

rpmquery -f /usr/bin/rpmquery
rpmquery --whatprovides /usr/bin/rpmquery

Provides, Requires и обратные запросы

# Что объявляет один пакет.
rpmquery --provides rpm
rpmquery --requires rpm
rpmquery -R rpm

# Какие установленные пакеты объявляют capability или требуют его.
rpmquery --whatprovides 'libpopt.so.0()(64bit)'
rpmquery --whatrequires 'libpopt.so.0()(64bit)'

--provides, --requires, --info, --last, --scripts и ряд других удобных режимов реализованы как popt-алиасы над --queryformat, а не как отдельные алгоритмы запросов.

Формат запросов для автоматической обработки

Стандартный вывод удобен человеку, но надёжнее не разбирать его пробелами. Массивы тегов RPM обходятся форматом в квадратных скобках. Чтобы внутри такого итератора повторять скалярный тег для каждого элемента массива, знак = ставят внутри имени тега: %{=NAME}.

Все set-Requires в TSV

LC_ALL=C rpmquery -a --qf \
  '[%{=NAME}\t%{REQUIRENAME}\t%{REQUIREFLAGS:depflags}\t%{REQUIREVERSION}\n]' |
awk -F '\t' '$4 ~ /^set:/'

Поля: пакет, имя capability, оператор, версия зависимости.

Пример:

rpm	libpopt.so.0()(64bit)	>=	set:jgtcU6BLBccTnteGxrE0

Все set-Provides в TSV

LC_ALL=C rpmquery -a --qf \
  '[%{=NAME}\t%{PROVIDENAME}\t%{PROVIDEFLAGS:depflags}\t%{PROVIDEVERSION}\n]' |
awk -F '\t' '$4 ~ /^set:/'

Пример:

libpopt	libpopt.so.0()(64bit)	=	set:jdtcJcAdqxTmPJUyuYcVIbLNhqmj...

Это предпочтительнее grep по человекочитаемому выводу, если нужны имена пакетов, операторы или однозначное разделение полей.

Что rpmquery делает и не делает с set:version

Показывает сохранённые зависимости

rpmquery --requires и rpmquery --provides извлекают из заголовков пакетов имя capability, флаги отношения и строку версии. Они не декодируют set: и не показывают исходные ELF-символы.

Для установленного rpm в p11 наблюдалось:

libpopt.so.0()(64bit) >= set:jgtcU6BLBccTnteGxrE0

Установленный провайдер находился по одному имени capability:

rpmquery --whatprovides 'libpopt.so.0()(64bit)'
# libpopt-1.18-alt1.x86_64

Не сравнивает выражение, переданное в --whatprovides

Передача полного выражения не запускает rpmsetcmp():

rpmquery --whatprovides \
  'libpopt.so.0()(64bit) >= set:jgtcU6BLBccTnteGxrE0'

Проверенный результат:

no package provides libpopt.so.0()(64bit) >= set:jgtcU6BLBccTnteGxrE0

Для непутевого аргумента --whatprovides делает точный поиск по индексу PROVIDENAME. Поэтому сначала нужно искать провайдера по имени capability, а его set-строку получать отдельным запросом:

name='libpopt.so.0()(64bit)'
provider=$(rpmquery --whatprovides "$name" | head -n1)
rpmquery --qf \
  '[%{PROVIDENAME}\t%{PROVIDEFLAGS:depflags}\t%{PROVIDEVERSION}\n]' \
  "$provider" |
grep -F "$name"

Настоящее сравнение двух set:-версий происходит в rpmdsCompareEVR() при проверке совместимости зависимостей. Если обе версии начинаются с set:, код вызывает rpmsetcmp(); если set: имеет только одна сторона, зависимости не пересекаются. Следовательно, rpmquery полезен для извлечения корпуса строк, но не является CLI для непосредственного сравнения двух наборов.

Как rpmquery используется при сборке самого ALT RPM

В alt/rpm.spec ветки p11 после первой сборки выполняется:

rpmquery -a --provides | fgrep '= set:' | sort >P
rpmquery -a --requires | fgrep '= set:' | sort >R
join -o 1.3,2.3 P R | shuf >setcmp-data
time ./setcmp <setcmp-data >/dev/null

Смысл конвейера:

  1. Из локальной базы сборочного окружения собираются все set-Provides и set-Requires.
  2. Строка поиска = set: захватывает как Provides с = set:, так и Requires с >= set:, потому что вторая строка тоже содержит подстроку = set:.
  3. После сортировки join соединяет файлы по первому полю — имени capability.
  4. -o 1.3,2.3 оставляет только третьи поля: set провайдера и set требования.
  5. Полученные реальные пары подаются в tools/setcmp для замера реализации rpmsetcmp() и профиль-управляемой пересборки lib/set.c.

В обновлённом минимальном контейнере p11 точный конвейер дал:

P: 75 строк
R: 109 строк, из них 13 полных дублей
setcmp-data: 109 пар

Эти числа описывают только состав минимального контейнера, а не весь p11 или Sisyphus. Также конвейер spec не сохраняет имя capability и пакет-владелец; для исследовательского корпуса удобнее TSV-форматы выше.

Современная замена устаревающего fgrep без изменения смысла:

grep -F '= set:'

Практические ограничения

  • rpmquery -a означает все установленные пакеты, а не весь репозиторий.
  • --whatprovides и --whatrequires ищут заголовки по имени capability; они не принимают полноценное выражение зависимости как запрос сравнения.
  • Несколько установленных версий или провайдеров могут дать несколько строк.
  • Set-строки крупных библиотек очень длинные; лучше писать полный вывод в файл, а в терминале показывать только статистику или начало строки.
  • Для скриптов следует фиксировать LC_ALL=C и использовать --queryformat.
  • --requires/--provides отражают метаданные RPM, а не текущее содержимое ELF на диске. Для восстановления исходных символов нужны nm, readelf и генераторы автозависимостей rpm-build.
  • При запросе другой системы безопаснее явно использовать --root; --dbpath меняет только путь к БД и требует внимательности к относительным путям.
  • ALT предоставляет --nowait-lock как popt-алиас, отключающий ожидание блокировки БД. Его стоит применять только когда отказ предпочтительнее ожидания, а не как способ обходить активную транзакцию RPM/APT.

Источники

  1. Документация платформы ALT 10.1: утилита RPM — RPM работает с файлами, пакетами, зависимостями и собственной БД, но не знает о репозиториях.
  2. Настройка списка репозиториев APT в ALT.
  3. Пакет rpm в p11 и specfile.
  4. RDB: текущий бинарный пакет rpm для p11/x86_64.
  5. rpmqv.c — выбор режима Query по имени rpmquery/rpmq.
  6. rpmpopt.in — ALT-алиасы --requires, --provides, --info, -i, --nowait-lock.
  7. lib/query.c — индексы для --whatprovides, запросы файлов и формат %{nvra} по умолчанию.
  8. lib/rpmds.c — вызов rpmsetcmp() при сравнении двух set-версий.
  9. alt/rpm.spec — построение setcmp-data через rpmquery.
  10. Официальный контейнер ALT — среда воспроизведения alt:p11.