# Линукс без Интернета *Планета есть колыбель разума, но нельзя вечно жить в колыбели[1]. — «Исследование мировых пространств реактивными приборами», 1911—1912* [Константин Эдуардович Циолковский](https://ru.wikiquote.org/wiki/Константин_Эдуардович_Циолковский) Фоновую музыку для этого выпуска снова обеспечивает ["Группа"](https://archive.org/search?query=creator%3A%22%D0%93%D1%80%D1%83%D0%BF%D0%BF%D0%B0+%D0%93%D1%80%D1%83%D0%BF%D0%BF%D0%B0+%D0%9D%D0%A1%D0%9A%22), из Новосибирска и с нашего [федивёрс-узла](https://mastodon.ml/@gruppagruppa). ## Выпуск 20. RPM. Часть 2. Из Debain основанной системы. В этом выпуске в системе на основе Debian поставим утилиты для работы с пакетами RPM и их сборки в домашнем каталоге. Посмотрим какие есть файлы и возможности с ними легко манипулировать не повреждая основную материнскую систему. ### Утилита установки RPM пакетов и базы данных её Здесь и далее команды под непревилигированным пользователем будем обозначать начиная со знака доллара (`$`), за которым непосредственно следует строка набираемая человеком, после ввода которой система выдаёт некий текст на стандартный вывод. В Ubuntu очень просто понять, какой пакет нужно ставить, чтобы работать с RPM. Просто выполняем в консоли команду rpm и система говорит, что ставить: ``` $ rpm Команда «rpm» не найдена, но может быть установлена с помощью: sudo apt install rpm ``` Чем и воспользуемся: ``` $ sudo apt install rpm [sudo] пароль для dron: Чтение списков пакетов… Готово Построение дерева зависимостей… Готово Чтение информации о состоянии… Готово Будут установлены следующие дополнительные пакеты: debugedit libfsverity0 librpmbuild9t64 librpmsign9t64 Предлагаемые пакеты: alien elfutils rpmlint rpm-i18n Следующие НОВЫЕ пакеты будут установлены: debugedit libfsverity0 librpmbuild9t64 librpmsign9t64 rpm Обновлено 0 пакетов, установлено 5 новых пакетов, для удаления отмечено 0 пакетов, и 0 пакетов не обновлено. Необходимо скачать 311 kB архивов. После данной операции объём занятого дискового пространства возрастёт на 992 kB. Хотите продолжить? [Д/н] ``` Следует отметить, что пакет alien весьма полезен при конвертации файлов из разных форматов. Хотя непонятно, как там будут работать специфичные скрипты пре- и пост- установки и удаления... Видимо, рассчёт на их простоту. **rpmlint** вроде как должен проверять синтаксис spec-файлов при сборке. Но об этом позже. А вот **rpm-i18n** должен обеспечить локализацию, поэтому будем его ставить первым: ``` sudo apt install rpm-i18n elfutils Чтение списков пакетов… Готово Построение дерева зависимостей… Готово Чтение информации о состоянии… Готово Будут установлены следующие дополнительные пакеты: libasm1t64 Следующие НОВЫЕ пакеты будут установлены: elfutils libasm1t64 rpm-i18n Обновлено 0 пакетов, установлено 3 новых пакетов, для удаления отмечено 0 пакетов, и 0 пакетов не обновлено. Необходимо скачать 931 kB архивов. После данной операции объём занятого дискового пространства возрастёт на 4 958 kB. Хотите продолжить? [Д/н] Пол:1 http://ru.archive.ubuntu.com/ubuntu noble-updates/main amd64 libasm1t64 amd64 0.190-1.1ubuntu0.1 [17,5 kB] Пол:2 http://ru.archive.ubuntu.com/ubuntu noble-updates/main amd64 elfutils amd64 0.190-1.1ubuntu0.1 [525 kB] Пол:3 http://ru.archive.ubuntu.com/ubuntu noble/universe amd64 rpm-i18n all 4.18.2+dfsg-2.1build2 [389 kB] Получено 931 kB за 0с (5 443 kB/s) Выбор ранее не выбранного пакета libasm1t64:amd64. (Чтение базы данных … на данный момент установлено 224716 файлов и каталогов.) Подготовка к распаковке …/libasm1t64_0.190-1.1ubuntu0.1_amd64.deb … Распаковывается libasm1t64:amd64 (0.190-1.1ubuntu0.1) … ... ``` Также поставим пакет **elfutils** для манипуляций двоичными исполнимыми файлами и разделяемыми библиотеками. Итак, вроде утилита RPM установилась и показала свою работоспособность рассказав о своей версии: ``` $ rpm --version RPM версия 4.18.2 ``` Пойдём дальше и посмотрим, как с ней обращаться по более полной программе: ``` $ rpm --help Использование: rpm [ПАРАМЕТР...] Query/Verify package selection options: -a, --all запросить/проверить все пакеты -f, --file query/verify package(s) owning installed file --path query/verify package(s) owning path, installed or not -g, --group запросить/проверить пакеты в группе -p, --package запросить/проверить файл пакета --pkgid запросить/проверить пакет(ы) по идентификатору пакета --hdrid запросить/проверить пакет(ы), по идентификатору заголовка -q, --query режим запроса rpm ... ``` Так и не нашлось в этой большой простыне вывода указаний, как с чистого листа создать базу данных пакетов RPM. Но традиционный `man rpm` нас удачно спасает. Итого получаем команду: ``` $ rpm -vv --initdb D: PRAGMA secure_delete = OFF: 0 D: PRAGMA case_sensitive_like = ON: 0 D: PRAGMA journal_mode = WAL: 0 D: PRAGMA wal_autocheckpoint = 10000: 0 D: CREATE INDEX IF NOT EXISTS 'Name_key_idx' ON 'Name'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Basenames_key_idx' ON 'Basenames'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Basenames_hnum_idx' ON 'Basenames'(hnum ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Group_key_idx' ON 'Group'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Requirename_key_idx' ON 'Requirename'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Requirename_hnum_idx' ON 'Requirename'(hnum ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Providename_key_idx' ON 'Providename'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Providename_hnum_idx' ON 'Providename'(hnum ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Conflictname_key_idx' ON 'Conflictname'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Conflictname_hnum_idx' ON 'Conflictname'(hnum ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Obsoletename_key_idx' ON 'Obsoletename'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Obsoletename_hnum_idx' ON 'Obsoletename'(hnum ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Triggername_key_idx' ON 'Triggername'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Triggername_hnum_idx' ON 'Triggername'(hnum ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Dirnames_key_idx' ON 'Dirnames'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Dirnames_hnum_idx' ON 'Dirnames'(hnum ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Sha1header_key_idx' ON 'Sha1header'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Filetriggername_key_idx' ON 'Filetriggername'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Filetriggername_hnum_idx' ON 'Filetriggername'(hnum ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Transfiletriggername_key_idx' ON 'Transfiletriggername'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Transfiletriggername_hnum_idx' ON 'Transfiletriggername'(hnum ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Recommendname_key_idx' ON 'Recommendname'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Recommendname_hnum_idx' ON 'Recommendname'(hnum ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Suggestname_key_idx' ON 'Suggestname'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Suggestname_hnum_idx' ON 'Suggestname'(hnum ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Supplementname_key_idx' ON 'Supplementname'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Supplementname_hnum_idx' ON 'Supplementname'(hnum ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Enhancename_key_idx' ON 'Enhancename'(key ASC): 0 D: CREATE INDEX IF NOT EXISTS 'Enhancename_hnum_idx' ON 'Enhancename'(hnum ASC): 0 D: PRAGMA synchronous = FULL: 0 D: PRAGMA optimize: 0 D: PRAGMA wal_checkpoint = TRUNCATE: 0 ``` Одной опции `-v` оказалось недостаточно и пришлось удвоить, чтобы посомтреть, из каких деталей состоит БД RPM. Файла расположения утилиты при создании почему-то не указала... Но, полагаю, есть другие методы. Методы поиска по командной строке и макросы не дали эффекта, зато команда ``` $ ls -ltra ~/ ``` Показала, что в домашней папке оказался интересны подкаталог `.rpmdb`. Полагаю, что это и есть та самая СУБД с пакетами. Перейдём в неё и осмотримся: ``` cd ~/.rpmdb file * rpmdb.sqlite: SQLite 3.x database, last written using SQLite version 3045001, writer version 2, read version 2, file counter 2, database pages 52, cookie 0x30, schema 4, UTF-8, version-valid-for 2 rpmdb.sqlite-shm: SQLite Write-Ahead Log shared memory, counter 0, page size 0, 0 frames, 0 pages, frame checksum 0, salt 0, header checksum 0x6180738, read-mark[1] 0xffffffff rpmdb.sqlite-wal: empty ``` Два из трёх файлов имеют формат SQLite-3. Хотя ещё недавно, там лежали текстовые файлы и их индексы в виде двоичных Berkley DB. Посмотрим состав таблиц первого из них: ``` $ sqlite3 rpmdb.sqlite .tables Basenames Name Sigmd5 Conflictname Obsoletename Suggestname Dirnames Packages Supplementname Enhancename Providename Transfiletriggername Filetriggername Recommendname Triggername Group Requirename Installtid Sha1header ``` Как видим, имена совпадают, с теми, которые писала утилита rpm при создании базы. Очевидно, что для того, чтобы в домашнем каталоге разворачивать инфраструктуру программ со всеми их данными под конкретного пользователя стандартные пакеты, с гвоздями прибитыми к корню файловой системы элементами не подойдут. Придётся пытаться собирать свои. ### Сборочная структура RPM под установку в домашний каталог Помочь нам должна в этом не утилита rpmbuild, но и rpmlint. Но и это ещё не всё. Чтобы искать по точным названиям утилит содержащие их DEB-пакеты поставим: но и rpmlint. Но и это ещё не всё. Чтобы искать по точным названиям утилит содержащие их DEB-пакеты поставим: ``` $ sudo apt install apt-file $ sudo apt-file update $ sudo apt-file search '/usr/bin/rpm' libsolv-tools: /usr/bin/rpmdb2solv libsolv-tools: /usr/bin/rpmmd2solv libsolv-tools: /usr/bin/rpms2solv rpm: /usr/bin/rpm rpm: /usr/bin/rpmbuild rpm: /usr/bin/rpmdb rpm: /usr/bin/rpmgraph rpm: /usr/bin/rpmkeys rpm: /usr/bin/rpmlua rpm: /usr/bin/rpmquery rpm: /usr/bin/rpmsign rpm: /usr/bin/rpmspec rpm: /usr/bin/rpmverify rpm2cpio: /usr/bin/rpm2archive rpm2cpio: /usr/bin/rpm2cpio rpmlint: /usr/bin/rpmdiff rpmlint: /usr/bin/rpmlint ``` Доустанавливаем доступные пакеты: ``` $ sudo apt install libsolv-tools rpm rpm2cpio rpmlint Чтение списков пакетов… Готово Построение дерева зависимостей… Готово Чтение информации о состоянии… Готово Уже установлен пакет rpm самой новой версии (4.18.2+dfsg-2.1build2). Уже установлен пакет rpm2cpio самой новой версии (4.18.2+dfsg-2.1build2). Будут установлены следующие дополнительные пакеты: appstream-util libappstream-glib8 libsolv1 libsolvext1 python3-enchant python3-magic python3-packaging python3-pybeam python3-rpm python3-tomli-w python3-zstandard Предлагаемые пакеты: python-zstandard-doc Следующие НОВЫЕ пакеты будут установлены: appstream-util libappstream-glib8 libsolv-tools libsolv1 libsolvext1 python3-enchant python3-magic python3-packaging python3-pybeam python3-rpm python3-tomli-w python3-zstandard rpmlint Обновлено 0 пакетов, установлено 13 новых пакетов, для удаления отмечено 0 пакетов, и 0 пакетов не обновлено. Необходимо скачать 1 492 kB архивов. После данной операции объём занятого дискового пространства возрастёт на 5 464 kB. Хотите продолжить? [Д/н] ... ``` Ключевым словом правильной инфраструктуры является слово `prefix`: ``` $ rpm --showrc |grep -i prefix ``` Как видно от префикса отстраиваются и файлы исполнимые и библиотеки и конфиги ... Обычно оно ставится, как `/usr/`. Но нам такое не подходит. Нужно от домашнего каталога. И чтобы оно было в конфигурационном файле `~/.rpmmacros`. ```sh %_prefix %(bash -c 'echo ~/opt') ``` После этого префикс и многие от него зависимые каталоги поменяются. Такая замысловатая конструкция нужна для того, чтобы правильно интерпретировать знак тильды `~` в макросах через оболочку `bash` в каталог по полному пути от корня (`/`). В имеющемся дистрибутиве нет ни пакета rpmdevtools, ни утилит rpmdev-setuptree, ни rpmdev-newspec. Несмотря, на некоторые другие утилиты на базе и около основного базового репозитория rpm софта. Но отвлекаться не будем, а в-ручную создадим сборочную структуру подкаталогов: ``` $ cd ~ $ mkdir -pv rpmbuild/{BUILD,BUILD,RPMS,SOURCES,SPECS,SRPMS} mkdir: создан каталог 'rpmbuild' mkdir: создан каталог 'rpmbuild/BUILD' mkdir: создан каталог 'rpmbuild/RPMS' mkdir: создан каталог 'rpmbuild/SOURCES' mkdir: создан каталог 'rpmbuild/SPECS' mkdir: создан каталог 'rpmbuild/SRPMS' ``` Далее будем делать простейший скрипт для RPM-пакета и спек-файл для его сборки. ``` $ cd /dev/shm $ d=hello-0.0.1 $ mkdir -pv $d && cd $d $ cat > hello.sh < ~/rpmbuild/SPECS/hello.spec < - 0.0.1 - First version being packaged EOT $ rpmbuild -bs ~/rpmbuild/SPECS/hello.spec Записан: /home/dron/rpmbuild/SRPMS/hello-0.0.1-1.src.rpm ``` Знак доллара экранирован обратным слешем для того, чтобы переменную `RPM_BUILD_ROOT` не интерпретировать пока она не попала под дейстиве команды `rpmbuild`. Последняя собрала исходный файл `rpm` за счёт переданной опции `-bs`. Если передать `-bb` - соберётся целевой (двоичный) rpm-файл, который и можно будет ставить в систему. Наша новая RPM-Система не понимает, что у нас уже есть интерпретатор bash. В базе данных её такой не зарегистрирован, а про глобальную она ничего не знает. По этому воспользуемся флагом с установкой без зависимостей: ``` $ rpm --nodeps -iv ~/rpmbuild/RPMS/noarch/hello-0.0.1-1.noarch.rpm rpm: RPM should not be used directly install RPM packages, use Alien instead! rpm: However assuming you know what you are doing... Verifying packages... Подготовка пакетов... hello-0.0.1-1.noarch ``` Запускаем свежеиспечённый файл из нашего пакета: ``` $ ~/opt/bin/hello.sh Hello, Юніксоїд ``` ## Источники * [How to create a Linux RPM package](https://www.redhat.com/en/blog/create-rpm-package) * [Macro syntax](https://rpm.org/docs/4.20.x/manual/macros) * [Packaging and distributing software](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/10/pdf/packaging_and_distributing_software/Red_Hat_Enterprise_Linux-10-Packaging_and_distributing_software-en-US.pdf), Red Hat Enterprise Linux 10