Automating UNIX and Linux Administration
by Kirk Bauer
Red Hat Package Manager (RPM)
Если вы не имеете понятия о создании дистрибутивов , то вы обычно поступаете так :
вы компилируете какую-то программу на своей машине , затем собираете все в архив ,
копируете этот архив на другую машину , извлекаете все из архива и запускаете там
make install . Через какое-то время вы удаляете инсталляционный каталог вместе с архивом ,
а потом возникает необходимость установить его еще раз на новой удаленной машине ,
и вы не знаете , что делать .
Через некоторое время выходит новая версия программы , вам нужно опять все откомпилировать
у себя и разослать новый архив , и там опять делать make install . Если компиляция составляет
несколько часов и более , это может привести к серьезным проблемам .
Эти проблемы решаются в RPM. Он обеспечивает гибкое управление пакетов и их версий .
В RPM существует специальный файл-сценарий - SPEC - в котором фиксируются все изменения
при генерации пакета .
Перед тем как построить первый RPM , нужно подготовить систему . Начинать нужно с каталога
исходников , в котором также будут присутствовать SPEC-файлы .
Базовым каталогом мы выберем ~/rpmroot. Внутри ~/rpmroot создадим подкаталоги :
BUILD/, RPMS/, SOURCES/, SPECS/, и SRPMS/.
Также необходим виртуальный инсталяционный каталог - пусть это будет ~/tmp/.
Далее в своем домашнем каталоге ~/ создаем файл .rpmmacros .
В данном примере имеется ввиду , что пользователь не root , а build ,
и его домашний каталог - /home/build/. Надо сказать , что некоторые пакеты можно построить
только из-под рута .
Содержание его может быть следующим :
%_topdir /home/build/rpmroot
%buildroot /home/build/tmp/build
%packager Kirk Bauer <kirk@kaybee.org>
%_tmppath /home/build/tmp
На вашей системе должны стоять rpm , rpm-build , я уже не говорю про gcc и make .
Начнем с создания простого пакета . Для начала создадим файл test.spec в подкаталоге /SPECS/.
Для того , чтобы с его помощью построить пакет , нужно с этого подкаталога запустить :
rpmbuild -ba test.spec
В случае успеха , в каталоге SRPMS/ будет создан source RPM ,
в каталоге PMS/noarch/ - "binary" RPM .
noarch - значит не конкретная архитектура .
Вот содержание этого spec-файла :
Name: test
Version: 1.0
Release: 1
Summary: A test package
License: MIT
Group: Test
BuildArch: noarch
%description
This is just a test package
%files
Если BuildArch не определить , система сама поставит i386 или что-то другое .
Секция %files пуста ради простоты .
Теперь строим пакет :
rpmbuild -ba test.spec
...
Wrote: /home/build/rpmroot/SRPMS/test-1.0-1.src.rpm
Wrote: /home/build/rpmroot/RPMS/noarch/test-1.0-1.noarch.rpm
Создаются две RPM-ки.
Теперь добавим в пакет один файл - /etc/hosts . Для этого в заголовок test.spec
добавим 2 строки :
Buildroot: /var/tmp/test_build
Source: hosts
При этом файл hosts должен лежать в нашем подкаталоге /SOURCES/.
Его можно скопировать туда так :
%prep
cp /etc/hosts $RPM_SOURCE_DIR
Следующей идет секция %install :
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/etc
cp $RPM_SOURCE_DIR/hosts $RPM_BUILD_ROOT/etc/hosts
Секция %files будет выглядеть так :
%files
%attr(0644,root, root) /etc/hosts
Запускаем еще раз
rpmbuild -ba test.spec
Пример gzip-1.3-15.src.rpm
В качестве примера возьмем пакет gzip-1.3-15.src.rpm из редхатовского дистрибутива.
Выполняем :
% rpm -i gzip-1.3-15.src.rpm
% cd ~/rpmroot/SPECS
Рассмотрим подробнее gzip.spec из этого пакета :
Summary: The GNU data compression program.
Name: gzip
Version: 1.3
Release: 15
License: GPL
Group: Applications/File
Source: ftp://alpha.gnu.org/gnu/gzip/gzip-%{version}.tar.gz
Patch0: gzip-1.3-mktemp.patch
Patch1: gzip-1.2.4-zforce.patch
Patch2: gzip-1.2.4a-dirinfo.patch
Patch3: gzip-1.3-stderr.patch
Patch4: gzip-1.3-zgreppipe.patch
Patch5: gzip-1.3-noppid.patch
URL: http://www.gzip.org/
Prereq: /sbin/install-info
Requires: mktemp
Buildroot: %{_tmppath}/gzip-%{version}-root
Интерес здесь представляют секции Prereq: и Requires: , которые показываю зависимости
и указывают на пакеты , которые уже должны быть установлены .
Buildroot: - каталог , необходимый при построении пакета .
Рассмотрим секцию prep :
%prep
%setup -q
%patch0 -p1
%patch1 -p1
%patch2 -p1
%patch3 -p1
%patch4 -p1
%patch5 -p1
Макрос %setup извлекает исходники из пакета , которые перечислены в поле Source ,
и ставит их в каталог /BUILD/ . Макрос %patch0 вызывает патч .
Рассмотрим секцию build :
%build
export DEFS="-DNO_ASM"
export CPPFLAGS="-DHAVE_LSTAT"
%configure --bindir=/bin
make
make gzip.info
С помощью export мы устанавливаем несколько переменных .
Конфигурация устанавливает каталог /bin ,
в который лягут бинарники .
Следующая секция - clean :
%clean
rm -rf $RPM_BUILD_ROOT
Секция install :
%install
rm -rf $RPM_BUILD_ROOT
%makeinstall bindir=$RPM_BUILD_ROOT/bin gzip.info
mkdir -p $RPM_BUILD_ROOT/usr/bin
ln -sf ../../bin/gzip $RPM_BUILD_ROOT/usr/bin/gzip
ln -sf ../../bin/gunzip $RPM_BUILD_ROOT/usr/bin/gunzip
for i in zcmp zegrep zforce zless znew gzexe zdiff zfgrep zgrep zmore ; do
mv $RPM_BUILD_ROOT/bin/$i $RPM_BUILD_ROOT/usr/bin/$i
done
gzip -9nf $RPM_BUILD_ROOT%{_infodir}/gzip.info*
cat > $RPM_BUILD_ROOT/usr/bin/zless <<EOF
#!/bin/sh
/bin/zcat "\$@" | /usr/bin/less
EOF
chmod 755 $RPM_BUILD_ROOT/usr/bin/zless
Относительно нашего buil root создается каталог /usr/bin/ .
Следующая секция - %post :
%post
/sbin/install-info %{_infodir}/gzip.info.gz %{_infodir}/dir
Следующая секция %files :
%files
%defattr(-,root, root)
%doc NEWS README AUTHORS ChangeLog THANKS TODO
/bin/*
/usr/bin/*
%{_mandir}/*/*
%{_infodir}/gzip.info*
Команда %defattr автоматически меняет пермишины на рутовые .
Build Root
Начнем с того , что в ~/.rpmmacros добавляем строку :
%buildroot /home/build/tmp/build
Предположим , что пакет должен проинсталировать программу /usr/bin/program и конфиг
/etc/program.conf . Процес будет таким .
1. Компилируем program
2. Ложим в $RPM_BUILD_ROOT/usr/bin/program и $RPM_BUILD_ROOT/etc/program.conf.
3. В секции files , добавляем /usr/bin/program и /etc/program.conf.
Пример SPEC-файла :
%build
make -DCONFIG_FILE=/etc/program.conf program
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/etc
mkdir -p $RPM_BUILD_ROOT/usr/bin
cp program $RPM_BUILD_ROOT/usr/bin
chmod a+rx $RPM_BUILD_ROOT/usr/bin/program
cp program.conf $RPM_BUILD_ROOT/etc
chmod a+r $RPM_BUILD_ROOT/etc/program.conf
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root, root)
/etc/program.conf
/usr/bin/program.conf
Патчи
Патч создается так : сначала тарбол test-1.0.tgz нужно разместить в каталог SOURCES/.
Затем выполняем 2 команды :
% tar -xzf test-1.0.tgz
% cp -ar test-1.0 test-1.0-orig
После этого заходим в каталог SOURCES/test-1.0/ и делаем изменения в исходниках .
После чего создаем патч :
% diff -uNr test-1.0-orig test-1.0 > test-patch1
% rm -rf test-1.0 test-1.0-orig
Теперь мы имеем патч test-patch1. Нужно в SPEC сделать ссылку на исходник
и на патч :
Source: test-1.0.tgz
Patch0: test-patch1
%prep
%setup
%patch0 -p1
RPM-зависимости
Зависимости не позволяют проинсталлировать пакет ,
если не установлены другие программы.
Иногда это может привести к циклической зависимости ,
когда отсутствующая программа
требует другую отсутствующую программу .
Если пакет использует расшаренные библиотеки ,
RPM автоматически добавит их в пакет .
Эту фичу можно отключить путем AutoProv: no . Можно использовать поле Provides .
В полях Prereq и Requires можно просто перечислить все зависимости ,
необходимые для установки пакета .
При указании пакета , который является предустановленной зависимостью ,
можно указать версию :
Requires: bash
Requires: bash = 2.05a
Requires: bash >= 2.0,
webserver, /bin/ls
Если в SPEC имеется непустое поле Conflicts , то пакет может быть не усстановлен , если
конфликт будет найден в системе .
Опция –nodeps позволяет устанавливать пакет , что называется ,
невзирая на лица и зависимости .
Advanced RPM
Зависимости можно определить в полях BuildPrereqs, BuildRequires,BuildConflicts.
Опции RPM можно задать из командной строки с помощью опции –with , для чего нужно
использовать макрос %_with_option внутри SPEC .
Переменную RPM можно использовать в любом месте SPEC .
Можно определить свою собственную переменную :
%define varname value
Использование такой переменной - %{varname} .
Директива %attr используется для раздачи прав на файлы :
%files
%attr(0644,root, root) /etc/*
%attr(0755,root, root) /usr/bin/*
Если в SPEC задается директива %config , то при инсталляции пакета , старые конфиги
будут сохранены с расширением .rpmorig. Если файл , отмаркированный этой директивой ,
модифицировался после инсталляции , то после удаления пакета этот файл
будет переименован
с расширением .rpmsave .
С помощью директивы %ghost можно указать файлы , которых не будет в пакете , но на них
например могут быть созданы линки .
Файлы для директивы %files можно сохранить в отдельном файле в каталоге /BUILD/ и затем
вызвать в SPEC так : %files -f file.list.
SPEC может включать несколько инсталяционных секций :
%pre - скрипт в этой секции выполняется перед установкой файлов
%post - скрипт в этой секции выполняется после установки файлов
%preu - скрипт вызывается перед апгрэйдом или удалением
%postun - скрипт вызывается после апгрэйда или удаления
Наличие пакета в системе можно проверить с помощью команды
rpm -V pkgname
Файлы пакета можно вывести командой
rpm -Va
Триггеры-специальные скрипты,которые выполняются RPM при инсталяции или удалении .
Существуют 3 типа триггеров :
%triggerin:
%triggerun:
%triggerpostun:
Триггер выполняется с 2-мя аргументами : первый - номер версии устанавливаемого пакета ,
второй - номер версии уже установленного пакета .
В большинстве случаев это параметры 0,1 и 2.
При апгрейде порядок выполнения скриптов следующий :
1. %pre
2. %files
3. %post
4. $triggerin из установленного ранее пакета
5. $triggerin из устанавливаемого пакета
6. %triggerun из установленного ранее пакета
7. %preun из установленного ранее пакета
8. %postun из установленного ранее пакета
9. %triggerpostun из установленного ранее пакета
Работу триггеров можно рассмотреть на следующем примере :
создадим 2 пакета - test1 и test2,и проинсталлируем их.
Затем создадим новую версию пакета test1 и сделаем его апгрэйд .
Пример SPEC для этого случая :
%define other_pkg test2
Name: test1
Summary: Package %{name}
Version: 1.0
Release: 1
License: MIT
Group: Test
BuildArch: noarch
BuildRoot: /var/tmp/%{name}
%description
Description for %{name}
%files
%pre
echo Executing pre in %{name}-%{version}-%{release}: arg=$1
%post
echo Executing post in %{name}-%{version}-%{release}: arg=$1
%preun
echo Executing preun in %{name}-%{version}-%{release}: arg=$1
%postun
echo Executing postun in %{name}-%{version}-%{release}: arg=$1
%triggerin -- %{other_pkg}
echo Executing triggerin in %{name}-%{version}-%{release}: args=$1 $2
%triggerun -- %{other_pkg}
echo Executing triggerun in %{name}-%{version}-%{release}: args=$1 $2
%triggerpostun -- %{other_pkg}
echo Executing triggerun in %{name}-%{version}-%{release}: args=$1 $2
Если вы хотите установить пакет в какой-то конкретный каталог ,
можно набрать :
rpm -i –prefix /some/path
При этом в SPEC должно быть поле :
Prefix: /usr
...
%files
/usr/bin/program
Другой вариант :
Prefix: /usr/local
...
%post
if [ ! "$RPM_INSTALL_PREFIX" == "/" ] ; then
ln -sf "$RPM_INSTALL_PREFIX/etc/program.conf" /etc/program.conf
fi
%postun
rm -f /etc/program.conf
%verifyscript
test -l /etc/program.conf
%files
%conf /usr/local/etc/program.conf
/usr/local/bin/program
Возможен такой вариант :
% rpm -i --relocate /usr=/usr/local /etc=/usr/local/etc test.rpm
Основные опции команды rpm
Команда | Действие |
rpm -i (package) | Установка пакета |
rpm -iv (package) | Установка пакета с распечаткой результатов |
rpm -ivh(package) | Установка пакета с распечаткой результатов + текущий процент |
rpm -ivv (package) | Установка пакета с подробной распечаткой результатов |
rpm -ivv --nodeps (package) | Установка пакета , игнорируя все зависимости |
rpm -qp (package) | Распечатка имени пакета |
rpm -qip (package) | Вывод более подробной информации о пакете |
rpm -qlp (package) | Вывод списка файлов в пакете |
rpm -qa | Показывает все пакеты , установленные в системе |
rpm -Va | Вывод краткой информации о каждом пакете |
rpm -U (package) | Upgrade уже установленного пакета или инсталляция нового |
rpm -F (package) | Upgrade уже установленного пакета лишь в том случае , если он уже установлен |
rpm -e (package) | Удаление пакета вместе с зависимостями |
rpm -e --nodeps (package) | Удаление пакета игнорируя зависимости |
|