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

445 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `rpmquery` в ALT Linux
## Краткий вывод
`rpmquery` — отдельная точка входа в тот же механизм запросов RPM, что и
`rpm -q`. В ALT Linux p11 `/usr/bin/rpmquery` является символьной ссылкой на
ELF-программу `/usr/lib/rpm/rpmq`; имя запуска заранее включает режим запроса,
поэтому `-q` обычно не нужен:
```sh
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`.
Обычный порядок работы:
```sh
# Показать настроенные источники 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 не нужны:
```sh
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 поля имели вид:
```text
Depends: ... liblzma.so.5()(64bit) (>= set:kiyIz7cr3p0), ...
Provides: libdw.so.1()(64bit) (= set:ldm5ZH1pjPBqjZ9bdJbLDnKGbC1hkJSX...)
```
`Depends` и `Pre-Depends` соответствуют требованиям пакета, `Provides` — тому,
что пакет предоставляет. `apt-cache show` может вывести несколько записей,
если в подключённых источниках доступны разные версии; выбранный кандидат
виден в `apt-cache policy`.
Для выгрузки всего подключённого репозитория можно использовать:
```sh
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` скачивает также отсутствующие
зависимости, но ничего не устанавливает.
```sh
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 выбранного пакета:
```sh
rpmquery -p --qf \
'[%{REQUIRENAME}\t%{REQUIREFLAGS:depflags}\t%{REQUIREVERSION}\n]' \
"$rpmfile" |
awk -F '\t' '$3 ~ /^set:/'
```
Set-Provides выбранного пакета:
```sh
rpmquery -p --qf \
'[%{PROVIDENAME}\t%{PROVIDEFLAGS:depflags}\t%{PROVIDEVERSION}\n]' \
"$rpmfile" |
awk -F '\t' '$3 ~ /^set:/'
```
Для `strace-7.0-alt1.x86_64.rpm` в p11 получены два требования:
```text
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`:
```sh
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:
```sh
curl -fsS \
"https://rdb.altlinux.org/api/site/package_downloads_bin/$hash?branch=$branch&arch=$arch" |
jq -r '.downloads[].packages[].url'
```
## Основные запросы
### Пакет и его метаданные
```sh
# Версия установленного пакета в стандартном формате 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.
### Файлы
```sh
# Какому установленному пакету принадлежит файл.
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. Поэтому для существующего установленного
файла эти команды обычно совпадают:
```sh
rpmquery -f /usr/bin/rpmquery
rpmquery --whatprovides /usr/bin/rpmquery
```
### Provides, Requires и обратные запросы
```sh
# Что объявляет один пакет.
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
```sh
LC_ALL=C rpmquery -a --qf \
'[%{=NAME}\t%{REQUIRENAME}\t%{REQUIREFLAGS:depflags}\t%{REQUIREVERSION}\n]' |
awk -F '\t' '$4 ~ /^set:/'
```
Поля: пакет, имя capability, оператор, версия зависимости.
Пример:
```text
rpm libpopt.so.0()(64bit) >= set:jgtcU6BLBccTnteGxrE0
```
### Все set-Provides в TSV
```sh
LC_ALL=C rpmquery -a --qf \
'[%{=NAME}\t%{PROVIDENAME}\t%{PROVIDEFLAGS:depflags}\t%{PROVIDEVERSION}\n]' |
awk -F '\t' '$4 ~ /^set:/'
```
Пример:
```text
libpopt libpopt.so.0()(64bit) = set:jdtcJcAdqxTmPJUyuYcVIbLNhqmj...
```
Это предпочтительнее `grep` по человекочитаемому выводу, если нужны имена
пакетов, операторы или однозначное разделение полей.
## Что `rpmquery` делает и не делает с `set:version`
### Показывает сохранённые зависимости
`rpmquery --requires` и `rpmquery --provides` извлекают из заголовков пакетов
имя capability, флаги отношения и строку версии. Они не декодируют `set:` и не
показывают исходные ELF-символы.
Для установленного `rpm` в p11 наблюдалось:
```text
libpopt.so.0()(64bit) >= set:jgtcU6BLBccTnteGxrE0
```
Установленный провайдер находился по одному имени capability:
```sh
rpmquery --whatprovides 'libpopt.so.0()(64bit)'
# libpopt-1.18-alt1.x86_64
```
### Не сравнивает выражение, переданное в `--whatprovides`
Передача полного выражения не запускает `rpmsetcmp()`:
```sh
rpmquery --whatprovides \
'libpopt.so.0()(64bit) >= set:jgtcU6BLBccTnteGxrE0'
```
Проверенный результат:
```text
no package provides libpopt.so.0()(64bit) >= set:jgtcU6BLBccTnteGxrE0
```
Для непутевого аргумента `--whatprovides` делает точный поиск по индексу
`PROVIDENAME`. Поэтому сначала нужно искать провайдера по имени capability, а
его set-строку получать отдельным запросом:
```sh
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 после первой сборки выполняется:
```sh
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 точный конвейер дал:
```text
P: 75 строк
R: 109 строк, из них 13 полных дублей
setcmp-data: 109 пар
```
Эти числа описывают только состав минимального контейнера, а не весь p11 или
Sisyphus. Также конвейер spec не сохраняет имя capability и пакет-владелец;
для исследовательского корпуса удобнее TSV-форматы выше.
Современная замена устаревающего `fgrep` без изменения смысла:
```sh
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](https://docs.altlinux.org/ru-RU/platform/10.1/html/alt-platform/utilita_komandnoj_stroki_rpm.html) — RPM работает с файлами, пакетами, зависимостями и собственной БД, но не знает о репозиториях.
2. [Настройка списка репозиториев APT в ALT](https://docs.altlinux.org/ru-RU/platform/10.1/html/alt-platform/nastrojka_spiska_repozitoriev_apt.html).
3. [Пакет `rpm` в p11](https://packages.altlinux.org/en/p11/srpms/rpm/) и [specfile](https://packages.altlinux.org/en/p11/srpms/rpm/specfiles/).
4. [RDB: текущий бинарный пакет `rpm` для p11/x86_64](https://rdb.altlinux.org/api/site/pkghash_by_binary_name?branch=p11&name=rpm&arch=x86_64).
5. [`rpmqv.c`](https://git.altlinux.org/gears/r/rpm.git?p=rpm.git;a=blob;f=rpmqv.c;hb=7e71ed3da438289d916f1cccc9c0a83e594455dc) — выбор режима Query по имени `rpmquery`/`rpmq`.
6. [`rpmpopt.in`](https://git.altlinux.org/gears/r/rpm.git?p=rpm.git;a=blob;f=rpmpopt.in;hb=7e71ed3da438289d916f1cccc9c0a83e594455dc) — ALT-алиасы `--requires`, `--provides`, `--info`, `-i`, `--nowait-lock`.
7. [`lib/query.c`](https://git.altlinux.org/gears/r/rpm.git?p=rpm.git;a=blob;f=lib/query.c;hb=7e71ed3da438289d916f1cccc9c0a83e594455dc) — индексы для `--whatprovides`, запросы файлов и формат `%{nvra}` по умолчанию.
8. [`lib/rpmds.c`](https://git.altlinux.org/gears/r/rpm.git?p=rpm.git;a=blob;f=lib/rpmds.c;hb=7e71ed3da438289d916f1cccc9c0a83e594455dc) — вызов `rpmsetcmp()` при сравнении двух set-версий.
9. [`alt/rpm.spec`](https://git.altlinux.org/gears/r/rpm.git?p=rpm.git;a=blob;f=alt/rpm.spec;hb=7e71ed3da438289d916f1cccc9c0a83e594455dc) — построение `setcmp-data` через `rpmquery`.
10. [Официальный контейнер ALT](https://hub.docker.com/_/alt) — среда воспроизведения `alt:p11`.