RPM –
Red Hat Package Manager, система управления программным обеспечением,
живущая и развивающаяся в недрах Red Hat. Имеет место также
рекурсивное определение RPM в стиле GNU is not UNIX – RPM
Package Manager. Если выбрать этот вариант, следовало бы написать:
«живет и развивается в недрах Open Source». В общем, кому
что нравится. Если же задеть формальную сторону, RPM лицензирован под
GPL v.2.
Наш
интерес к этой системе обусловлен существованием множества rpm-based
дистрибутивов, то есть дистрибутивов, являющихся коллекциями
rpm-пакетов, увязанных непротиворечивыми зависимостями и снабженных
необходимыми механизмами установки-сопровождения. Среди
дистрибьюторов rpm-based операционных систем – гранды
осестроения, такие, как сама Red Hat, Suse (ныне подразделение
Novell), Mandrake и множество других проектов, и долгоживущих, и
однодневок.
И в
составе дистрибутивов и в виде отдельных разработок доступно большое
количество графических утилит-фронтэндов к rpm.
В силу сокрытия, как это нередко бывает, некоторых особенностей
работы системы пакетирования этими утилитами, мы их рассматривать не
будем. Кроме того, в подавляющем большинстве ситуаций работа в
консоли в отношении rpm-пакетов существенно проще и прозрачней (за
исключением сложных сочетаний зависимостей различных версий одних
программ от других).
В
настоящей статье сделана попытка рассмотреть такие важные темы, как
формат команд утилиты rpm, формат пакета и spec-файла,
последовательность сборки пакетов и практически совсем не затронута
архитектура RPM, так как администраторам эти подробности не важны, а
разработчики в основном используют интерфейсы вроде библиотеки
librpm. Поэтому в лучших традициях
отсылаем «интересующихся знать», как это устроено внутри,
к исходному коду.
Для
исследования rpm (не RPM) из
инструментов потребуется Midnight Commander. Честно говоря, сложно
представить себе разработчика rpm-пакетов, который не пользуется mc.
О причинах этого поговорим далее. Из дополнительных условий следует
назвать некоторое количество свободного времени и терпения.
В
качестве предполагаемого читателя автор представляет себе
относительных новичков в мире Linux или специалистов, не использующих
rpm-дистрибутивы.
Все
примеры запускались на ОС Suse Linux 10.0.
Поверхностный взгляд.
Утилита
rpm предоставляет простой командный интерфейс, с помощью которого
легко управлять программным обеспечением. Одна команда для установки
пакета, одна для удаления, одна для верификации. Что может быть
проще! Если Вы часто собираете и устанавливаете программное
обеспечение из тарбалов, Вам, без сомнения, известно, как трудно
держать в голове все многообразие проделанных действий. И как нелегко
бывает удалить их (этих действий) следы! Далее, как говорится в
рекламных агитках, «все Ваши проблемы возьмет на себя rpm».
Не хочется вызывать религиозных споров по поводу: «А вот в
Debian, да с помощью apt-get, даже не придется указывать версию
пакета и все зависимости удовлетворятся автоматом, так что идите со
своим rpm...». Все так. Признаю достоинства deb-пакетов и
утилит, автоматизирующих действия с ними. Однако только в ситуации,
когда сравнивается rpm как таковой с
развитыми движками. Ведь на базе rpm
работают такие инструменты, как yum,
которые тоже это все умеют. Поэтому сравнение закончим на сравнении
управления множеством разных программ на множестве машин из тарбалов
и из rpm-пакетов. Так постепенно мы подошли к главному свойству RPM,
вытекающему из основной его задачи – система автоматизирует
большое количество рутинных операций. Важной характеристикой системы
является возможность «апгрейдить» пакеты, причем
соответственные файлы новых версий затирают старые файлы прозрачно
для пользователя.
Поскольку
rpm избавляет пользователя от
необходимости держать все подробности об установленном в операционной
системе ПО, должен быть механизм, выполняющий эти функции внутри
пакетного менеджера. И он есть: это база данных rpm.
В версиях младше v.4 движок базы статически собирался в составе
пакета, v.4 использует внешнюю BDB (Berkeley Data Base). Утилита
имеет высокоуровневый командный интерфейс, позволяющий производить
запросы к базе об установленных пакетах и их зависимостях.
Итак,
приступим к примерам. Утилита rpm
может работать в разных режимах, режимы задаются значением основного
ключа команды. Кроме того, имеется ряд опций, действующих в любых
режимах.
Для
установки пакета используется такой формат команды:
# rpm -i <имя
пакета>
И тут мы
сталкиваемся с первым неудобством, так как имя пакета должно
приводится полностью, вместе со всеми его версиями и номерами сборок.
Дело в том, что утилита просто ищет по указанному пути файл с таким
именем, поэтому имя должно совпадать в точности. Например, в моей
системе для установки mc придется
сказать следующее (предполагается наличие прав суперпользователя):
# rpm -i /путь
к репозиторию/mc-4.6.1-5.i586.rpm
А вот
для того, чтобы удалить пакет, можно указать в качестве имени пакета
просто mc, так как в этом случае
утилита уже обращается к базе данных пакетов:
# rpm -e mc
Для
работы в режиме апгрейда пакетов используется ключ -U.
Если требуется установить пакет определенной версии, не обязательно
выяснять, имеются ли предыдущие версии пакета в системе, достаточно
использовать -U. Имеющиеся версии
утилита освежит, а в случае отсутствия пакета – установит его.
Поэтому наиболее стандартным считается вариант команды установки,
подобный следующему:
# rpm -Uhv
/путь к репозиторию/mc-4.6.1-5.i586.rpm
или из
сетевого репозитория:
# rpm -Uhv
ftp://ivanov:secret@192.168.5.101:7020/путь к
репозиторию/mc-4.6.1-5.i586 .rpm
где
опция -v означает многословность
вывода диагностики, -h – вывод
прогресса установки, ivanov –
логин пользователя ftp, secret
– его пароль, 192.168.5.101:7020
– IP-адрес и порт сервера (или URL).
Еще два
интересных режима: верификация и режим запросов. Для оценки
возможностей верификации установленных пакетов произведем следующий
эксперимент: повредим файл какого-нибудь пакета и проверим его с
помощью rpm.
Например,
почти всегда в списке команд каталога /bin
первым идет исполняемый файл arch.
Эта утилита выводит информацию о процессорной архитектуре системы на
стандартный вывод. Чтобы узнать, из состава какого пакета этот файл,
воспользуемся режимом запроса, в котором rpm
запускается с помощью ключа -q. Опция
-f спросит, к какому пакету относится
данный файл:
# rpm -qf
/bin/arch
util-linux-2.12q-26
Теперь
создадим резервную копию программы, а затем добавим в файл один
символ:
# cp /bin/arch
/bin/arch_back
# echo "1"
>> /bin/arch
После
чего механизм верификации пакета должен нам сообщить о существующих
проблемах:
# rpm -V
util-linux-2.12q-26
S.5....T
/bin/arch
В строке
вывода перед названием файла пакета появляются некие символы (если
верификация успешна и повреждений нет, вывода не будет), которые
указывают на характер неисправностей. В данном случае s
означает изменение размера файла, 5 –
нарушение сигнатуры md5 файла, T –
изменение времени последней модификации (то есть времени копирования
файла в систему в нашем случае).
Восстановим
файл и вновь проведем проверку:
# rm /bin/arch
# mv
/bin/arch_back /bin/arch
# rpm -V
util-linux-2.12q-26
.......T
/bin/arch
и
увидим, что все у нас хорошо, кроме времени последней модификации
файла /bin/arch, которое будет
соответствовать времени обратного копирования. Такая информация
помогает администратору выявить некоторые проблемы и пакеты –
кандидаты на переустановку.
Расширенные возможности.
Помимо
режимов работы, утилита rpm имеет
великое множество опций, одни из которых привязаны к определенному
режиму, другие имеют смысл в нескольких режимах, или во всех.
Несколько примеров часто используемых опций.
В
режимах установки-удаления довольно часто возникает необходимость
воспользоваться опциями --nodeps или
--force. Первая позволяет установить
(удалить) пакет независимо от того, удовлетворяются ли все его
зависимости, вторая – установить пакет даже в том случае, если
в системе имеются файлы более свежих версий. Некоторый интерес
представляют опции --aid, которая
автоматически удовлетворит возникающие зависимости и --test,
которая и означает тестирование операций, то есть весь вывод о
возникающих проблемах будет осуществлен, но реальных операций не
производится. Очень удобно моделировать поломку системы в результате
каких-либо действий.
Большую
коллекцию возможностей предоставляют опции режима запросов. О пакете
и его файлах можно получить практически любую информацию, начиная с
краткой справки о пакете (сочетание ключей -qi)
и списка файлов пакета (-ql) и
заканчивая значением некоторых служебных полей бинарного заголовка
пакета. Эти возможности реализуются либо с помощью соответственных
ключей-фильтров, либо посредством опции --queryformat,
которая позволяет вывести только заказанные служебные поля. Например,
команда
# rpm -q
--queryformat %{DESCRIPTION} <имя пакета>
выведет
описание пакета, а команда
# rpm -q
--queryformat %{DISTRIBUTION} <имя пакета>
– название
дистрибутива, в составе которого находится пакет. Причем замена -q
на -qp позволит ту же самую
информацию получить от rpm-файла не установленного в систему пакета.
Можно получить список только конфигурационных файлов пакета, или
только файлов документации, или только файлов, содержащих в имени
регулярное выражение, то есть в принципе что угодно.
Очень
часто (лень – двигатель прогресса) используются запросы, вывод
которых перенаправляется во внешние фильтры. Так, запрос о всех
установленных файлах, перенаправленный в grep,
поможет найти пакет с заданным именем, или с именем, содержащим
заданную последовательность символов. Команда
# rpm -qa |
grep mc
выведет
список пакетов, в именах которых встречается сочетание mc, а команда
# rpm -qa |
grep ^mc
список
пакетов, имена которых начинаются на mc.
За
недостатком места мы не обсуждаем такие экзотические, но важные для
разработчика опции, как, например, --showrc,
которая позволяет вывести содержимое файлов скриптов и макросов из
файлов rpmrc и macros
на стандартный вывод или в файл, и многие другие. Следует заметить,
что rpm снабжен подробнейшей
документацией man.
Что внутри.
Несколько
слов о том, что происходит в процессе установки и удаления пакета.
Как уже отмечалось, rpm
автоматизирует рутинные операции. При установке (удалении) пакетов
любые, сколь угодно сложные действия по настройке установленных
пакетов, или удалении следов деятельности пакетов удаляемых, можно
поместить в скрипты. В зависимости от положения этих скриптов в
канонической последовательности действий, например, по установке
пакета, эти скрипты называются преинсталляционными или
постинсталляционными. Стандартная последовательность операций,
инициализируемых rpm при установке
пакета, такова:
проверяются
зависимости;
проверяются
возможные конфликты (наиболее частый вариант конфликта – в
системе установлен одноименный пакет более свежей версии);
обрабатываются
конфигурационные файлы;
копируются
бинарные файлы в нужные каталоги;
выполняются
постинсталляционные скрипты;
обновляется
база данных пакетов.
Формат rpm-пакета.
Формат
пакета состоит из бинарного заголовка и cpio-архива, который содержит
бинарные файлы в таком дереве каталогов, в каком они будут находится
в системе после установки пакета. Файловый менеджер mc
понимает множество всяких архивов, и в том числе – упаковку
rpm. Если в панели mc выделить
rpm-пакет и нажать ввод, мы увидим псевдофайловую систему, состоящую
из следующих компонентов: каталога INFO, архива CONTENTS.cpio, того
самого, содержащего бинарные файлы, файла HEADER и псевдоскриптов
INSTALL и UPGRADE. В каталоге INFO содержатся файлы, имена которых
соответствуют именам полей spec-файла, содержимое – значениям
полей. Файл HEADER – по сути то же самое, только в одном файле.
Ссылки INSTALL и UPGRADE соответствуют командам rpm
-ih <имя пакета> и rpm -Uh <имя
пакета>. То есть, если на них нажать, эти действия и
произойдут.
В
реальном формате никакой файловой системы нет. Просто mc
умеет по-своему интерпретировать бинарный заголовок пакета, за что
его разработчикам большой респект.
При
желании можно выделить cpio-архив из всего пакета. Для этого
существует утилита rpm2cpio.
Соберем пакет.
В rpm
версии v.4 режим сборки пакета оформлен в виде отдельной утилиты –
rpmbuild. Воспользуемся самым
эффективным методом изучения технологии, то есть, соберем модельный
rpm-пакет. Нет и вопроса, что должна делать программа, которую мы
упакуем в rpm. Она должна говорить:
«Hello, world!»!
В
rpm-based дистрибутивах существует специальное дерево каталогов,
предназначенное исключительно для сборки пакетов. Оно лежит в
/usr/src (в Suse Linux – в
/usr/src/packages) и содержит
каталоги BUILD, RPMS, SOURCES, SPECS, SRPMS. Предназначены они
соответственно для хранения временных каталогов сборки, собранных
бинарных rpm, исходного кода, хранения файлов спецификации, собранных
src.rpm-пакетов. Src.rpm содержат исходный код и spec-файлы и
предназначены для пересборки на целевых машинах с целью лучшей
адаптации к архитектуре и системному окружению этих машин. Для сборки
нам потребуется исходный код программы, который традиционно
упаковывается в tar.gz или в tar.bz2 и spec-файл. Spec-файл для rpm
примерно то же, что Makefile для утилиты make. Это подробнейший
сценарий того, что должно происходить при сборке со всеми
необходимыми определениями и служебными полями. Итак, за дело.
Создадим
текст программы на С. Файл назовем hi.c и поместим его в каталог
SOURCES. Отредактируем содержание файла в любимом текстовом редакторе
следующим образом:
#include
<stdio.h>
int main(int
argc, char **argv)
{
fprintf(stdout,"Hello,
World!\n");
return 0;
}
Не
забудем пустую строку в конце файла. Запакуем исходный код в tar.gz
(команду отдаем, находясь в каталоге /usr/src/packages/SOURCES):
# tar cvfz
./hi-0.1.tar.gz ./hi.c
Далее
spec-файл. По сути дела, умение создавать хорошо пересобираемые
rpm-пакеты – это умение писать spec-файлы. Они имеют сложную
структуру, подробности которой рассмотреть не представляется
возможным в журнальной статье, поэтому обсудим главное. Файл делится
на секции, каждая секция отвечает за свою часть работы. Создадим файл
под именем hi.spec в каталоге SPECS и
наполним его следующим содержанием:
Summary:
Приветствующая утилита.
Name: hi
Version: 0.1
Release: 1
Copyright: GPL
v.2
Group: Tests
Source:%{name}-%{version}.tar.gz
BuildRoot:
/tmp/hi
%description
Тестовая
программка для вывода приветствия.
%prep
%setup -c hi
%build
gcc -o hi hi.c
%install
mkdir -p
$RPM_BUILD_ROOT/usr/local/bin
cp hi
$RPM_BUILD_ROOT/usr/local/bin
%clean
rm -rf
$RPM_BUILD_ROOT
%files
/usr/local/bin/hi
Проанализируем
функции отдельных секций spec-файла.
Все, что
написано до строки %prep, составляет секцию introduction. Она
содержит описания, определения версий и релизов, корень сборки и
другую служебную информацию. Значения всех этих полей используются
через имена полей в качестве переменных при сборке и включаются в
одноименные поля бинарного заголовка rpm-пакета.
Секция
prep отвечает за подготовку исходного кода к сборке. В нашем примере
секция содержит строку %setup -c hi,
которая вызывает макрос RPM. Макрос, в свою очередь, распаковывает
исходный текст Си из архива.
Секция
build содержит инструкции по сборке. В данном случае мы указали имя
выходного бинарного файла – hi.
Если проект большой, как правило должен быть сгенерирован Makefile,
поэтому секция может содержать нечто подобное:
./configure
CXXFLAGS=-03 –prefix=$RPM_BUILD_ROOT/usr
make
Секция
install включает операции по установке программы в систему. В
развитых проектах она может содержать весьма обширные списки
действий. Или, например:
rm -fr
$RPM_BUILD_ROOT
make install
В нашем
проекте нет Makefile, поэтому мы указали все непосредственно.
Секция
clean содержит команды удаления каталогов сборки.
Секция
files – список файлов проекта.
Итак, у
нас имеется исходный код в виде tar.gz-архива и spec-файл. Больше для
сборки пакета ничего не требуется. Естественно, в системе должны быть
установлены средства разработки.
Для
сборки пакета вызываем утилиту rpmbuild
с ключом -b. Второй символ набора
ключей означает цель сборки. Опция -a
указывает на необходимость собрать как rpm-пакет, так и src.rpm.
Укажем также целевую архитектуру. Находясь в каталоге SPECS, отдадим
следующую команду:
rpmbuild -ba
--target i586 ./hi.spec
Если в
последней строке консольного вывода Вы увидите: +
exit 0, значит все прошло как надо. В каталоге RPMS/i586
должен обнаружиться файл
hi-0.1-1.i586.rpm,
в каталоге SRPMS – файл hi-0.1-1.src.rpm.
Воспользуемся уже имеющимися знаниями, и установим пакет hi
в систему:
# cd
/usr/src/packages/RPMS/i586
# rpm -Uhv
./hi-0.1-1.i586.rpm
Теперь,
если Вы скажете в консоли:
$ hi
получите
ответ: Hello, World!
Для
установки src.rpm-пакета воспользуйтесь командой:
# rpm -i
/путь к каталогу/hi-0.1-1.src.rpm
При этом
в систему скопируются исходный код и spec-файл (в то самое дерево
каталогов для rpm-сборки). Если нужно просто собрать бинарный пакет,
достаточно сказать:
# rpmbuild
--rebuild /путь к каталогу/hi-0.1-1.src.rpm
Вот,
собственно, для начала и все.