21 KiB
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
Смысл конвейера:
- Из локальной базы сборочного окружения собираются все set-Provides и set-Requires.
- Строка поиска
= set:захватывает как Provides с= set:, так и Requires с>= set:, потому что вторая строка тоже содержит подстроку= set:. - После сортировки
joinсоединяет файлы по первому полю — имени capability. -o 1.3,2.3оставляет только третьи поля: set провайдера и set требования.- Полученные реальные пары подаются в
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.
Источники
- Документация платформы ALT 10.1: утилита RPM — RPM работает с файлами, пакетами, зависимостями и собственной БД, но не знает о репозиториях.
- Настройка списка репозиториев APT в ALT.
- Пакет
rpmв p11 и specfile. - RDB: текущий бинарный пакет
rpmдля p11/x86_64. rpmqv.c— выбор режима Query по имениrpmquery/rpmq.rpmpopt.in— ALT-алиасы--requires,--provides,--info,-i,--nowait-lock.lib/query.c— индексы для--whatprovides, запросы файлов и формат%{nvra}по умолчанию.lib/rpmds.c— вызовrpmsetcmp()при сравнении двух set-версий.alt/rpm.spec— построениеsetcmp-dataчерезrpmquery.- Официальный контейнер ALT — среда воспроизведения
alt:p11.