Руководство пользователя и разработчика RPM
Версия для печати
(Многостраничная версия)Red Hat RPM Guide - русский перевод
RPM Guide
Eric Foster-Johnson
Copyright © 2005 Eric Foster-Johnson
От переводчика:
Данный текст в его исходном варианте выложен Red Hat под лицензией Open Publication License. Надеюсь, что эта лицензия подразумевает возможность самодеятельного перевода с указанием авторов и правообладателей. Если это, паче ожидания, не так, сообщите мне, пожалуйста, об этом на
vlad_goreletsky(at)lexpr.ru
Там, где обсуждается система пакетного менеджмента, обычно употребляется аббревиатура RPM. Там, где речь идет о пакетах или утилите rpm, используется rpm или src.rpm
Замечания по орфографии принимаются. Замечания по стилю не принимаются. Любой из вас может сделать это лучше, если знает как.
В тексте по отношению к оригинальному множество сокращений. Много слов иногда ничего не добавляют к ЗНАНИЮ. Тут возможны претензии, но таков уж мой произвол. Считаю, что потенциальные читатели в основном люди занятые.
Влад Горелецкий
Источник: http://www.lexpr.ru/node/11
Полное руководство Red Hat Package Manager
Раздел 1. Введение в RPM
1.1 Необходимость системы управления пакетами
1.2 Цели разработки RPM
1.2.1 Легкость использования
1.2.2 Ориентирование на понятие "пакет"
1.2.3 Возможность обновления пакетов
1.2.4 Межпакетные зависимости
1.2.5 Возможность запросов
1.2.6 Верификация пакетов
1.2.7 Поддержка различных архитектур
1.2.8 "Чистые" исходные коды
1.3 Терминология RPM
Раздел 2. Обзор RPM
2.1.1 Введение в структуру файла пакета
2.1.2 Формат файла rpm
2.1.3 Бинарные rpm и rpm с исходным кодом
2.2 База данных RPM
2.3 Команды RPM
Раздел 3. Использование RPM
3.1 Утилита rpm
3.2 Установка и обновление пакетов
3.2.1 Режим установки
3.2.2 Режим обновления
3.2.3 Обновление в режиме freshen
3.2.5 Установка через сеть
3.2.6 Установка пакетов с исходным кодом
3.3 Удаление пакетов
3.4 Другие опции rpm
Раздел 4. Использование базы данных RPM
4.1 Запросы к БД RPM
4.1.1 Запросы о пакетах
4.1.3 Как повысить информативность запроса
4.1.4 Какому пакету принадлежит файл?
4.2 Получение информации о пакетах
4.2.1 Описание пакета
4.2.2 Группы пакетов
4.2.3 Список файлов пакета
4.2.4 Список конфигурационных файлов пакета
4.2.5 Список файлов документации пакета
4.2.6 Список статуса файлов пакета
4.2.7 Список скриптов
4.2.8 Список изменений
4.2.9 Комбинирование запросов
4.2.10 Пользовательские запросы
4.2.11 Прочие запросы
4.3 Получение информации из файла rpm-пакета
4.4 Верификация установленных пакетов
4.4.1 Верификация системы в целом
4.4.2 Настройка проверок
4.5 Работа с БД RPM
4.5.1 Создание резервной копии БД RPM
4.5.2 Перестройка БД RPM
4.5.3 Создание новой БД RPM
Раздел 5. Зависимости пакетов
5.1 Введение в концепцию зависимостей
5.1.1 Возможности
5.1.2 Зависимости версий
5.1.3 Конфликты
5.1.4 Неактуальные возможности
5.2.1 Проверка зависимостей вида Requires
5.2.2 Проверка зависимостей вида Provides
5.2.3 Проверка на конфликты
5.2.4 Какой пакет требует данную возможность?
5.2.5 Какой пакет предоставляет данную возможность?
5.3 Триггеры
Раздел 6. Транзакции
6.1 Введение в транзакции
6.1.1 Когда транзакции необходимы
6.1.2 Возврат транзакции
6.2 Транзакции с командой rpm
6.2.1 Идентификаторы транзакций
6.2.2 Откат транзакций
6.3 Сохранение старых пакетов
Раздел 7. Программное обеспечение для управления RPM
7.1 Нахождение программного обеспечения в формате rpm-пакетов
7.1.1 rpmfind и rpm2html
7.2 Сайты, посвященные RPM, в Интернете
7.3 Утилиты расширенного менеджмента пакетов
7.3.1 Yum
7.3.2 Рекомендуемые плагины yum
7.3.3 Графические фронтенды программ управления пакетами
Раздел 8. Создание rpm-пакетов: обзор
8.1 Подготовка к сборке пакета
8.1.1 Планирование - что хотим собрать
8.1.2 Консолидация программного обеспечения
8.1.3 Воспроизводимая сборка ПО
8.1.4 Планирование обновлений
8.1.5 Удовлетворение зависимостей
8.2 Сборка rpm-пакета
8.2.1 Разворачивание структуры директорий
8.2.2 Размещение исходного кода в дереве сборки
8.2.3 Создание spec-файла
8.2.4 Сборка пакета с помощью утилиты rpmbuild
8.2.5 Верификация собранных пакетов
Раздел 9. Работа со spec-файлом
9.1 Чтение spec-файла
9.2 Начинаем создавать spec-файл
9.3 Информация о пакете в spec-файле
9.3.1 Описание пакета
9.3.2 Установка пути сборки
9.3.3 Имена файлов архивов с исходным кодом
9.3.4 Имена патчей
9.4 Контроль сборки
9.4.1 Подготовка к сборке
9.4.2 Сборка ПО
9.4.3 Инсталляция ПО
9.4.4 Очистка после сборки
9.4.5 Определение установочных скриптов
9.5 Заполнение списка файлов
9.5.1 Использование шаблонов
9.5.2 Имена каталогов
9.5.3 Пометка файлов как файлов документации или конфигурационных
9.5.4 Определение атрибутов файлов
9.5.5 Верификация секции %files
9.5.6 Автоматизированное создание списка файлов
9.5.7 Обработка ошибок для неупакованных файлов
9.6 Добавление записей в журнал изменений
9.7 Макросы
9.7.1 Встроенные макросы
9.7.2 Макросы, специфичные для spec-файла
9.7.3 Определение нового макроса
9.7.4 Параметры макросов
9.8 Создание spec-файла в XML формате
Раздел 10. Расширенные возможности RPM
10.1 Зависимости пакета
10.1.1 Имена зависимостей
10.1.2 Установка предварительных требований
10.1.3 Зависимости сборки
10.1.4 Автоматизация создания списка зависимостей
10.2 Установка триггеров
10.3 Написание проверочных скриптов
10.4 Создание субпакетов
10.4.1 Предоставление информации о субпакетах
10.4.2 Скрипты в субпакетах
10.4.3 Сборка субпакетов
10.5 Создание пакетов с переопределяемыми путями
10.5.1 Задание префикса
10.5.2 Редактирование секции files
10.5.3 Проблемы создания пакетов с переопределяемыми путями
10.6 Условная сборка
10.6.1 Условные макросы
10.6.2 Условные блоки
10.6.3 Архитектурно-зависимые условия
Раздел 11. Контролирование сборки с помощью утилиты rpmbuild
11.1.1 Настройка сборки
11.1.2 Тестирование сборки
11.1.3 Отладка сборки
11.1.4 Очистка
11.1.5 Сборка для других платформ
11.2 Сборка rpm-пакетов без внешнего spec-файла
11.2.1 Опции rpmbuild для работы с tar-архивами
11.2.2 Ожидаемая структура архива
11.3 Работа с пакетами, содержащими исходный код (src.rpm)
11.3.1 Пересборка бинарных пакетов из src.rpm
11.3.2 Перекомпиляция бинарных пакетов из src.rpm
11.4 Подписывание собранных пакетов
11.4.1 Проверка установки программного обеспечения GPG
11.4.2 Конфигурирование подписи
11.4.3 Подписывание пакетов с помощью утилиты rpmbuild
11.4.4 Подписывание с помощью утилиты rpm
11.4.5 Верификация подписей
11.4.6 Импорт публичного ключа
Раздел 12. Вспомогательное программное обеспечение сборщика пакетов
12.1.1 Использование плагинов vim для облегчения редактирования spec-файлов
12.1.2 Добавление функций с помощью emacs-плагина rpm-spec-mode
12.1.3 Отладка spec-файла с помощью rpmlint
12.1.4 Использование rpm2cpio для извлечения файлов из нагрузки пакетов
Раздел 13. Пакетостроение - генеральная линия партии или колхоз "Светлый путь"
13.1 Наука о граблях - как избегать повторения наиболее общих ошибок
13.1.1 Просматривайте тематические рассылки
13.1.2 Используйте rpmbuild
13.1.3 Не пытайтесь победить систему
13.1.4 Отключите автоматическую генерацию зависимостей
13.1.5 Не помещайте в секцию %files каталоги
13.1.6 Разомкните циклические зависимости
13.2 Наука об изучении опыта передовиков производства. Следуйте лучшей практике
13.2.1 Передовой опыт: подготовка к сборке
13.2.2 Передовой опыт: сборка
Раздел 14. Автоматизация операций RPM с помощью скриптов
14.1 Скриптинг
14.2 Отличительные черты скриптовых языков
14.3 Как определить, когда нужна программа, а когда - скрипт?
14.4 Основы shell-скриптинга
14.4.1 Создание скрипта
14.4.2 Запуск скрипта
14.4.3 Проблемы при запуске скрипта
14.4.4 Преобразование скрипта в команду
14.4.5 Передача параметров в скрипт
14.5 Файлы в rpm-пакетах
14.6 Запросы к БД RPM
14.6.1 Запрос списка пакетов, установленных единовременно
14.6.2 Чтение html-документации о пакете
Раздел 15. Программирование RPM на C
15. Программирование RPM на C
15.1.1 Установка окружения для C-программирования
15.1.2 Установка окружения программирования RPM
15.1.3 Использование библиотеки RPM
15.1.4 Компиляция и линковка RPM программ
15.1.5 Получение информации о RPM окружении
15.2 Мощь popt
15.2.1 Псевдонимы popt
15.2.2 Программирование с popt
15.2.3 Обработка ошибок
15.2.4 Работающий пример
15.2.5 Обработка опций командной строки rpm
15.3 Работа с rpm-файлами
15.3.1Открытие rpm-файла
15.3.2 Чтение начального идентификатора rpm и сигнатуры
15.3.3 Чтение хэдера
15.3.4 Короткий путь к информации хэдера
15.3.5 Закрытие rpm-файла
15.4 Программирование с БД RPM
15.4.1 Итераторы БД
15.4.2 Сет зависимости
15.5 Сравнение rpm-файла и установленного пакета
Раздел 16. Программирование RPM на Python
16.1.1 Установка окружения для программирования на Python
16.1.2 Использование Python в графических приложениях
16.2 Иерархия Python API
16.3 Программирование с БД RPM
16.3.1 Доступ к БД RPM
16.3.2 Запросы к БД RPM
16.3.3 Работа с хэдером пакета
16.3.4 Запросы о конкретных пакетах
16.3.5 Вывод информации о пакетах
16.3.6 Уточнение запросов
16.4 Чтение файлов пакетов
16.4.1 Чтение хэдера из файла пакета
16.4.2 Установка флагов верификации
16.5 Сравнение зависимостей
16.6 Установка и обновление пакетов
16.6.1 Построение сета транзакции
16.6.2 Элементы транзакции
16.6.3 Проверка и переопределение порядка элементов транзакции
Раздел 17. Программирование RPM на Perl
17. Программирование RPM на Perl
17.1 Получение и использование RPM-модулей Perl
17.2 Работа с rpm-файлами
17.2.1 Открытие rpm-файла
17.2.2 Получение значений полей хэдера из файла пакета
17.2.3 Удобные методы
17.2.4 Вывод имени и версии
17.2.5 Проверка, является ли файл пакета пакетом с исходными кодами
17.3 Работа с БД RPM
17.3.1 Открытие БД RPM
17.3.2 Поиск пакетов
17.3.3 Обход списка пакетов
17.3.4 Дополнительные функции поиска
17.3.5 Получение информации о пакетах
17.3.6 Сравнение версий
17.3.7 Закрытие БД
Раздел 18. Использование RPM в не-Red Hat Линуксах
18.1 О проблемах установки rpm-пакетов в не-Red Hat Линуксах
18.1.1 Версии системы RPM
18.1.2 Разделение ПО по пакетам
18.1.3 Зависимости
18.1.4 Пути установки
18.1.5 Если ничего не помогает, соберите пакет из исходников
18.2 Решение проблем сборки пакетов
18.2.1 Создание пакетов, специфичных для конкретного дистрибутива
18.2.2 Работа с автоматической генерацией зависимостей
18.2.3 Работа с различающимися макросами
18.2.4 Создание пакетов с переопределимыми путями
18.2.5 Построение окружения сборки RPM
18.3 Работа с не-rpmbased дистрибутивами и утилита alien
18.4 Стандартизация RPM
Раздел 19. Использование RPM в других операционных системах
19.1 Запуск RPM на других операционных системах
19.1.1 Получение RPM для целевой системы
19.1.2 Запуск RPM под Windows
19.2 Разворачивание RPM на других операционных системах
19.2.1 Скачивание кода
19.2.2 Извлечение ПО
19.2.3 Информация в файле INSTALL
19.2.4 Библиотеки, необходимые RPM
19.2.5 Инструменты для сборки RPM
19.2.6 Сборка RPM
19.2.7 Решение проблем
19.3 Установка и настройка системы RPM
19.3.1 Разворачивание БД RPM
19.3.2 Создание окружения RPM
19.4 Создание rpm-пакетов для не-Linux систем
19.4.1 Настройка окружения сборки
19.4.2 Кросс-сборка пакетов
Раздел 20. Изменение поведения RPM
20.1 Настройка поведения с помощью RPM-макросов
20.1.1 Определение макросов
20.1.2 Пользовательские макросы
20.2 Конфигурирование RPM
20.2.1 Просмотр текущих установок
20.2.2 Расположение rpmrc-файлов
20.2.3 Изменение установок
20.3 Добавление псевдонимов popt
20.3.1 Определение псевдонимов
20.3.2 Пользовательские псевдонимы
Раздел 21. Справочник по командам RPM
21.1.1 Команда rpm в режиме запросов
21.1.2 Команда rpm в режимах установки и обновления (upgrade, freshen, install)
21.1.3 Команда rpm в режиме удаления
21.1.4 Команда rpm в режиме подписи
21.1.5 Команда rpm в режиме верификации
21.1.6 Команда rpm в режиме работы с БД RPM
21.1.7 Разные опции
21.2.1 Утилита rpmbuild. Сборка посредством указания spec-файла
21.2.2 Утилита rpmbuild. Сборка из tar-архива
21.2.3 Пересборка пакетов из пакетов с исходным кодом
21.2.4 Изменение хода сборки
Раздел 22. Синтаксис spec-файла
22.1 Поля, содержащие информацию о пакете
22.1.1 Комментарии
22.1.2 Параметры сборки
22.1.3 Поля, содержащие информацию о зависимостях
22.1.4 Файлы с исходным кодом
22.2.1 Макросы определения переменных
22.2.2 Макросы условий
22.2.3 Встроенные макросы
22.3.1 Подготовка к сборке
22.3.2 Сборка
22.3.3 Установка
22.3.4 Очистка
22.3.5 Скрипты стадий установки и удаления
22.4 Файлы
22.4.1 Создание пакетов с переопределимыми путями
22.5 Журнал изменений
Раздел 23. Эволюция функциональности RPM
23 Эволюция функциональности RPM
Раздел 24. Формат файла rpm-пакета
24.1 Файл пакета
24.1.1 Начальный идентификатор
24.1.2 Подпись
24.1.3 Хэдер
24.1.3.1 Поля хэдера
24.1.3.2 Скрытые поля хэдера
24.1.3.3 Поля подписи
24.1.3.4 Поля для установочной информации
24.1.3.5 Поля информации о файлах
24.1.3.6 Поля зависимостей
24.1.4 Нагрузка
Раздел 25. Ресурсы, посвященные RPM
25.1.1 Сайт rpm.org
25.1.2 Сайты для поиска пакетов rpm
25.1.3 Сайты, посвященные утилитам RPM
Как не относящиеся к делу пропущены разделы 26 - Текстовые редакторы и вспомогательные средства разработки в Linux, 27 - Лицензирование RPM (текст GPL)
Ветка форума, в которой обсуждается данная работа
11.4.5 Верификация подписей
Для того, чтобы убедиться, что пакет не был модифицирован с момента его подписывания, используется механизм проверки подписи. Процедура верификации также проверяет соответствие ключа ключу определенного вендора.
Утилита rpm (не rpmbuild) работает в режиме проверки подписи, если применяется опция -K:
rpm -K package.rpm |
Для изменения режима (отключения какой-то определенной проверки) используются опции, указанные в таблице ниже.
Опция |
Использование |
--nogpg |
Отключает проверку подписи GPG |
--nomd5 |
Отключает проверку подписи MD5 |
--nopgp |
Отключает проверку подписи PGP |
Также может применятся опция --checksig, она является синонимом -K.
Когда команда проверки находит в пакете правильные подписи, выводится примерно следующее:
# rpm -K xtoolwait-1.3-3.src.rpm |
Это сообщение означает, что пакет с момента подписывания не изменялся. Эта проверка также показала, что ключ производителя пакета соответствует его публичному ключу.
Для вывода более подробной информации нужно задействовать опцию -v:
$ rpm -Kv vixie-cron-3.0.1-69.src.rpm |
Если проверка закончилась не успешно, а это может означать многое, вплоть до подмены пакета злоумышленником, увидим сообщение об ошибке:
# rpm --checksig xtoolwait-1.3-3.src.rpm |
Позиции, не прошедшие проверку, выделены в выводе символами верхнего регистра, например, DSA, в то время как прошедшие проверку подписи обозначаются символами в нижнем регистре. В данном примере сигнатуры sha1 и md5 совпали с ожидаемыми.
Наиболее вероятные причины ошибок при проверке:
1. Пакет не был правильным образом подписан. При этом он может быть легитимным.
2. Пакет был модифицирован и он больше не легитимен.
3. Система RPM не содержит публичного ключа вендора, который используется при проверке.
Из этого понятно, что само наличие ошибки ничего не говорит нам о легитимности пакета или наоборот. Поэтому первым шагом при возникновении таких ошибок должен быть импорт публичного ключа вендора пакетов.
Далее - Импорт публичного ключа
Назад - Подписывание с помощью утилиты rpm
Содержание
11.4.6 Импорт публичного ключа
Опция --import утилиты rpm импортирует публичный ключ заданного вендора. Формат ключа следующий (некоторые строки пропущены для экономии места):
The following public key can be used to verify RPM packages built |
В качестве параметра команде импорта нужно задать имя файла, содержащего ключ:
rpm --import key_file |
Для импорта ключа необходимо иметь права root. Проверка после импорта ключа может выглядеть так:
# rpm --import RPM-GPG-KEY |
Если после импорта ключа проблема все еще остается, дальнейшие действия - на совести администратора. Некоторые администраторы никогда не устанавливают таких пакетов.
Для получения списка доступных ключей используйте команду:
$ rpm -qa | grep -i gpg |
В текущем случае установлен один публичный ключ. Установленный ключ можно удалить, если он представляет собой пакет, как в данном примере. Используйте команду rpm -e.
Для импорта публичного ключа Red Hat найдите файл RPM-GPG-KEY на дистрибутивном диске и выполните импорт с помощью rpm --import .
Далее - Раздел 12. Вспомогательное программное обеспечение сборщика пакетов
Назад - Верификация подписей
Содержание
13.1.1 Просматривайте тематические рассылки
Множество людей уже предпринимали попытки разрешения серьезных проблем использования RPM, поэтому, если вы столкнулись с трудностями, велика вероятность того, что кто-то уже сталкивался с тем же самым. Почтовая рассылка и форумы, посвященные RPM, помогут во многих, если не в большинстве, случаев.
Весьма полезены для разработчика списки почтовой рассылки
http://www.rpm.org/mailing_list, а также архив рассылки http://groups.yahoo.com/group/rpm-list/messages .
Задавайте вопросы в такой форме, которая породит наиболее дружественный и полезный ответ. Правильная форма вопроса включает следующие компоненты:
1. Сначала - домашняя работа. Убедитесь, что на ваш вопрос нет уже готового ответа.
2. Опишите проблему и симптомы как можно подробнее.
3. Используйте ясные заголовки сообщений. Это то, что люди видят в первую очередь. Если тема сообщения мутная, велика вероятность, что ключевой человек, тот, кто реально способен ответить, не будет читать само сообщение.
4. Используйте в сообщениях только простой текст, не HTML.
5. Облегчите людям возможность ответа, включая адрес в тело сообщения.
Кроме мэйл-листа полезной может оказаться новостная группа linux.redhat.rpm .
Далее - Используйте rpmbuild
Назад - Импорт публичного ключа
Содержание
13.1.2 Используйте rpmbuild
В RPM версий до 4.1 сборка выполнялась командой rpm -b . Если что-то идет не так, проверьте, может быть в текущей системе уже используется rpmbuild вместо rpm -b .
Как ни странно, список людей, попадающих в столь простой тупик, все время пополняется.
Далее - Не пытайтесь победить систему
Назад - Просматривайте тематические рассылки
Содержание
13.1.3 Не пытайтесь победить систему
Если вы вдруг обнаружили, что ваши spec-файлы приобретают все бОльшую и бОльшую сложность, и, с другой стороны, вы отключаете одну возможность RPM за другой, велика вероятность, что имеют место попытки победить систему великого и непобедимого социализма. Вряд ли это хорошая идея.
RPM работает определенным образом. Можно быть несогласным со светлым путем, однако попытки заставить двигаться путем, чуждым системе, приведут к коллапсу (вопрос чьему).
Есть набор правил, и, что важнее, набор соглашений, которым должна следовать RPM. Следуйте им и вы, и ваши внуки будут жить при коммунизме.
Далее - Отключите автоматическую генерацию зависимостей
Назад - Используйте rpmbuild
Содержание
13.1.4 Отключите автоматическую генерацию зависимостей
Когда собирается пакет, rpmbuild автоматически генерирует список зависимостей от разделяемых библиотек и системных команд. Это поведение можно изменить.
Для отключения генерации зависимостей в spec-файл нужно поместить специальную директиву:
Autoreq: 0 |
Более корректной возможностью, однако, является переопределение макросов %{__find_requires} и %{__find_provides}, или одного из них по необходимости. Для придания макросу пустого значения требуется вставить в spec-файл команду:
%define __find_requires %{nil} |
Эта возможность лучше, так как позволяет отключить только интересующую вас проверку, а не все сразу. Кроме того, можно применить простые и весьма специфичные настройки процесса генерации зависимостей. Например, можно определить макросы для нормальной проверки зависимостей, исключая некоторые проблематичные файлы (пакеты). В следующем примере макросы разворачиваются в shell-скрипты, которые выполняют проверку зависимостей:
$ rpm --eval "%__find_provides" |
Вы можете переписать эти скрипты для выполнения поиска зависимостей каким-либо специфическим образом.
Далее - Не помещайте в секцию %files каталоги
Назад - Не пытайтесь победить систему
Содержание
13.1.5 Не помещайте в секцию %files каталоги
До тех пор, пока вам это действительно не станет необходимо, не помещайте в список секции %files имен каталогов, потому что rpmbuild автоматически добавляет все файлы под указанным каталогом в пакет. Если вы случайно указали системную директорию, например, /usr/bin, пакет станет владельцем всех файлов в этом каталоге.
Для предотвращения такого события желательно в точности указывать полные имена файлов. Часто для этого используют возможности получения списка из текстового файла и вывод команды find.
Если пакет нуждается во включении в список его файлов каталога, используйте директиву %dir .
Далее - Разомкните циклические зависимости
Назад - Отключите автоматическую генерацию зависимостей
Содержание
13.1.6 Разомкните циклические зависимости
Если два пакета зависят один от другого, не стоит помещать эту информацию в spec-файл обоих пакетов (директива Requires). Если это все же произошло, пакеты не захотят устанавливаться один без другого без форсирования процедуры.
Эту коллизию можно обойти, используя директиву PreReq вместо Requires. Например, если пакет А зависит от пакета В и пакет В зависит от пакета А, поместите следующее в spec-файл пакета В:
PreReq: A |
Кроме того, проблему можно обойти на стадии установки. Для этого надо оба пакета установить в рамках одной транзакции:
rpm Uvh ./A.rpm ./B.rpm |
Далее - Передовой опыт: подготовка к сборке
Назад - Не помещайте в секцию %files каталоги
Содержание
13.2.1 Передовой опыт: подготовка к сборке
Перед тем, как начать собирать rpm-пакет, необходимо выполнить несколько шагов в духе генеральной линии. Для того, чтобы быть уверенным, что все действительно готово.
13.2.1.1 Создайте пакет с исходным кодом
Наличие пакета с исходным кодом позволяет превращать исходники в пакеты для любой системы, любой архитектуры с помощью инструкций, заложенных в spec-файл. Возникает два полезных эффекта - все время отслеживается эволюция ПО и можно в любой момент получить бинарный rpm, пересобрав src.rpm.
Иными словами, создайте поколения rpm-пакетов, следуя соглашениям системы RPM и превратите такой подход в обычный для вас путь разработки.
13.2.1.2 Начинайте с чистых исходников
Кроме планирования процесса сборки, следует помнить, что нужно начинать с чистых, неизмененных исходных текстов приложения, из которого формируется пакет. Старт с чистого листа означает, что можно воспроизвести все детали процесса эволюции ПО, вплоть до отката в самое начало, если это потребуется.
Для всех изменений кода используются патчи. Таким образом, все внесенные изменения у вас в руках.
Некоторые rpm-пакеты имеют сотню и более патчей, которые rpmbuild применяет к коду в процессе сборки. Несмотря на это, при добавлении новых патчей процесс никак не изменяется.
Сохранение патчей отдельно от изначального исходного кода делает несложной задачу отката к прежним состояниям и задачу интеграции в пакет новых версий ПО.
13.2.1.3 Решите, что именно содержится в каждом пакете
Нет никакой практической необходимости помещать все ваше ПО в один пакет. Вместо этого разработчики, как правило, стремятся поделить большие проекты на несколько пакетов, возможно зависимых друг от друга. Например, сама система RPM имеет один пакет для базовой системы, один для разработчиков RPM, rpm-devel, и один для ПО сборки пакетов, rpm-build. Также имеются средства интеграции, например прикладной программный интерфейс для Python в пакете rpm-python.
Почему такое разделение важно? Возьмем, например, rpm-python. Этот пакет зависит от самого Python. Зачем заставлять пакет системы RPM зависеть от Python? Разделение на более мелкие структурные единицы позволяет избегать подобных коллизий.
В процессе разделения ПО на пакеты следует держать в уме два соображения:
- мы делим ПО на пакеты, так как это адаптирует принятую модель к пользователю;
- мы делим ПО на пакеты, так как маленькие пакеты просты, а это облегчает их создание и сопровождение.
Система RPM следует генеральной линии, особенно в части первого пункта. Некоторые пользователи сами расширяют возможности RPM, что позволяет разработчикам выносить эти дополнительные возможности за рамки базовой системы и сохранять их в rpm-devel. Большинство пользователей относятся к категории, использующей RPM, потому что всем нужно устанавливать пакеты, но некоторые пользователи эти пакеты собирают. Таким образом, разделение ПО на более компактные пакеты работает на расширение пользовательских перспектив.
Если разделение пакета на более мелкие не упрощает вещи, а усложняет, значит в конкретном случае это была, возможно, нездоровая идея.
13.2.1.4 Создайте тестовую базу данных RPM
В процессе сборки пакетов RPM так или иначе обращается к БД RPM. Но вы не обязаны работать с общесистемной БД. Если имеется тестовая БД RPM, можно устанавливать и тестировать пакеты в рамках этой БД. Для осуществления такого подхода задействуйте опции --justdb, --dbpath, --prefix, и --badreloc . Эти опции позволяют установить пакеты только в БД, в альтернативную БД, в другой "корневой" каталог.
Опция --test позволяет понять, что происходит при установке, на самом деле не выполняя ее.
Осуществляя скрупулезный подход к сборке и тестированию, разработчик должен создать тестовую БД на основе в точности известного набора пакетов с нуля. Это позволит проверить поведение пакетов в других системных окружениях. Наилучшим путем в этом направлении будет установка пакетов в альтернативную "корневую" директорию, которая не совпадает с системным корнем.
Можно также скопировать системную БД RPM в такую директорию и использовать ее. При этом пути к файлам в БД будут совпадать с реальными системными путями.
Независимо от того, как вы создаете тестовую БД, пересоздавайте базу всякий раз, когда выполняется тот или иной тест. Это необходимо для уверенности в том, что начальные условия нам известны, и они одинаковы. Обычно это сводится к копированию исходной тестовой БД в каталог, где происходят тесты.
Далее - Передовой опыт: сборка
Назад - Разомкните циклические зависимости
Содержание
13.2.2 Передовой опыт: сборка
Сборка пакета не так проста, как бы мы этого хотели. Часто приходится делать одно и то же снова и снова, чтобы получить работающий rpm. Эта глава рассказывает о лучшей для применения практике сборки.
13.2.2.1 Используйте инструменты
Использование инструментов часто помогает ускорить процесс сборки, а также помогает пониманию механизмов работы RPM. Утилиты, применимые в сборке, например, Red Hat плагин для Eclipse IDE, есть живое доказательство этой полезности.
Даже так называемые "реальные Линукс-хакеры", могущие смоделировать работающую систему виртуальной памяти с помощью одной команды cat, не пренебрегают утилитами, поскольку время - настоящая ценность.
Пример утилиты, имеющей действительную пользу - программа gendiff, входящая в RPM. Она позволяет избегнуть необходимости хранить отдельную директорию оригинальных исходников. gendiff позволяет создать патч на каталог исходников, если было изменено много файлов внутри каталога.
Для работы gendiff нужно сохранить копии всех файлов, которые вы хотите модифицировать. Задайте сохраняемым файлам одинаковое расширение, например, .orig . После редактирования файлов запускается gendiff таким образом:
$ gendiff directory_name .orig > patch_name.patch |
Файл патча patch_name.patch будет содержать все изменения для всех файлов в каталоге.
13.2.2.2 Никогда не собирайте пакеты из-под root
Никогда не собирайте пакеты из-под пользователя root (Never, never, never build RPMs logged in as the root user - весьма эмоционально, прим. перев.). Поскольку для тестирования собранных пакетов их требуется установить, а это происходит с правами root, этот аспект приходится держать в голове. Но все-таки опасность велика. В spec-файле присутствует множество скриптов и команд и даже одна ошибка в одной команде может обрушить систему, если эта команда выполняется с правами суперпользователя. Права root допускают возможность модификации файлов, в том числе системных, удаление файлов и запись нового содержания поверх существующего. Вряд ли это допустимо для системы, в которой вы собираете пакеты.
13.2.2.3 Создайте цифровую подпись
В RPM версии 4.1 и новее гораздо большее значение придано цифровой подписи пакетов. Команда rpm в поведении по умолчанию проверяет подпись каждого пакета, к которому обращается.
Мы должны подписать каждый собранный пакет хотя бы для того , чтобы не обмануть ожиданий пользователя. Кроме того, необходимо поместить публичный ключ на свой сайт и на сервера публичных ключей во избежании имперсонализации ваших пакетов.
13.2.2.4 "Умное" копирование
Возможно, ваш дистрибутив содержит сотни rpm-пакетов и каждый их них имеет spec-файл. Почему бы не просмотреть эти спецификации? Вместо старта с пустого spec-файла множество определений могут быть перенесены в виде готовых решений. Однако, не все spec-файлы идеальны. Вряд ли стоит копировать не оптимальные подходы и решения.
13.2.2.5 Определение BuildRoot
Директива BuildRoot задает каталог, в котором будет собираться ПО. В соответствии со стандартным подходом мы должны определить каталог под каталогом _tmppath. Например:
BuildRoot: %{_tmppath}/%{name}-buildroot |
Однажды заданный, путь к каталогу сборки заставляет rpmbuild инициализировать переменную окружения RPM_BUILD_ROOT этим значением.
В команде rpmbuild можно использовать опцию --buildroot для переопределения директивы BuildRoot из spec-файла.
Использование BuildRoot требует, чтобы обычный пользователь имел в определяемый каталог право записи, иначе не будет возможности собирать пакеты из-под обычного пользователя. Важное значение BuildRoot имеет в отношении разделения деревьев сборки для разных пакетов.
Всегда определяйте BuildRoot.
13.2.2.6 Добавляйте запись в журнал изменений для каждой новой версии сборки
Каждый раз, когда собирается новая версия пакета, в секцию %changelog должна быть добавлена запись. Благодаря этому администраторы систем могут отслеживать последовательность изменений и устанавливать те версии, в которых нужные изменения есть (или наоборот, нет ненужных изменений).
Эта информация также помогает понять, нуждается ли пакет в обновлении.
Важной информацией в журнале изменений являются сообщения об исправлении различных уязвимостей.
13.2.2.7 Задайте группу пакета
Все пакеты категорированы по группам. Имена групп позволяют, например, облегчить пользователю поиск нужных пакетов. Они выводятся графическими утилитами управления пакетами в свойствах пакетов.
Если ваше приложение представляет собой общесистемную консольную утилиту, поместите ее в группу System Environment/Shells и не помещайте ее в группы Development/Languages или System Environment/Daemons. Это сравнительно незначительная деталь во всем стеке информации о пакете, но она помогает поиску в огромном массиве пакетов дл я Linux.
Официальный список групп пакетов Red Hat для RPM версии 4.1 содержится в файле /usr/share/doc/rpm-4.1/GROUPS и где-нибудь в похожей локации для других версий RPM.
Далее - Раздел 14. Автоматизация операций RPM с помощью скриптов
Назад - Передовой опыт: подготовка к сборке
Содержание
Red Hat RPM Guide - русский перевод
RPM Guide
Eric Foster-Johnson
Copyright © 2005 Eric Foster-Johnson
От переводчика:
Данный текст в его исходном варианте выложен Red Hat под лицензией Open Publication License. Надеюсь, что эта лицензия подразумевает возможность самодеятельного перевода с указанием авторов и правообладателей. Если это, паче ожидания, не так, сообщите мне, пожалуйста, об этом на
vlad_goreletsky(at)lexpr.ru
Там, где обсуждается система пакетного менеджмента, обычно употребляется аббревиатура RPM. Там, где речь идет о пакетах или утилите rpm, используется rpm или src.rpm
Замечания по орфографии принимаются. Замечания по стилю не принимаются. Любой из вас может сделать это лучше, если знает как.
В тексте по отношению к оригинальному множество сокращений. Много слов иногда ничего не добавляют к ЗНАНИЮ. Тут возможны претензии, но таков уж мой произвол. Считаю, что потенциальные читатели в основном люди занятые.
Влад Горелецкий
Полное руководство Red Hat Package Manager
Раздел 1. Введение в RPM
1.1 Необходимость системы управления пакетами
1.2 Цели разработки RPM
1.2.1 Легкость использования
1.2.2 Ориентирование на понятие "пакет"
1.2.3 Возможность обновления пакетов
1.2.4 Межпакетные зависимости
1.2.5 Возможность запросов
1.2.6 Верификация пакетов
1.2.7 Поддержка различных архитектур
1.2.8 "Чистые" исходные коды
1.3 Терминология RPM
Раздел 2. Обзор RPM
2.1.1 Введение в структуру файла пакета
2.1.2 Формат файла rpm
2.1.3 Бинарные rpm и rpm с исходным кодом
2.2 База данных RPM
2.3 Команды RPM
Раздел 3. Использование RPM
3.1 Утилита rpm
3.2 Установка и обновление пакетов
3.2.1 Режим установки
3.2.2 Режим обновления
3.2.3 Обновление в режиме freshen
3.2.5 Установка через сеть
3.2.6 Установка пакетов с исходным кодом
3.3 Удаление пакетов
3.4 Другие опции rpm
Раздел 4. Использование базы данных RPM
4.1 Запросы к БД RPM
4.1.1 Запросы о пакетах
4.1.3 Как повысить информативность запроса
4.1.4 Какому пакету принадлежит файл?
4.2 Получение информации о пакетах
4.2.1 Описание пакета
4.2.2 Группы пакетов
4.2.3 Список файлов пакета
4.2.4 Список конфигурационных файлов пакета
4.2.5 Список файлов документации пакета
4.2.6 Список статуса файлов пакета
4.2.7 Список скриптов
4.2.8 Список изменений
4.2.9 Комбинирование запросов
4.2.10 Пользовательские запросы
4.2.11 Прочие запросы
4.3 Получение информации из файла rpm-пакета
4.4 Верификация установленных пакетов
4.4.1 Верификация системы в целом
4.4.2 Настройка проверок
4.5 Работа с БД RPM
4.5.1 Создание резервной копии БД RPM
4.5.2 Перестройка БД RPM
4.5.3 Создание новой БД RPM
Раздел 5. Зависимости пакетов
5.1 Введение в концепцию зависимостей
5.1.1 Возможности
5.1.2 Зависимости версий
5.1.3 Конфликты
5.1.4 Неактуальные возможности
5.2.1 Проверка зависимостей вида Requires
5.2.2 Проверка зависимостей вида Provides
5.2.3 Проверка на конфликты
5.2.4 Какой пакет требует данную возможность?
5.2.5 Какой пакет предоставляет данную возможность?
5.3 Триггеры
Раздел 6. Транзакции
6.1 Введение в транзакции
6.1.1 Когда транзакции необходимы
6.1.2 Возврат транзакции
6.2 Транзакции с командой rpm
6.2.1 Идентификаторы транзакций
6.2.2 Откат транзакций
6.3 Сохранение старых пакетов
Раздел 7. Программное обеспечение для управления RPM
7.1 Нахождение программного обеспечения в формате rpm-пакетов
7.1.1 rpmfind и rpm2html
7.2 Сайты, посвященные RPM, в Интернете
7.3 Утилиты расширенного менеджмента пакетов
7.3.1 Yum
7.3.2 Рекомендуемые плагины yum
7.3.3 Графические фронтенды программ управления пакетами
Раздел 8. Создание rpm-пакетов: обзор
8.1 Подготовка к сборке пакета
8.1.1 Планирование - что хотим собрать
8.1.2 Консолидация программного обеспечения
8.1.3 Воспроизводимая сборка ПО
8.1.4 Планирование обновлений
8.1.5 Удовлетворение зависимостей
8.2 Сборка rpm-пакета
8.2.1 Разворачивание структуры директорий
8.2.2 Размещение исходного кода в дереве сборки
8.2.3 Создание spec-файла
8.2.4 Сборка пакета с помощью утилиты rpmbuild
8.2.5 Верификация собранных пакетов
Раздел 9. Работа со spec-файлом
9.1 Чтение spec-файла
9.2 Начинаем создавать spec-файл
9.3 Информация о пакете в spec-файле
9.3.1 Описание пакета
9.3.2 Установка пути сборки
9.3.3 Имена файлов архивов с исходным кодом
9.3.4 Имена патчей
9.4 Контроль сборки
9.4.1 Подготовка к сборке
9.4.2 Сборка ПО
9.4.3 Инсталляция ПО
9.4.4 Очистка после сборки
9.4.5 Определение установочных скриптов
9.5 Заполнение списка файлов
9.5.1 Использование шаблонов
9.5.2 Имена каталогов
9.5.3 Пометка файлов как файлов документации или конфигурационных
9.5.4 Определение атрибутов файлов
9.5.5 Верификация секции %files
9.5.6 Автоматизированное создание списка файлов
9.5.7 Обработка ошибок для неупакованных файлов
9.6 Добавление записей в журнал изменений
9.7 Макросы
9.7.1 Встроенные макросы
9.7.2 Макросы, специфичные для spec-файла
9.7.3 Определение нового макроса
9.7.4 Параметры макросов
9.8 Создание spec-файла в XML формате
Раздел 10. Расширенные возможности RPM
10.1 Зависимости пакета
10.1.1 Имена зависимостей
10.1.2 Установка предварительных требований
10.1.3 Зависимости сборки
10.1.4 Автоматизация создания списка зависимостей
10.2 Установка триггеров
10.3 Написание проверочных скриптов
10.4 Создание субпакетов
10.4.1 Предоставление информации о субпакетах
10.4.2 Скрипты в субпакетах
10.4.3 Сборка субпакетов
10.5 Создание пакетов с переопределяемыми путями
10.5.1 Задание префикса
10.5.2 Редактирование секции files
10.5.3 Проблемы создания пакетов с переопределяемыми путями
10.6 Условная сборка
10.6.1 Условные макросы
10.6.2 Условные блоки
10.6.3 Архитектурно-зависимые условия
Раздел 11. Контролирование сборки с помощью утилиты rpmbuild
11.1.1 Настройка сборки
11.1.2 Тестирование сборки
11.1.3 Отладка сборки
11.1.4 Очистка
11.1.5 Сборка для других платформ
11.2 Сборка rpm-пакетов без внешнего spec-файла
11.2.1 Опции rpmbuild для работы с tar-архивами
11.2.2 Ожидаемая структура архива
11.3 Работа с пакетами, содержащими исходный код (src.rpm)
11.3.1 Пересборка бинарных пакетов из src.rpm
11.3.2 Перекомпиляция бинарных пакетов из src.rpm
11.4 Подписывание собранных пакетов
11.4.1 Проверка установки программного обеспечения GPG
11.4.2 Конфигурирование подписи
11.4.3 Подписывание пакетов с помощью утилиты rpmbuild
11.4.4 Подписывание с помощью утилиты rpm
11.4.5 Верификация подписей
11.4.6 Импорт публичного ключа
Раздел 12. Вспомогательное программное обеспечение сборщика пакетов
12.1.1 Использование плагинов vim для облегчения редактирования spec-файлов
12.1.2 Добавление функций с помощью emacs-плагина rpm-spec-mode
12.1.3 Отладка spec-файла с помощью rpmlint
12.1.4 Использование rpm2cpio для извлечения файлов из нагрузки пакетов
Раздел 13. Пакетостроение - генеральная линия партии или колхоз "Светлый путь"
13.1 Наука о граблях - как избегать повторения наиболее общих ошибок
13.1.1 Просматривайте тематические рассылки
13.1.2 Используйте rpmbuild
13.1.3 Не пытайтесь победить систему
13.1.4 Отключите автоматическую генерацию зависимостей
13.1.5 Не помещайте в секцию %files каталоги
13.1.6 Разомкните циклические зависимости
13.2 Наука об изучении опыта передовиков производства. Следуйте лучшей практике
13.2.1 Передовой опыт: подготовка к сборке
13.2.2 Передовой опыт: сборка
Раздел 14. Автоматизация операций RPM с помощью скриптов
14.1 Скриптинг
14.2 Отличительные черты скриптовых языков
14.3 Как определить, когда нужна программа, а когда - скрипт?
14.4 Основы shell-скриптинга
14.4.1 Создание скрипта
14.4.2 Запуск скрипта
14.4.3 Проблемы при запуске скрипта
14.4.4 Преобразование скрипта в команду
14.4.5 Передача параметров в скрипт
14.5 Файлы в rpm-пакетах
14.6 Запросы к БД RPM
14.6.1 Запрос списка пакетов, установленных единовременно
14.6.2 Чтение html-документации о пакете
Раздел 15. Программирование RPM на C
15. Программирование RPM на C
15.1.1 Установка окружения для C-программирования
15.1.2 Установка окружения программирования RPM
15.1.3 Использование библиотеки RPM
15.1.4 Компиляция и линковка RPM программ
15.1.5 Получение информации о RPM окружении
15.2 Мощь popt
15.2.1 Псевдонимы popt
15.2.2 Программирование с popt
15.2.3 Обработка ошибок
15.2.4 Работающий пример
15.2.5 Обработка опций командной строки rpm
15.3 Работа с rpm-файлами
15.3.1Открытие rpm-файла
15.3.2 Чтение начального идентификатора rpm и сигнатуры
15.3.3 Чтение хэдера
15.3.4 Короткий путь к информации хэдера
15.3.5 Закрытие rpm-файла
15.4 Программирование с БД RPM
15.4.1 Итераторы БД
15.4.2 Сет зависимости
15.5 Сравнение rpm-файла и установленного пакета
Раздел 16. Программирование RPM на Python
16.1.1 Установка окружения для программирования на Python
16.1.2 Использование Python в графических приложениях
16.2 Иерархия Python API
16.3 Программирование с БД RPM
16.3.1 Доступ к БД RPM
16.3.2 Запросы к БД RPM
16.3.3 Работа с хэдером пакета
16.3.4 Запросы о конкретных пакетах
16.3.5 Вывод информации о пакетах
16.3.6 Уточнение запросов
16.4 Чтение файлов пакетов
16.4.1 Чтение хэдера из файла пакета
16.4.2 Установка флагов верификации
16.5 Сравнение зависимостей
16.6 Установка и обновление пакетов
16.6.1 Построение сета транзакции
16.6.2 Элементы транзакции
16.6.3 Проверка и переопределение порядка элементов транзакции
Раздел 17. Программирование RPM на Perl
17. Программирование RPM на Perl
17.1 Получение и использование RPM-модулей Perl
17.2 Работа с rpm-файлами
17.2.1 Открытие rpm-файла
17.2.2 Получение значений полей хэдера из файла пакета
17.2.3 Удобные методы
17.2.4 Вывод имени и версии
17.2.5 Проверка, является ли файл пакета пакетом с исходными кодами
17.3 Работа с БД RPM
17.3.1 Открытие БД RPM
17.3.2 Поиск пакетов
17.3.3 Обход списка пакетов
17.3.4 Дополнительные функции поиска
17.3.5 Получение информации о пакетах
17.3.6 Сравнение версий
17.3.7 Закрытие БД
Раздел 18. Использование RPM в не-Red Hat Линуксах
18.1 О проблемах установки rpm-пакетов в не-Red Hat Линуксах
18.1.1 Версии системы RPM
18.1.2 Разделение ПО по пакетам
18.1.3 Зависимости
18.1.4 Пути установки
18.1.5 Если ничего не помогает, соберите пакет из исходников
18.2 Решение проблем сборки пакетов
18.2.1 Создание пакетов, специфичных для конкретного дистрибутива
18.2.2 Работа с автоматической генерацией зависимостей
18.2.3 Работа с различающимися макросами
18.2.4 Создание пакетов с переопределимыми путями
18.2.5 Построение окружения сборки RPM
18.3 Работа с не-rpmbased дистрибутивами и утилита alien
18.4 Стандартизация RPM
Раздел 19. Использование RPM в других операционных системах
19.1 Запуск RPM на других операционных системах
19.1.1 Получение RPM для целевой системы
19.1.2 Запуск RPM под Windows
19.2 Разворачивание RPM на других операционных системах
19.2.1 Скачивание кода
19.2.2 Извлечение ПО
19.2.3 Информация в файле INSTALL
19.2.4 Библиотеки, необходимые RPM
19.2.5 Инструменты для сборки RPM
19.2.6 Сборка RPM
19.2.7 Решение проблем
19.3 Установка и настройка системы RPM
19.3.1 Разворачивание БД RPM
19.3.2 Создание окружения RPM
19.4 Создание rpm-пакетов для не-Linux систем
19.4.1 Настройка окружения сборки
19.4.2 Кросс-сборка пакетов
Раздел 20. Изменение поведения RPM
20.1 Настройка поведения с помощью RPM-макросов
20.1.1 Определение макросов
20.1.2 Пользовательские макросы
20.2 Конфигурирование RPM
20.2.1 Просмотр текущих установок
20.2.2 Расположение rpmrc-файлов
20.2.3 Изменение установок
20.3 Добавление псевдонимов popt
20.3.1 Определение псевдонимов
20.3.2 Пользовательские псевдонимы
Раздел 21. Справочник по командам RPM
21.1.1 Команда rpm в режиме запросов
21.1.2 Команда rpm в режимах установки и обновления (upgrade, freshen, install)
21.1.3 Команда rpm в режиме удаления
21.1.4 Команда rpm в режиме подписи
21.1.5 Команда rpm в режиме верификации
21.1.6 Команда rpm в режиме работы с БД RPM
21.1.7 Разные опции
21.2.1 Утилита rpmbuild. Сборка посредством указания spec-файла
21.2.2 Утилита rpmbuild. Сборка из tar-архива
21.2.3 Пересборка пакетов из пакетов с исходным кодом
21.2.4 Изменение хода сборки
Раздел 22. Синтаксис spec-файла
22.1 Поля, содержащие информацию о пакете
22.1.1 Комментарии
22.1.2 Параметры сборки
22.1.3 Поля, содержащие информацию о зависимостях
22.1.4 Файлы с исходным кодом
22.2.1 Макросы определения переменных
22.2.2 Макросы условий
22.2.3 Встроенные макросы
22.3.1 Подготовка к сборке
22.3.2 Сборка
22.3.3 Установка
22.3.4 Очистка
22.3.5 Скрипты стадий установки и удаления
22.4 Файлы
22.4.1 Создание пакетов с переопределимыми путями
22.5 Журнал изменений
Раздел 23. Эволюция функциональности RPM
23 Эволюция функциональности RPM
Раздел 24. Формат файла rpm-пакета
24.1 Файл пакета
24.1.1 Начальный идентификатор
24.1.2 Подпись
24.1.3 Хэдер
24.1.3.1 Поля хэдера
24.1.3.2 Скрытые поля хэдера
24.1.3.3 Поля подписи
24.1.3.4 Поля для установочной информации
24.1.3.5 Поля информации о файлах
24.1.3.6 Поля зависимостей
24.1.4 Нагрузка
Раздел 25. Ресурсы, посвященные RPM
25.1.1 Сайт rpm.org
25.1.2 Сайты для поиска пакетов rpm
25.1.3 Сайты, посвященные утилитам RPM
Как не относящиеся к делу пропущены разделы 26 - Текстовые редакторы и вспомогательные средства разработки в Linux, 27 - Лицензирование RPM (текст GPL)
Ветка форума, в которой обсуждается данная работа
14.1 Скриптинг
Скриптинг, то есть написание сценариев действий, доступно в скриптовых языках. Так обычно называют инерпретируемые языки сценариев. В таких языках легко писать новые команды, помогающие в нелегком труде автоматизации. Используемые в большей степени системными администраторами и в меньшей - прикладными программистами, скрипты помогают в некоторой части администрирования избавиться от скучно-утомительных ежедневных процедур. Администратору, работающему с RPM, скрипты помогут также хранить многосложные команды, например, нетривиальные форматы запросов к БД RPM.
Скрипты - это текстовые файлы. Они содержат команды на используемом языке. Множество таких скриптов несут в своем тексте общесистемные команды. Для запуска скрипта вызывается строка, которая запускает соответствующий языку интерпретатор, а он, в свою очередь, читает сценарий строка за строкой и выполняет написанные команды.
Несмотря на внешнее сходство, скриптинг как вид деятельности все же отличают от программирования, как из-за сложности решаемых задач, так и благодаря формату исполняемых файлов. Кроме того, большинство программ должны быть превращены в машинный код с помощью компиляторов и ассемблеров. Отдельные части программ могут быть соединены процедурами связывания.
Различия между программированием и скриптингом иногда вводят людей в заблуждение. Автору приходилось общаться с разработчиками, которые считали программирование чем-то вроде искусства, тогда как в рабочее время им приходилось писать сотни килобайт скриптов, запускающихся в изощренных графических интерфейсах. С точки зрения автора эти люди несомненно были программистами.
Не обращайте много внимания на тонкости и используйте для работы ПРАВИЛЬНЫЕ инструменты.
Далее - Отличительные черты скриптовых языков
Назад - Передовой опыт: сборка
Содержание
14.2 Отличительные черты скриптовых языков
Эксперты расходятся в оценках отличий скриптовых языков от компилируемых. Ясно, что языки, подобные Python, размывают границы, прежде казавшиеся четкими.
Изначально скрипты представляли собой небольшие текстовые файлы, в которых вызывались системные команды. Скрипты исполнялись интепретаторами команд, которые умеют перебирать строки с командами одну за другой.
Современные скриптовые языки, вроде Tcl, разбирают скрипт во время запуска и преобразуют текст в промежуточный байт-код. Однажды скомпилированный, скрипт уже трудно отличить от программы.
В скриптовых языках:
* Нет нужды в компиляции. Инерпретатор может уметь преобразовывать текст в промежуточный код, но этого не требуется делать в отдельной стадии. Такой код, если это предусмотрено конструкцией языка, будет сгенерирован при первом запуске скрипта.
* Возможности скриптового языка обычно представлены в виде более высокого уровня абстрагирования от системы, чем в компилируемом языке программирования. Например, программирование сокетов требует в Tcl существенно меньше кода, чем на С. Tcl предоставляет более высокоуровневые интерфейсы для работы с сетью, поэтому код намного проще.
* Команды в сценарии - это в большинстве своем команды, доступные оператору в командной строке операционной системы. Скрипты могут также содержать свои собственные команды.
* Язык, на котором пишут скрипты, называется скриптовым. А какой язык считать таковым? Это скорее соглашение большинства, чем что-то более серьезное.
В таблице представлены короткие списки скриптовых языков и языков программирования (компилируемых). Впрочем, эти списки не могут запретить вам программировать на Perl или Python.
Скриптовые языки |
Языки программирования |
Bash (Bourne Again shell), Csh (C shell), JavaScript, Ksh (Korn shell), Lua, MS-DOS batch files, Perl, Python, Ruby, Sh (Bourne shell), Tcl |
Assembler, C, C++, C#, FORTRAN, Forth, Java, LISP, Modula-2, Modula-3, Oberon, Pascal |
Далее - Как определить, когда нужна программа, а когда - скрипт?
Назад - Скриптинг
Содержание
14.3 Как определить, когда нужна программа, а когда - скрипт?
Если уж граница между языками программирования и скриптовыми размылась, соответственно размылась и граница между прикладными задачами. Однако, существует простое правило - используйте ту технику, которая делает ваш труд более продуктивным. Кроме того, никого не волнует, как вы назвали то, что написали - скриптом или программой (зачем автор написал эти главы? - прим. перев.).
И, наконец, некоторые предложения для формирования генеральной линии:
* Если есть потребность выполнять много операций над огромным массивом rpm-пакетов, напишите программу. Она будет быстрее, чем shell-скрипт, который вынужден снова и снова вызывать команду rpm.
* Если вы дружите с каким-либо конкретным языком, используйте его.
* Если нужно выполнять сложные композиции операций - используйте программу.
* Программы во многих случаях предпочтительнее, если нужен сложный графический интерфейс, хотя у Python и Perl есть такие штуки, как PyQt и Perl/Tk
Далее - Основы shell-скриптинга
Назад - Отличительные черты скриптовых языков
Содержание
14.4 Основы shell-скриптинга
Просьба не беспокоиться читателей, никогда не писавших скрипты - это не такое уж мудреное дело. Скрипт, в нашем случае shell-скрипт, это текстовый файл, наполненный командами, которые мы можем отдавать системе с клавиатуры. Различия, которые на самом деле есть, мы рассмотрим ниже.
Далее - Создание скрипта
Назад - Как определить, когда нужна программа, а когда - скрипт?
Содержание
14.4.1 Создание скрипта
Для начала создадим скрипт, состоящий всего из одной команды. Создайте текстовый файл, и введите в качестве текста следующее:
rpm -qa | grep rpm |
Этот скрипт содержит двусоставную команду: rpm -qa выводит список всех пакетов, установленных в системе, grep rpm отфильтровывает для вывода только строки, в которых встречается буквосочетание rpm. Это очень простой скрипт, но на простом примере можно показать особенности работы со скриптами.
Сохраните файл под именем listrpmpkgs, поскольку он выводит список неких пакетов.
Далее - Запуск скрипта
Назад - Основы shell-скриптинга
Содержание
14.4.2 Запуск скрипта
Однажды создав скрипт, вы можете использовать его вновь и вновь. Для запуска можно применить команду sh, как показано ниже, передавая команде имя файла скрипта в качестве параметра:
$ sh listrpmpkgs |
Введите в командной строке команду, записанную в скрипте. Вывод в этих двух случаях не должен отличаться:
$ rpm -qa | grep rpm |
Далее - Проблемы при запуске скрипта
Назад - Создание скрипта
Содержание
14.4.3 Проблемы при запуске скрипта
Запуск скрипта в предыдущем примере требует программы sh (экземпляра командной оболочки Linux). Также файл listrpmpkgs должен быть доступен. Например, если вы сохранили файл в каталоге /home2/bin, тогда для запуска придется использовать команду:
$ sh /home2/bin/listrpmpkgs |
Согласитесь, не очень-то удобно. Кроме того, необходимо все время держать в голове расположение файла. Для того, чтобы это работало лучше, нужно преобразовать скрипт в команду.
Далее - Преобразование скрипта в команду
Назад - Запуск скрипта
Содержание
14.4.4 Преобразование скрипта в команду
Для преобразования скрипта в комнаду, сделайте три простых шага:
1. Добавьте специальную строку в начало файла, чтобы система определяла текст как командный скрипт.
2. Установите права доступа на файл, так, чтобы он был исполняемым.
3. Скопируйте скрипт в каталог с исполняемыми файлами.
В shell-скриптах для комментариев используются символы # в начале строки. Если первая строка начинается с символов #!, командный интерпретатор распознает с помощью этого маркера файл командного скрипта. Текст в строке после символов #! указывает на необходимый для исполнения интерпретатор. В большом количестве случаев мы видим строку #!/bin/sh в начале файла.
Отредактируйте файл скрипта listrpmpkgs, добавив строку, указывающую на интерпретатор:
#!/bin/sh |
Далее, измените права на файл для предоставления возможности его выполнения. Для проверки прав используется команда ls -l:
$ ls -l listrpmpkgs |
Теперь мы имеем команду, которую можно запускать локально. Например:
$ ./listrpmpkgs |
Следующий шаг - скопировать файл в каталог с исполняемыми файлами, который отмечен в системных путях. Для просмотра каталогов системного окружения запустите следующую команду:
$ echo $PATH |
Выберите один из указанных каталогов. Каталог /usr/local/bin - стандартное место для хранения локально созданных команд. Если имеется в виду команда, которая будет использоваться одним пользователем, разумно задействовать каталог под домашним каталогом этого пользователя. /home/ericfj/bin в примере выше - такой каталог. Скопируйте файл скрипта в один из каталогов и все будет готово к запуску.
Введите команду:
$ listrpmpkgs |
В отличие от ОС Windows, Linux-скрипты не обязаны иметь специальные расширения в именах файлов.
Если вы хотите сделать скрипт доступным для всех пользователей, выполните:
$ chmod a+x listrpmpkgs |
В этом случае, скрипт будет доступен для запуска всем.
Далее - Передача параметров в скрипт
Назад - Проблемы при запуске скрипта
Содержание
14.4.5 Передача параметров в скрипт
Скрипт listrpmpkgs, как мы успели заметить, не обладает развитыми вомзожностями. Он содержит и выполняет только одну команду. Мы не можем изменить его поведение, не написав другой скрипт.
Один из путей повышения мощности скрипта - научить его воспринимать опции командной строки. Подобно тому, как поведение утилиты rpm зависит от множества опций, мы можем изменять поведение скриптов, если сможем передавать в них информацию в виде параметров команды.
В языке shell определен ряд специальных переменных, с помощью которых можно передавать значения в сценарий. В таблице ниже перечисляются эти переменные.
Переменная |
Использование |
$0 |
Хранит имя самого скрипта, полученное из командной строки |
$1 |
Первая опция |
$2 |
Вторая опция |
$3 |
Третья опция |
$4 |
Четвертая опция |
$5 |
Пятая опция |
$6 |
Шестая опция |
$7 |
Седьмая опция |
$8 |
Восьмая опция |
$9 |
Девятая опция |
$* |
Все опции |
$# |
Число заданных опций |
Если вы используете C-shell, тогда вместо $# применяется $#argv .
В контексте нашего скрипта, что можно передать команде, которую он содержит, в качестве параметра? Команда всегда ищет строки, в которых есть буквосочетание rpm. Заменив шаблон на переменную, сможем искать что угодно:
#!/bin/sh |
Так теперь выглядит текст скрипта и ищет он подстроки, которые будут заданы в команде после имени файла. Переименуем его в rpmgrep и запустим, например, так:
$ ./rpmgrep python |
($* хранит все опции командной строки в виде строки с пробелами. Но shell не поддерживает больше 9 параметров, если не использовать оператор shift, а grep не поддерживает мультишаблонный поиск, для этого есть egrep. Поэтому, если ввести несколько параметров через пробел, например, ./rpmgrep rpm python, скрипт завершит работу с сообщением об ошибке. Итог - в данном случае в параметре передается "что-то, что ввели после имени файла в виде последовательности символов, не содержащей пробелов" - прим. перев. )
Далее - Файлы в rpm-пакетах
Назад - Преобразование скрипта в команду
Содержание
14.5 Файлы в rpm-пакетах
Когда есть необходимость работать со множеством файлов rpm-пакетов, например, с пакетами, не установленными в систему, вы можете обнаружить, что вводите одни и те же команды вновь и вновь при переходе к следующему пакету. Стандартная задача - вам нужно знать, какие зависимости имеются у пакета. Можно, конечно, ввести команду rpm с соответствующими опциями несколько раз, а можно написать коротенький скрипт с нужными опциями:
#!/bin/sh |
Этот скрипт принимает имя файла rpm-пакета в качестве параметра. Назовем скрипт rpmdepend и запустим таким образом:
$ rpmdepend vim-common-6.1-14.i386.rpm |
Смысл: мы получили список зависимостей пакета, не обращаясь к БД RPM, из самого пакета.
Другая часто встречающаяся задача - вывод листинга файлов пакета вместе с информацией о пакете. Это реально помогает в работе, поскольку многие пакеты имеют ничего не говорящие об их функциональности имена, например dia или anakonda.
#!/bin/sh |
Этот скрипт (назовем его rpminfo) может вывести много строк, поэтому вывод перенаправлен в просмотрщик less. Например:
$ ./rpminfo perl-XML-Dumper-0.4-22.noarch.rpm |
Таким образом, мы узнали функции пакета и какие файлы он установит в систему.
Далее - Запросы к БД RPM
Назад - Передача параметров в скрипт
Содержание
2.1.1 Введение в структуру файла пакета
RPM предоставляет возможности установки, обновления и удаления пакетов. В обычном случае каждый пакет представляет собой приложение и некоторое количество ассоциированных с ним файлов. Например, веб-сервер Apache в полной комплектации это сам сервер, множество файлов конфигурации, обширная документация, дополнения, etc. Все это помещается в один RPM пакет.
Одно из преимуществ системы RPM - это то, что каждый rpm-файл (а это один файл с точки зрения операционной системы) содержит весь комплект файлов приложения. Например, следующий файл содержит пакет xcopilot:
xcopilot-0.66-3.i386.rpm |
В соответствии с соглашениями об именовании, обсуждаемыми в Разделе 2, этот пакет представляет программу xcopilot версии 0.6.6, сборка 3, целевая архитектура i386.
С помощью одной команды этот файл можно скопировать на другую систему и установить его, и, также с помощью одной команды его можно будет удалить.
14.6 Запросы к БД RPM
Кроме извлечения информации непосредственно из файлов rpm-пакетов, с помощью скриптов можно автоматизировать запросы к БД RPM. Этот подход сильно облегчает составление сложных запросов с использованием опции --queryformat , особенно если нет времени запоминать все форматы.
Далее - Запрос списка пакетов, установленных единовременно
Назад - Файлы в rpm-пакетах
Содержание
14.6.1 Запрос списка пакетов, установленных единовременно
Если вы хотите получить список пакетов, установленных в рамках одной транзакции и имеющих одинаковый идентификатор транзакции, можно использовать скрипт rpmtran, показанный ниже:
#!/bin/sh |
Этот скрипт использует формат запроса для получения идентификатора транзакци (transaction ID, TID) определенного пакета. Имя пакета передается в параметре командной строки. Затем выводится список всех пакетов с таким TID. Это пакеты, установленные одновременно. Пример использования:
$ ./rpmtran tcl |
Далее - Чтение html-документации о пакете
Назад - Запросы к БД RPM
Содержание
2.1.2 Формат файла rpm
rpm-пакеты содержат некоторое количество служебной информации и файлы, которые должны быть установлены в систему. Файл пакета, таким образом, состоит из нескольких частей, в частности бинарного заголовка, в котором в жестко заданных полях в определенном формате хранится информация о пакете, и cpio-архива, представляющего собой дерево каталогов и файлов проекта.
Например, служебное поле NAME хранит имя пакета, а опциональное поле PRE - прединсталляционный скрипт, который будет выполнен до копирования файлов в систему.
Полный формат rpm-файла - это четыре секции. Первая секция - начальный идентификатор, помечающий файл как rpm-пакет. Секция идентификатора создается соответствующей версией системы RPM, установленной в ОС, которая использовалась для сборки. Последующие секции, это: сигнатура, бинарный хедер и нагрузка. Из них только секция нагрузки несет в себе файлы и каталоги.
Сигнатура следует за начальным идентификатором. Подобно любой цифровой подписи сигнатура позволяет оценить в последствии целостность пакета. С помощью сигнатуры нельзя понять, содержит ли пакет ошибки :), но можно установить, верный ли rpm-архив вы загрузили.
Сигнатура является результатом выполнения некоторой функции над хедером и нагрузкой. Это может быть криптофункция вроде PGP или хэш-функция, например дайджест MD5.
Хедер содержит ноль или более полей данных, описывающих пакет. В служебные поля заключается информация о правах на программное обеспечение, версия, описание пакета и некоторые другие сведения.
Нагрузка содержит файлы, которые реально используются в проекте. Когда отдана команда на установку пакета, эти файлы копируются в систему в нужные места. Все дерево каталогов и файлов пакета заархивировано и сжато gzip.
Для извлечения архива из пакета используется утилита rpm2cpio (описывается далее в этом разделе в главе "Другие RPM команды").
Далее - Бинарные rpm и rpm с исходным кодом
Назад - Введение в структуру файла пакета
Содержание
14.6.2 Чтение html-документации о пакете
Можно комбинировать команды rpm с другими командами. Например, rpm -qd выводит файлы документации, содержащиеся в пакете. Если имеется документация в формате HTML, можно отобразить документ в Web-браузере. В соответствии с соглашениями, индекс документации должен называться index.php . С учетом этого обстоятельства создадим скрипт rpmmoz, запускающий браузер для просмотра найденных документов:
#!/bin/sh |
Скрипт ищет документацию пакета с заданным именем, находит первый по порядку index.php и запускает для его просмотра фоновый процесс браузера (Mozilla или что у вас сконфигурировано по умолчанию). После запуска скрипта в консоли можно увидеть следующее:
$ ./rpmmoz rpm-devel |
Далее - Раздел 15. Программирование RPM на C
Назад - Запрос списка пакетов, установленных единовременно
Содержание
15. Программирование RPM на C
С-библиотека RPM позволяет разработчику выполнять все операции rpm из своих собственных программ на C или C++.
Сама по себе утилита rpm работает быстро и она проста для пользователя. Зачем тогда выполнять операции RPM из сторонних программ?
Можно привести по меньшей мере несколько резонов:
* Скорость: если есть нужда выполнять операции над большим массивом пакетов, например, верификацию большого числа пакетов, выполнение функций из одной программы несколько ускорит процесс по сравнению с запуском rpm для каждого пакета.
* Пользовательские опции: если вы хотите делать что-то, что rpm не предоставляет, или что сделать не просто, вы можете захотеть сделать это по-своему.
* Удобство: если необходимо обработать много пакетов быстро и с нестандартными опциями, возможно, удобнее это сделать из программы, спроектированной специально для выполнения узкоспециализированной операции. Перед написанием такой программы убедитесь, что никто не написал скрипт, решающий данную задачу. Поправить готовый скрипт, конечно, выгоднее по затратам, чем написать приложение.
* Установка программ: мир Windows имеет промышленные стандарты для графических установщиков программ, как, например, InstallShield или InstallAnywhere. С другой стороны, RPM ориентирована на автоматизацию установки с помощью rpm. Соединяя положительные черты того и другого, напишите графическое приложение для установки вашего приложения с помощью доступных в RPM функций.
* Интеграция с окружением: вы можете захотеть наилучшим образом интегрировать RPM со средами GNOME или KDE.
* Работа с другими языками: вы можете захотеть получить доступ к функциям C-библиотеки RPM из языков, отличных от C, Python или Perl, например, из Ruby или Tcl. Используя библиотеку, можно создать привязки для этих языков.
Далее - Установка окружения для C-программирования
Назад - Чтение html-документации о пакете
Содержание
15.1.1 Установка окружения для C-программирования
Как минимум, нужно иметь компилятор, gcc, и текстовый редактор.
Пакет gcc имеет ряд зависимостей. Убедитесь, что все они установлены.
В качестве текстовых редакторов разработчики используют vi или emacs, или один из редакторов в графической системе, таких как gedit (а кто-то любит mcedit - прим. перев.).
Установленное окружение сборки потребует лишь доступ к RPM библиотеке из пакета разработки RPM.
Далее - Установка окружения разработки RPM
Назад - Программирование RPM на C
Содержание
15.1.2 Установка окружения программирования RPM
Для разработки программ, использующих библиотеку RPM, необходимо установить пакет rpm-devel. По версии он должен совпадать с пакетом rpm, установленным в вашей системе.
Для уверенности в совместимости следует линковать те же библиотеки, что использует утилита rpm. Установите их из вашего дистрибутива Linux. Для древних версий дистрибутивов Red Hat используйте ftp://ftp.rpm.org/pub/rpm/dist/ . Этот сайт хранит версии всех библиотек RPM вплоть до 1996 года (по понятиям истории Linux - верхний палеолит).
Далее - Использование библиотеки RPM
Назад - Установка окружения для C-программирования
Содержание
15.1.3 Использование библиотеки RPM
Все программы на C, использующие библиотеку RPM, нуждаются во включении заголовочного файла rpmcli.h, определяющего высокоуровневый API. Он базируется на опциях командной строки для утилиты rpm. Таблица ниже описывает другие нужные файлы для построения основных подсистем системы RPM.
Файл |
Определяет |
rpmdb.h |
Функции доступа к БД RPM |
rpmio.h |
Процедуры ввода/вывода RPM |
popt.h |
Функции обработки опций командной строки |
Кроме того, некоторое количество заголовочных файлов определяют основные объекты данных в RPM и функции для работы с этими объектами. Таблица ниже описывает эти файлы.
Файл |
Определяет |
rpmts.h |
Объекты транзакций |
rpmte.h |
Элементы транзакций (пакеты) |
rpmds.h |
Информация о файлах |
header.h |
Заголовки пакетов |
В большинстве rpm-дистрибутивах заголовочные файлы RPM расположены где-то в районе /usr/include/rpm . Для установки более точного места расположения используйте команду:
$ rpm ql rpm-devel |
Далее - Компиляция и линковка RPM программ
Назад - Установка окружения программирования RPM
Содержание
15.1.4 Компиляция и линковка RPM программ
Программы, использующие rpmlib API, это обычные программы на C . Нам нужно включить заголовочные файлы, функции из которых требуются, и выполнить операцию связывания (линковку) для правильных библиотек.
15.1.4.1 Подключаемые файлы
Подключаемые файлы RPM находятся в каталоге /usr/include/rpm , нужно включить этот каталог в группу каталогов, которые компилятор обходит в поисках хэдеров, с помощью опции -I :
$ gcc I/usr/include/rpm c rpm1.c |
Это, в частности, означает, что разработчик может установить хэдеры в другой каталог, изменив в команде путь к нему.
Для облегчения отладки, вы, возможно, захотите включить опцию -Wall (выводить все предупреждения) и -g (компилировать с отладочной информацией). Например:
$ gcc -Wall -g I/usr/include/rpm c rpm1.c |
15.1.4.2 Библиотеки
Основная библиотека RPM - librpm.a , или ее разделяемая версия. Для выполнения каких-либо значимых операций нужны и другие библиотеки:
Библиотека |
Использование |
rpm |
Основная библиотека RPM |
rpmdb |
Библиотека БД RPM |
rpmio |
Библиотека ввода/вывода |
popt |
Библиотека для разбора опций командной строки |
Если из своей программы вы собираете rpm-пакеты, потребуется еще библиотека rpmbuild . Для компиляции и линковки простой программы потребуется отдать команду, подобную следующей:
gcc -I/usr/include/rpm -o program program.c lrpmbuild \ |
В некоторых версиях Linux или в других ОС, возможно потребуется включить вспомогательные библиотеки:
gcc -I/usr/include/rpm -o program program.c L/opt/lib/rpm \ |
Если библиотеки установлены в нестандартный каталог, необходимо использование опции -L для указания путей:
gcc -I/usr/include/rpm -o program program.c L/opt/lib/rpm \ |
Начиная с версии RPM 4.2 подключается только библиотека rpm . Все остальные библиотеки будут включены автоматически, если они нужны.
Далее - Получение информации о RPM-окружении
Назад - Использование библиотеки RPM
Содержание
15.1.5 Получение информации о RPM окружении
Значительная часть конструкции системы RPM лежит в области системно-зависимых конфигураций. К системно-зависимым параметрам относится, например, информация о платформе, о совместимых платформах, о расположении различных файлов. Файлы настройки RPM - rc и macros поддерживают сотни опций, настраивающих RPM на определенную систему, и многие настройки не могут быть оставлены в значениях по умолчанию.
RPM программы на C нуждаются в доступе к системным настройкам RPM для проверки правильности установок, адекватных системной архитектуре и текущей инсталляции. Для запуска программы, системные установки должны быть считаны (возможно, из всех конфигурационных файлов). Для выполнения этой операции вызывается rpmReadConfigFiles :
int rpmReadConfigFiles(const char *files, const char *target); |
Параметр files содержит разделенный двоеточиями список файлов, которые отвечают за системную конфигурацию. Параметр target содержит целевую платформу. Для использования установок по умолчанию достаточно, чтобы оба параметра имели значение NULL .
Функция rpmReadConfigFiles возвращает 0 в случае успеха и -1 в случае ошибки.
Единожды считав конфигурационные файлы, мы можем получить доступ к значениям переменных конфигурации, или, например, распечатать их.
15.1.5.1. Вывод конфигурации
Для вывода конфигурации используется rpmShowRC :
int rpmShowRC(FILE* output); |
Чтобы задать выходной файл, устанавливается значение единственного параметра, например, для стандартного вывода:
rpmShowRC( stdout ); |
Функция rpmShowRC всегда возвращает 0.
Для контролирования вывода rpmShowRC и других библиотечных функций используют установку уровня многословности вывода с помощью функции rpmSetVerbosity :
rpmSetVerbosity(RPMMESS_NORMAL); |
Возможные варианты многословности вывода:
Уровень |
Что означает |
RPMMESS_FATALERROR |
Сообщения о критических ошибках и все не менее значимое |
RPMMESS_ERROR |
Сообщения об ошибках вообще и все не менее значимое |
RPMMESS_WARNING |
Все предупреждения и все не менее значимое |
RPMMESS_QUIET |
То же, что и RPMMESS_WARNING |
RPMMESS_NORMAL |
Только существенные сообщения |
RPMMESS_VERBOSE |
Все информационные сообщения |
RPMMESS_DEBUG |
Вся отладочная информация и все не менее значимое |
Пример программы, использующей вышеописанные функции:
/* Show the rpmrc settings. */ |
Скомпилируйте программу с помощью команды:
$ cc -I/usr/include/rpm -o rpm1 rpm1.c -lrpm -lrpmdb -lrpmio lpopt |
После запуска программы мы должны увидеть содержимое файлов конфигурации, выводимое на консоль.
15.1.5.2. Разворачивание значений макросов
В совокупности rc-файлы и файлы макросов содержат множество определений, которые можно использовать для обращения к значениям установок. Термин макроопределение или макрос используются потому, что значение может быть не просто строкой. Может быть макрос, ссылающийся на другие макросы, возможны и другие сложноподчиненные случаи. Базовый синтаксис макросов:
%name_of_macro |
Например:
%_target |
Множество макросов начинаются с нижнего подчеркивания.
Всегда можно раскрыть макрос с помощью команды rpm --eval :
$ rpm --eval %_target |
Обратиться к значению макроса можно так:
%{_target} |
Такой синтаксис облегчает задачу включения макросов в комбинации с обычным текстом и другими макросами.
15.1.5.2. Разворачивание макросов внутри кода
Для получения значения макроса в C-программе используют rpmExpand. Функция rpmExpand может развернуть один или несколько макросов, возвращая развернутое значение. Функции можно передать различное число параметров. Список должен быть инициализирован значением NULL .
char* rpmExpand (const char *arg,...); |
Для очистки после возврата данных из rpmExpand следует использовать free .
Программа в примере ниже забирает из командной строки первый параметр и пытается его развернуть как макрос:
/* Show some macro settings. */ |
Скомпилируйте программу как показано выше. При запуске после имени исполняемого файла введите имя макроса для разворачивания. Например:
$ ./rpmexpand %_target |
Можно задать несколько имен макросов в связке:
$ ./rpmexpand %_builddir/%_target |
Для проверки работы программы можно использовать rpm --eval :
$ rpm --eval %_builddir/%_target |
Далее - Мощь popt
Назад - Компиляция и линковка RPM программ
Содержание
15.2 Мощь popt
Popt предоставляет мощную библиотеку для разбора командных строк. Она позволяет утилите rpm иметь массу опций, сочетание которых может применятся весьма эффективно. Popt можно использовать в качестве самостоятельной библиотеки, или применять ее для разбора опций командных строк rpm.
Основное назначение библиотеки - разобрать значение опций командной строки для C-программы, передав их в виде аргументов (argc, argv). Они используются в так называемой таблице опций, которая хранит и описывает все возможные значения.
Основные преимущества popt по сравнению с более простыми библиотеками, типа getopt, это возможность обработки комплексных аргументов и возможность определения псевдонимов. Утилита rpm поддерживает три вида поведения для опции -i, они зависят от контекста (установка пакета, получение информации о пакете в составе сложного запроса, выполнение стадии инсталляции как части процесса сборки). Все эти случаи надо уметь обрабатывать.
Popt поддерживает традиционную для Unix форму коротких опций, типа -U, и обычную для GNU форму, как --upgrade. Используя popt, можно для любой опции определить обе формы, длинную и короткую. Кроме того, опции командных строк могут быть отдельными флагами, например, -v для многословного вывода, или опциями, через которые передаются различные значения, например, -f для передачи имени файла, которое может быть любым.
Далее - Псевдонимы popt
Назад - Получение информации о RPM окружении
Содержание
15.2.1. Псевдонимы popt
Возможность определять псевдонимы (алиасы - обычный жаргонизм разработчиков) есть важная особенность popt. Псевдонимы позволяют определять опции как ссылки на группу других опций. В качестве простейшего примера: --upgrade и U выполняют одно и то же действие, одну опцию можно рассматривать как ссылку на другую.
У RPM имеется специальный файл (обычно /usr/lib/rpm/rpmpopt-%version), определяющий несколько сотен псевдонимов popt для конфигурации опций командной строки rpm. Например:
Rpm alias requires --qf \ |
Этот пример определяет rpm --requires как реальный запрос с использованием опций --qf или --queryformat.
Далее - Программирование с popt
Назад - Мощь popt
Содержание
15.2.2. Программирование с popt
Для использования popt в программе, мы должны заполнить таблицу опций и вызвать poptGetContext. Функция poptGetContext разбирает опции командной строки и возвращает poptContext, неопределенный тип данных, которые нужно передавать в качестве параметров в разные функции popt. poptContext хранит состояние процедуры разбора. Это позволяет вызывать библиотеку, передавая ее функциям различные и многочисленные наборы аргументов. Каждый набор имеет собственный контекст, благодаря чему все данные можно держать отдельно во избежании смешивания с другими.
Базовый синтаксис poptGetContext:
poptContext poptGetContext (const char * name, |
Использование функций popt требует включения хэдера popt.h:
#include <popt.h> |
Flags должен быть битовой маской, возможные значения перечислены ниже.
Флаг |
Значение |
POPT_CONTEXT_NO_EXEC |
Игнорировать исполняемые расширения |
POPT_CONTEXT_KEEP_FIRST |
Обрабатывать argv[0] (имя самой команды) как опцию |
POPT_CONTEXT_POSIXMEHARDER |
Не позволять опциям следовать за аргументами |
Когда использование poptContext завершилось, необходимо освободить память вызовом poptFreeContext:
poptContext poptFreeContext(poptContext context); |
Также возможно заполнить контекст из установок, записанных в файле, с помощью poptReadConfigFile:
int poptReadConfigFile(poptContext context, |
15.2.2.1. Заполнение таблицы опций
Программа нуждается в доступе к таблице, которая определяет все возможные опции. Эта "таблица" - массив структур, где каждая структура задает одну опцию. Формат структуры для отдельной опции такой:
struct poptOption { |
Соответственно, longName определяет длинную форму опции, shortName - короткую, однобуквенную форму. Для того, чтобы задать отсутствие короткой опции, надо поместить в это поле нулевой символ, '\0'. Например, опция --rebuilddb не имеет короткого варианта.
Поле descrip содержит короткое описание опции, argDescrip - типы значений, или NULL, если опция не имеет передаваемых значений.
Поле argInfo содержит флаг, указывающий библиотеке, как обрабатывать данную опцию. По крайней мере, необходимо определить тип опции. Также можно задать специальные флаги обработки. Ниже описываются типы опций.
Тип |
Значение |
Определяет |
POPT_ARG_NONE |
0 |
Данные не передаются в аргументе, это отдельная опция, как -v |
POPT_ARG_STRING |
1 |
Аргумент обрабатывается как строка |
POPT_ARG_INT |
2 |
Аргумент обрабатывается как целое |
POPT_ARG_LONG |
3 |
Аргумент обрабатывается как длинное целое |
POPT_ARG_INCLUDE_TABLE |
4 |
Аргумент помещается в таблицу |
POPT_ARG_CALLBACK |
5 |
Аргумент помещается в вызывающую функцию |
POPT_ARG_INTL_DOMAIN |
6 |
Устанавливает домен перевода |
POPT_ARG_VAL |
7 |
Использовать значение из поля val для аргумента |
POPT_ARG_FLOAT |
8 |
Аргумент обрабатывается как число с плавающей точкой |
POPT_ARG_DOUBLE |
9 |
Аргумент обрабатывается как число с плавающей точкой двойной точности |
Используйте эти константы, определенные в popt.h, вместо указывания реальных чисел.
В зависимости от типа определенного в поле argInfo, popt будет интерпретировать неспецифицированное поле указателя arg различными путями. Использование указателя позволяет библиотеке автоматически обновлять переменные программы, базируясь на установках опций командной строки.
Вы можете задать в поле arg значение NULL. В этом случае popt не будет задавать никаких значений.
Тип POPT_ARG_NONE указывает, что опция не имеет значений, как, например, -v.
Если тип аргумента POPT_ARG_NONE, popt установит arg в значение 1, если опция присутствует в командной строке.
15.2.2.2. Обработчики обратного вызова (callback-функции) popt
Тип POPT_ARG_CALLBACK указывает, что в поле arg хранится указатель на callback-функцию, определенную следующим образом:
typedef void (*poptCallbackType) (poptContext con, |
Reason будет одним из следующих значений:
enum poptCallbackReason { |
Поле data содержит значение поля descrip из структуры poptOption. В этом поле вы можете установить указатель на какие-то произвольные данные.
Callback-функции удобны, если вы используете вложенные таблицы опций. В этом случае вы можете поместить код обработки для вложенных опций в callback.
15.2.2.3. Специальные флаги таблицы опций
В дополнение ко всему вышеперечисленному, вы можете определить специальные битовые флаги, определяющие расширенную информацию для обработки каждой опции. Сочетание этих флагов с типом значения происходит с использованием логического оператора OR:
* POPT_ARGFLAG_ONEDASH позволяет использовать longName с одним или двумя дефисами, например, -upgrade или --upgrade.
* Для опций типа битовых масок флаги POPT_ARGFLAG_OR, POPT_ARGFLAG_NOR, POPT_ARGFLAG_AND, POPT_ARGFLAG_NAND, и POPT_ARGFLAG_XOR говорят popt применять заданную операцию, то есть соответственно OR, NOR, AND, NAND, или XOR, к значению, если оно установлено. Флаг POPT_ARGFLAG_NOT вначале инвертирует значение.
* Также можно использовать макрос POPT_BIT_SET для установки бита, а POPT_BIT_CLR - для очистки бита.
* Флаг POPT_ARGFLAG_OPTIONAL указывает на то, что значение аргумента опционально.
* Флаг POPT_ARGFLAG_DOC_HIDDEN говорит popt скрыть эту опцию при отображении справочной информации. Другими словами, это опция для внутреннего использования.
* Редко используемый флаг POPT_ARGFLAG_STRIP заставляет popt игнорировать опцию.
* Флаг POPT_ARGFLAG_SHOW_DEFAULT говорит popt показать значение опции, которым она инициализирована по умолчанию, при выводе справочной информации.
15.2.2.4. Магические опции
В программировании RPM разработчики обычно завершают таблицу опций тремя специальными опциями: POPT_AUTOALIAS, POPT_AUTOHELP, и POPT_TABLEEND. Опция POPT_AUTOALIAS определяет таблицу псевдонимов:
#define POPT_AUTOALIAS { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptAliasOptions, \ |
Эта опция ссылается на таблицу poptAliasOptions. Вы можете использовать тип argInfo POPT_ARG_INCLUDE_TABLE для включения другой таблицы опций. Эти опции будут заполнены псевдонимами popt. Кроме того, внутри RPM программ еще одна таблица, rpmcliAllPoptTable, хранит группы опций, общие для всех RPM программ и всех режимов работы rpm.
Опция POPT_AUTOHELP поддерживает стандартную опцию help. Макрос POPT_AUTOHELP автоматически поддерживает использование опций -?, --help, и --usage:
#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \ |
Опция POPT_TABLEEND определяет пустую опцию для обозначения конца таблицы. Включение пустой опции в конце - обязательное требование:
#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL } |
15.2.2.5. Разбор опций командной строки
Когда контекст определен, далее нужно обойти параметры командной строки. Для обхода используется poptGetNextOpt:
int poptGetNextOpt(poptContext context); |
Если встречаются ошибки, poptGetNextOpt в качестве кода возврата возвращает отрицательный код ошибки. Если при обходе появилась пустая опция (признак конца таблицы опций), возвращается -1. Коды ошибок:
Код |
Означает |
POPT_ERROR_NOARG |
Опция требует аргумента, но он отсутствует |
POPT_ERROR_BADOPT |
Аргумент не может быть разобран |
POPT_ERROR_OPTSTOODEEP |
Псевдонимы имеют слишком большой уровень вложенности |
POPT_ERROR_BADQUOTE |
Начальные и конечные кавычки не заданы |
POPT_ERROR_BADNUMBER |
Аргумент не может быть приведен к номеру |
POPT_ERROR_OVERFLOW |
Номер агрумента выходит из диапазона возможных значений |
POPT_ERROR_ERRNO |
Системный вызов вернул ошибку в errno |
15.2.2.6. Обзор опций командной строки
В нормальных обстоятельствах poptGetNextOpt разбирает все опции командной строки и возвращает -1. Если вы нуждаетесь в упрощении, можно задать указатели на переменные, переданные в таблицу опций. Если необходимы некоторые специальные действия при обработке опций, которые не выполняет popt, например, нужно обработать опции с типом POPT_ARG_NONE, тогда poptGetNextOpt возращает однобуквенную опцию.
В этом случае можно вызвать poptGetNextOpt в цикле:
while ((option = poptGetNextOpt(context) ) { |
Внутри цикла мы можем вызвать poptGetOptArg для получения значения аргумента:
char * poptGetOptArg(poptContext context); |
Для перезапуска обработки опций используется poptResetContext:
void poptResetContext(poptContext context); |
В целом, popt просто ищет аргументы, начинающиеся с черточки -. В большинстве приложений командной строки имеется экстрааргумент в конце команды, например, список файлов. popt не обрабатывает их, но может предоставить возможности такой обработки. Для обхода экстрааргументов вызывается poptGetArg:
char * poptGetArg(poptContext context); |
Чтобы получить все экстрааргументы, нужно вызывать функцию до тех пор, пока она не вернет NULL.
Чтобы перейти к следующему аргументу, используют poptPeekArg, при этом не отмечая аргумент, как прошедший обработку:
char * poptPeekArg(poptContext context); |
Или же можно получить весь список экстрааргументов путем вызова poptGetArgs:
char ** poptGetArgs(poptContext context); |
15.2.3. Обработка ошибок
Внутри цикла обработки опций можно вызвать poptBadOption для получения опции, которая оказалась "плохой", и poptStrerror для вывода сообщения, соответствующего данной ошибке.
В poptBadOption необходимо передать контекст и битовую маску флагов. Для указания отсутствия флагов передается 0, или POPT_BADOPTION_NOALIAS, который заставляет popt вернуть не значение, определенное в алиасах, а саму опцию. Для лучшего протоколирования ошибки в этом случае poptBadOption вернет опцию, максимально похожую на то, что ввел пользователь, если нет возможности вернуть в точности то, что он ввел.
Синтаксис:
char * poptBadOption(poptContext context, int flags); |
Для получения стандартного сообщения о конкретной ошибке, в
poptStrerror нужно передать ее код:
const char * poptStrerror(const int error_code); |
Скомбинировав возможности можно вывести ошибку и ее код, как показано в примере:
fprintf( stderr, "Error with option [%s]\n %s", |
Для вывода стандартной помощи (сообщения об использовании) применяется функция poptPrintUsage:
void poptPrintUsage(poptContext context, |
Эта функция выводит помощь, когда пользователь задал в командной строке неверные опции или не минимально достаточный их набор.
Далее - Работающий пример
Назад - Программирование с popt
Содержание
15.2.4. Работающий пример
Сведем всю уже имеющуюся у нас информацию в программу, демонстрирующую возможности popt.
Программа popt1.c :
/* Processes command-line options. */ |
Этот пример определяет collback, но, с другой стороны, использует простейший подход для обработки опций командной строки. Программа просто позволяет popt поместить значения опций в таблицу опций. В большинстве случаев потребуется более сложный подход к обработке опций.
Скомпилируем программу:
gcc -I/usr/include/rpm -o popt1 popt1.c -lpopt |
При запуске программы используйте различные опции. Если использовать все опции, получим примерно следующий вывод:
$ ./popt1 --int 42 -p --file filename1 |
После обработки опции имеют значения:
intVal holds 42 |
Этот пример использует короткую форму print, -p, и длинные формы для двух других опций. Библиотека popt также предоставляет вывод помощи с использованием макроса POPT_AUTOALIAS. Для получения помощи нужно ввести --help или -?:
$ ./popt1 --help |
Далее - Обработка опций командной строки rpm
Назад - Обработка ошибок
Содержание
15.2.5. Обработка опций командной строки rpm
C-библиотека RPM широко использует popt для обработки опций командной строки утилиты rpm. Функции библиотеки RPM, например, rpmcliInit, которая устанавливает окружение командной строки rpm, требуют наличие таких записей в таблице опций, которые определяют опции командной строки для вашей программы.
Для написания простой программы, обрабатывающей стандартные опции командной строки rpm, потребуется определить следующую таблицу опций:
static struct poptOption optionsTable[] = { |
Затем инициализируйте свою программу с помощью вызова rpmcliInit:
poptContext rpmcliInit(int argc, char *const argv[], |
При вызове rpmcliInit устанавливаются все переменные для стандартных опций командной строки rpm.
Например, чтобы увидеть значение флага многословности, нужно вызвать rpmIsVerbose:
int rpmIsVerbose(); |
После того, как программа, использующая rpmcliInit, завершила работу, вызовите rpmcliFini для очистки глобальных данных:
poptContext rpmcliFini(poptContext context); |
rpmcliFini возвращает NULL.
Далее - Работа с rpm-файлами
Назад - Работающий пример
Содержание
15.3. Работа с rpm-файлами
C-библиотека RPM предоставляет функции как доступа к БД RPM, так и чтения файлов rpm-пакетов. Таким образом, из своих программ с помощью библиотеки мы можем выполнять все действия утилит rpm и rpmbuild. Однако, есть задачи, которые таким путем выполнить легче, чем некоторые другие. Например, если требуется разработать приложение для комплексной установки множества пакетов или программу, которая будет поддерживать множество различных систем в актуальном состоянии так, чтобы выполнялись требования по версиям пакетов, возможно, что вам захочется взглянуть на такие инструменты, как Python RPM API, вместо C-библиотеки RPM.
Далее - Открытие rpm-файла
Назад - Обработка опций командной строки rpm
Содержание
15.3.1. Открытие rpm-файла
Первое, что требуется для чтения rpm-файла, это открыть его. Используйте Fopen:
FD_t Fopen(const char * path, |
Fopen работает как стандартная C-функция fopen(3).
Далее - Чтение начального идентификатора rpm и сигнатуры
Назад - Работа с rpm-файлами
Содержание
15.3.2. Чтение начального идентификатора rpm и сигнатуры
Вспомним структуру файла rpm-пакета (Формат файла rpm) - начальный идентификатор, подпись (сигнатура), заголовок пакета (бинарный хэдер), нагрузка (cpio-архив с файлами пакета).
Наиболее важная информация о rpm-файле находится в хэдере. Она-то в основном и требуется для работы с пакетами. Однако, чтобы установить указатель смещения в позицию начала чтения хэдера, все равно потребуеся прочитать начальный идентификатор и подпись, даже если они не интересуют вашу программу. Для чтения начального идентификатора применяют функцию readLead:
int readLead(FD_t fd, struct rpmlead *lead); |
Функция readLead возвращает 0 в случае успешного считывания начального идентификатора и 1 в случае ошибки. Считанными данными заполняется структура rpmlead:
struct rpmlead { |
Для чтения до конца сигнатуры вызывается rpmReadSignature:
rpmRC rpmReadSignature(FD_t fd, |
Возможные коды возврата показаны ниже:
Code |
RPMRC_OK |
RPMRC_BADMAGIC |
RPMRC_FAIL |
RPMRC_BADSIZE |
RPMRC_SHORTREAD |
После завершения чтения подписи можно считывать содержимое полей хэдера.
15.3.3 Чтение хэдера
Информация, заключенная в хэдере, содержит имя пакета, версию, пост- и пре-инсталляционные скрипты, и тому подобные данные. Для чтения содержания хэдера вызывается headerRead. В случае успеха функция возвращает так называемый объект хэдера. Из объекта можно читать значения переменных.
Header headerRead(FD_t fd, |
Наиболее сложным в вызове headerRead является необходимость передачи в функцию флага "магического" числа. Этот флаг должен иметь значение HEADER_MAGIC_YES если хэдер имеет набор магических чисел, или HEADER_MAGIC_NO - если не имеет. Если предположение о нужном значении флага было неверным, функция вернет ошибку. Для обхода этой проблемы можно получить старший номер версии RPM из начального идентификатора для сравнения:
Header header = headerRead(fd, (lead.major >= 3) ? |
Для чтения значений переменных из хэдера используют headerGetEntry. В ее вызове нужно передать объект хэдера и идентификатор поля. В результате функция вернет тип поля, указатель на значения поля, и счетчик значений в этом поле.
nt headerGetEntry(Header header, |
headerGetEntry возвращает 1 в случае успеха и 0 в случае ошибки. В случае успеха указатель будет указывать на полученные данные с типом параметра, установленным в одно из значений:
enum rpmTagType_e { |
Если тип оказался RPM_STRING_ARRAY_TYPE или RPM_BIN_TYPE, потребуется освободить указатель, для чего вызывается headerFreeData:
void* headerFreeData(const void *pointer, |
Для передачи нам нужны указатель на данные и флаг типа. Можно вызвать headerFreeData для обоих. Функция не делает ничего, если тип не требует освобождения указателя.
Вызывая headerGetEntry, мы должны знать имя поля, из которого хотим получить значения. Это имя является идентификатором для --queryformat. Заголовочный файл rpmlib.h содержит список имен полей.
Пример ниже показывает, как можно получить запись строкового типа из хэдера:
/* Function to read a string header entry. */ |
Для чтения значений из поля передаем объект хэдера и ID поля. Например:
char* name = readHeaderString(header, RPMTAG_NAME); |
Для получения имени, версии и релиза можно вызвать функцию headerNVR:
int headerNVR(Header header, |
Когда все нужные значения считаны, следует освободить память, занимаемую объектом хэдера:
Header headerFree(Header header); |
Вызов headerFree возвращает NULL, это можно использовать для установки исходного указателя в NULL во избежании случайного использования. Например:
header = headerFree(header); |
Далее - Короткий путь к информации хэдера
Назад - Чтение начального идентификатора rpm и сигнатуры
Содержание
15.3.4. Короткий путь к информации хэдера
Имеется возможность читать из хэдера с использованием специального метода rpmReadPackageFile:
int rpmReadPackageFile(rpmts ts, |
Для передачи в качестве аргументов потребуются сет транзакции и открытый файл. Имя файла используется только для вывода ошибок. В случае успеха rpmReadPackageFile заполняет объект хэдера из файла пакета. Возвращаемое значение - 0 в случае успеха.
Для передачи сета транзакции его нужно создать с помощью rpmtsCreate. Эта функция описывается далее в главе "Программирование с БД RPM".
В большом количестве случаев возможен вызов rpmReadPackageFile без вызова readLead, rpmReadSignature, и headerRead, поскольку rpmReadPackageFile также проверяет целостность пакета.
2.1.3 Бинарные rpm и rpm с исходным кодом
Существует два типа пакетов rpm - бинарные и с исходным кодом. Бинарные пакеты содержат исполняемые файлы, скомпилированные для определенной процессорной архитектуры. Например, веб-сервер Apache, скомпилированный для работы на платформе Intel Pentium не будет работать на Sharp Zaurus, в котором используется процессор ARM. Для использования на обоих системах необходимо иметь два бинарных пакета и только один пакет с исходным кодом.
Бинарные пакеты
Бинарные пакеты содержат полный набор приложений и библиотек, собранных под определенную процессорную архитектуру. Работа этих приложений часто зависит от других приложений (библиотек), которые в свою очередь содержатся в иных бинарных rpm-пакетах.
Хотя большАя часть пакетов включает приложения, некоторые пакеты содержат только библиотеки функций. Например, библиотека SDL, поддерживающая графические потребности множества игр, может быть упакована в rpm файл. Пакеты библиотек позволяют поддерживать работу множества приложений с помощью одной библиотеки (так называемые shared libraries). Поэтому как правило библиотеки входят в пакеты, не содержащие приложений.
В дополнение к бинарным пакетам для определенных архитектур, RPM поддерживает концепцию платформно-независимых бинарных пакетов, так называемых noarch-пакетов, предоставляющих библиотеки, или, например, коллекции скриптов, работа которых не зависит от конкретной платформы. Приложения, написанные на Perl, Python или shell не связаны необходимостью компиляции. Многие приложения Java также не зависят от платформы.
Пакеты с исходным кодом
Упоминавшийся выше пакет xcopilot содержит приложение xcopilot. Оно используется для синхронизации данных с КПК Palm. Исходный код этого приложения содержится, например, в пакете:
xcopilot -0.66-3.src.rpm |
В соответствии с соглашениями об именовании, имя файла пакета с исходным кодом оканчивается постфиксом src.rpm.
src.rpm-пакет должен содержать все необходимые средства, потребные для сборки бинарного пакета. Наличие такого пакета означает, что можно пересобрать бинарный пакет в любое время, сделав необходимые изменения/исправления, либо под другую процессорную архитектуру. И это одна из главных целей системы пакетного менеджмента RPM.
Поскольку в пакете с исходным кодом содержится вся информация для сборки, есть возможность пересобирать бинарные пакеты в Linux-среде, отличной от исходной. Например, скрипты настройки процесса сборки могут адаптировать этот процесс в зависимости от версий найденных в системе библиотек.
Если при в процессе сборки руководствоваться указаниями, обычно содержащимися в пакетах с исходным кодом, получаемые бинарные пакеты будут максимально адекватны системному окружению.
Далее - База данных RPM
Назад - Структура пакета RPM
Содержание
15.3.5 Закрытие rpm-файла
После завершения работы с rpm-файлом, закройте его с помощью Fclose:
int Fclose(FD_t fd); |
Fclose в основном действует также, как стандартная C-функция fclose(3). FD_t есть классификатор типа данных RPM.
Подсистема ввода/вывода RPM, определенная в rpmio.h, включает функции, которые имитируют (а также в большинстве случаев являются обертками) функции ввода/вывода ANSI C. Сюда относятся: Fopen, Fclose, Fread, Fwrite, Ferror, Fflush, Fileno, и Fseek.
Необходимость реализовать RPM функции в виде рапперов обусловлена тем, что функции ввода/вывода должны содержать дополнительные возможности. Например, Fopen кроме имен файлов поддерживает также HTTP и FTP URL.
Далее - Программирование с БД RPM
Назад - Короткий путь к информации хэдера
Содержание
15.4. Программирование с БД RPM
Многие функции библиотеки RPM используют так называемые сеты транзакций. В особенности это касается функций работы с БД RPM.
Создать сет транзакции можно вызовом rpmtsCreate:
rpmts rpmtsCreate(void); |
RPM использует сеты транзакций для группирования операций с БД RPM. RPM API эволюционирует таким образом, что сеты транзакций становятся все более важной деталью конструкции. Сеты транзакций, кроме того, позволяют автоматизировать обращение к БД RPM, если оно требуется по ходу задачи.
После завершения использования сета транзакции, следует прибрать за собой:
rpmts rpmtsFree(rpmts ts); |
Вызов rpmtsFree всегда возвращает NULL.
15.4.1. Итераторы БД
С момента создания сета транзакции мы можем обходить информацию об установленных пакетах в БД RPM с помощью итератора. Для создания итератора вызывается rpmtsInitIterator:
rpmdbMatchIterator rpmtsInitIterator(const rpmts ts, |
В вызове следует задать, какой тэг (какое поле, заданное его именем) мы будем перебирать. Как правило, это имя пакета, RPMTAG_NAME. Вместе с идентификатором поля мы должны передать имя пакета. keypointer изменяется в зависимости от того, какой тэг передан.
Для строковых данных можно передать 0 в параметре keylen. Например, ниже приводится конструкция, в которой rpmtsInitIterator ищет все пакеты, называющиеся sendmail:
rpmdbMatchIterator iter; |
rpmdbMatchIterator позволяет обходить множество пакетов, в данном случае обходятся пакеты, в именах которых имеется заданный шаблон. После вызова rpmtsInitIterator следующим шагом будет вызов rpmdbNextIterator:
Header rpmdbNextIterator(rpmdbMatchIterator iter); |
Эта функция возвращает следующий в итераторе объект хэдера. Если в итераторе больше нет пакетов, отвечающих условиям, вернется NULL.
Если Header - не NULL, из объекта можно получить записи, как было показано в предыдущих главах. Используйте цикл для обхода всех пакетов, отвечающих шаблону:
while ( (installed_header = rpmdbNextIterator(iter) ) != NULL) { |
Нет нужды освобождать объект после использования, так как каждый следующий вызов rpmdbNextIterator пересоздает Header.
Особенности работы итератора подлежат подстройке под ваши нужды. Для добавления шаблона в итератор используется rpmdbSetIteratorRE:
int rpmdbSetIteratorRE(rpmdbMatchIterator iter, |
Вызов rpmdbSetIteratorRE изменяет переданный итератор таким образом, чтобы использовался дополнительный шаблон. Параметр mode именует тип шаблона. Он может быть одним из:
Тип |
Означает |
RPMMIRE_DEFAULT |
То же, что и регулярное выражение, с добавлением \., .*, и ^..$ |
RPMMIRE_GLOB |
Шаблон в стиле glob с использованием fnmatch |
RPMMIRE_REGEX |
Регулярное выражение с использованием regcomp |
RPMMIRE_STRCMP |
Сравнение строк с использованием strcmp |
Для получения дополнительной информации по данным типам шаблонов воспользуйтесь man-ами для fnmatch(3), glob(7), regcomp(3), regex(7), и strcmp(3).
После использования итератор должен быть освобожден:
rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator iter); |
Вызов rpmdbFreeIterator возвращает NULL.
Далее - Сет зависимости
Назад - Программирование с БД RPM
Содержание
15.4.2. Сет зависимости
Для сравнения версий пакета применяется сет зависимости. Утилита rpm, например, использует сет зависимости для этой операции.
Можно, конечно, сравнивать версии напрямую, то есть вызывать
headerGetEntry для получения значения полей версии и релиза, превращать эти строки в числа и затем сравнивать, но этот подход будет источником проблем. Такое "пользовательское" сравнение не будет столь точным, как способ, описанный в этой главе. В особенности потому, что номера версий не представляют собой числа в действительности, например, 1.12.4 по сути строка, а не число.
Для реализации всей непростой логики сравнения версий можно использовать код этой главы или вызов rpmvercmp. Не пытайтесь сравнивать версии с помощью доморощенных реализаций.
Для создания сета зависимости данного объекта хэдера (заданного пакета) вызывается rpmdsThis. Вызов rpmdsThis создает сет зависимости, который хранит имя пакета, информацию о Эпохе/Версии/Релизе и флаги.
rpmds rpmdsThis(Header header, |
Для сравнения пакетов вы можете передать RPMTAG_REQUIRENAME в качестве tagID. Настоящий tagID игнорируется механизмом сравнения. В действительности важны флаги, задающие, будет ли определятся равенство Эпохи/Версии/Релиза или же будет определяться меньшинство Эпохи/Версии/Релиза в данном сете. Для выполнения этой настройки передаются битовые флаги:
(RPMSENSE_EQUAL|RPMSENSE_LESS) |
После создания сета зависимости можно вызывать функцию rpmdsNVRMatchesDep для сравнения Имени, Версии, Релиза, то есть записей Name, Version, Release в хэдере пакета по сравнению с данными из сета зависимости.
int rpmdsNVRMatchesDep(const Header header, |
После проверки зависимостей rpmdsNVRMatchesDep возвращает 1 если зависимость перекрывается или 0 в противном случае. В терминах сравнения пакетов это означает, что при возврате 1 файлы установленного пакета такие же или старее, чем пакета сравнения, при возврате 0 установленные файлы новее. В параметре nopromote можно передать 1 для предотвращения влияния Эпохи на результат сравнения.
Также для сравнения версий двух пакетов можно вызвать rpmVersionCompare:
int rpmVersionCompare(Header header1, Header header2); |
Если хэдер 1 представляет более старую версию, чем хэдер 2, возвращается -1. Если версии одинаковые, возвращается 0. Если хэдер 1 представляет более новую версию, чем хэдер 2, возвращается 1.
Для получения имени пакета из сета зависимости используется rpmdsN:
const char* rpmdsN(const rpmds dependency_set); |
Можно использовать rpmdsN для получения имени при вызове rpmtsInitIterator, если вы работаете с сетом зависимости при поиске в БД RPM.
После использования сет зависимости должен быть освобожден:
rpmds rpmdsFree(rpmds dependency_set); |
rpmdsFree возвращает NULL, как и прочие функции очистки.
Далее - Сравнение rpm-файла и установленного пакета
Назад - Итераторы БД
Содержание
15.5. Сравнение rpm-файла и установленного пакета
Сведя вместе знания о приемах работы с rpm-файлами и БД RPM можно создать немало полезных программ. Код утилиты для сравнения файла rpm-пакета и установленного пакета показан ниже - программа vercompare.c.
/* Compares a package file with an installed package, |
Программа vercompare.c выполняет чтение из файла rpm-пакета и поиск в БД RPM. В коде вводятся сеты транзакций и сеты зависимости. Используйте пример для создания собственных RPM программ.
Для запуска программы ей надо передать в качестве параметров имена одного или нескольких rpm-файлов. Программа извлечет имена пакетов из файлов и запросит БД RPM о пакетах с такими именами. Для каждого такого пакета vercompare даст заключение, является ли данный пакет старее установленного, идентичным установленному или новее установленного.
Например, если установлен пакет jikes-1.17-1 и имеется файл пакета более новой версии, тогда при запуске программы увидим примерно следующее:
$ ./vercompare -v jikes-1.18-1.i386.rpm |
Если происходит сравнение с более старой версией rpm-файла, получится такой результат:
$ ./vercompare -v jikes-1.14-1-glibc-2.2.i386.rpm |
И, наконец, если установленный пакет имеет такую же версию, получим следующее:
$ ./vercompare -v jikes-1.17-glibc2.2-1.i386.rpm |
Этот аспект сравнения можно изменить путем передачи флагов в rpmdsThis.
Если нужно организовать многословный вывод, используем -v.
Функции интерфейса командной строки RPM используют те же общие опции, что и утилиты rpm и rpmbuild. Вы можете использовать эти функции для высокоуровневого доступа к RPM. Например, для активации опции запроса используйте rpmcliQuery:
int rpmcliQuery(rpmts transaction_set, |
Установите переменную QVA_t так, чтобы она указывала на глобальную переменную rpmQVKArgs, которая инициализирована из глобальной таблицы опций для режима запросов, rpmQueryPoptTable. Передайте rpmcliQuery список имен файлов или имен пакетов.
Для поддержки опций запросов необходимо иметь записи в локальной таблице опций poptOption. Для доступа к этим опциям добавьте следующую запись:
{ NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmQueryPoptTable, 0, |
С опциями pmQueryPoptTable можно создать программу, которая будет работать как команда pm --query, используя следующий код:
poptContext context; |
Этот код поддерживает все опции запросов, что и команда rpm. Это одновременно и хорошо и плохо. Если вы хотите выполнять в точности, что и утилита rpm, можно с тем же успехом использовать саму утилиту. Но если вы хотите использовать поддержку запросов из программы, возможно есть более простой способ сделать это.
С помощью небольшого куска кода можно добавить в программу функционал, обеспечивающийся опцией --verify. Для этого необходимо включить определения командной строки из глобальной таблицы rpmVerifyPoptTable:
/* Add in --verify options. */ |
Далее можно проверить режим верификации и поддержку опций с помощью кода, подобного этому:
if (qva->qva_mode == 'V') { |
Рабочая лошадь этого кода - высокоуровневая функция rpmcliVerify, она выполняет всю ту работу, которую выполняет утилита rpm под опцией --verify.
int rpmcliVerify(rpmts transaction_set, |
И вновь, установите переменную QVA_t чтобы она указывала на глобальную переменную rpmQVKArgs, которая инициализирована значениями из глобальной таблицы опций rpmQueryPoptTable.
Соберем это все вместе и получим программу rpmq.c, которая выполняет все то же, что и команды rpm --query и rpm --verify:
/* |
Для такой функциональности не так уж много кода, не так ли? Эта эффективность достигается благодаря использованию высокоуровневых функций для доступа к интерфейсу командной строки.
Программа rpmq выполняет все те же задачи, что и команды rpm --query и rpm --verify. Например, поддерживаются форматы расширенных запросов:
$ ./rpmq -q --qf "%{NAME} %{INSTALLTID:date}\n" jikes |
Далее - Раздел 16. Программирование RPM на Python
Назад - Сет зависимости
Содержание
16.1.1 Установка окружения для программирования на Python
Установка программного окружения Python принципиально не отличается от установки программного окружения C. Следует установить пакеты разработки Python, пакет, предоставляющий Python API к системе RPM и текстовый редактор, понимающий синтаксис Python.
Базовый пакет языка - python. Для использования в RPM-разработке требуется версия не ниже 2.2. Пакет, обеспечивающий доступ к RPM из Python - rpm-python.
Далее - Использование Python в графических приложениях
Назад - Сравнение rpm-файла и установленного пакета
Содержание
2.2 База данных RPM
База данных RPM хранит информацию обо ВСЕХ пакетах, установленных в системе. Эта база может использоваться для запросов, касающихся того, что установлено, для информирования о версиях установленного ПО, для оценки целостности пакетов и системы, по крайней мере с точки зрения набора пакетов.
Файлы базы данных живут в директории /usr/lib/rpm и их набор включает примерно следующее:
Basenames
Conflictname
__db.001
__db.002
__db.003
Dirnames
Filemd5s
Group
Installtid
Name
Packages
Providename
Provideversion
Pubkeys
Requirename
Requireversion
Sha1header
Sigmd5
Triggername
Эти файлы созданы системой управления базой данных RPM. Файлы __db.001 и ему подобные - это файлы блокировки СУБД RPM. Остальные файлы - компоненты БД в формате Berkeley DB. Наиболее значимый файл - Packages. В нем хранятся значения полей хедера для каждого пакета, индексированные по внутреннему номеру пакета.
Другие файлы, как, например, Name, Providename, Group помогают оптимизировать запросы с помощью их типизации и ускорить работу БД с точки зрения пользователя.
Далее - Команды RPM
Назад - Бинарные rpm и rpm с исходным кодом
Содержание
16.1.2. Использование Python в графических приложениях
Python поддерживает ряд различных инструментов для создания графического интерфейса пользователя. Чтобы разработать GUI-приложение, придется выбрать один из них. Доступны:
* PyGTK. Это привязки Python к библиотеке GTK+, использующейся для построения интегрированной графической среды GNOME. PyGTK используется в графических утилитах системных настроек Red Hat system-config-*. PyGTK предоставляет полный доступ к виджетам GTK+. Для получения подробной информации используйте www.daa.com.au/~james/pygtk/.
* PyQt предоставляет привязки к графической библиотеке Qt, которая используется для построения интегрированной графической среды KDE. Подобно PyGTK пакет делает доступным все богатство виджетов Qt программе на Python.
Для получения подробной информации используйте www.riverbankcomputing.co.uk/pyqt/.
* Tkinter - стандартный компонент Python, базирующийся на наборе графических инструментов Tk, разработанных для визуализации интерфейса пользователя программ на языке Tcl. Преимущество интегрированности Tkinter в Python подкреплено широкой кроссплатформенностью.
Tkinter пригоден для простых в смысле оформления интерфейсов.
Для получения более подробной информации используйте www.python.org/topics/tkinter/.
После того, как нужные компоненты выбраны и установлены, следующим шагом будет освоение Python API для RPM.
Далее - Иерархия Python API
Назад - Установка окружения для программирования на Python
Содержание
16.2 Иерархия Python API
RPM Python API предоставляет высокоуровневый интерфейс к функциональности RPM, разделенный на несколько логически выделенных областей.
Класс |
Обеспечивает |
rpm |
Базовый модуль доступа к RPM API |
rpmts |
Работа с сетами транзакций |
rpmte |
Работа с элементами транзакций (пакеты в сете транзакций) |
rpmmi |
Определяет итераторы для работы с БД RPM |
Rpmds |
Работа с сетами зависимости |
Rpmfi |
Работа с rpm-файлами |
Header |
Работа с хэдером |
RPM Python API хорошо интегрирован в стандартный Python API. Например, можно использовать класс Python os для чтения rpm-файла.
Примеры этого раздела используют RPM 4.1 Python API.
Далее - Программирование с БД RPM
Назад - Использование Python в графических приложениях
Содержание
16.3 Программирование с БД RPM
По сравнению с RPM C API, Python API намного проще в использовании.
Практически все скрипты на Python, работающие с RPM, нуждаются в работе с сетами транзакций. Создание сета транзакции происходит следующим образом:
import rpm |
При этом БД RPM открывается автоматически, если это требуется.
Для более подробного ознакомления с приемами работы, используйте исходный код anaconda, программы инсталляции дистрибутивов Red Hat, написанной на Python.
Далее - Доступ к БД RPM
Назад - Иерархия Python API
Содержание
16.3.1 Доступ к БД RPM
Сеты транзакций предоставляют ряд методов работы с БД RPM на уровне движка базы данных. Используйте эти методы, если нуждаетесь во взаимодействии с БД в целом, в отличие от доступа к данным по какому-то одному пакету. Скажем, с помощью этих методов можно инициализировать или перестроить базу. Кроме того, можно получать доступ к альтернативным БД помимо БД по умолчанию.
16.3.1.1 Установка расположения БД
Сет транзакции открывает БД RPM основываясь на ее расположении по умолчанию. Для задания альтернативного расположения БД используется addMacro, как показано ниже:
rpm.addMacro("_dbpath", path_to_rpm_database) |
Возможна работа с несколькими БД путем определения макроса
_dbpath, создания сета транзакции и последующего удаления макроса. После такой последовательности можно создать другой сет транзакции для БД по умолчанию. Например:
# Open the rpmdb-redhat database |
Этот пример использует пакет rpmdb-redhat, который содержит данные обо всех пакетах Red Hat. Прямой вызов openDB открывает БД RPM. В большинстве случаев в скриптах Python прямой вызов openDB не требуется. Вместо этого сет транзакции откроет БД если это необходимо.
Вызов delMacro удаляет макрос, позволяя следующему вызову TransactionSet использовать БД по умолчанию.
Не вызывайте closeDB в сете транзакции. Этот метод действительно закроет БД, но также закроется возможность автоматического открытия при необходимости.
16.3.1.2 Инициализация, перестройка и верификация БД
Сет транзакции предоставляет метод initDB для инициализации новой БД RPM. Действие сходно с действием команды rpm --initdb:
ts.initDB() |
Метод rebuildDB регенерирует БД, подобно команде rpm --rebuilddb:
ts.rebuildDB() |
Метод rebuildDB перестраивает индекс базы.
Метод verifyDB проверяет читаемость базы и индексов функциями библиотеки Berkeley DB:
ts.verifyDB() |
Вызов этого метода - то же самое, что вызов команды db_verify для каждого файла БД в каталоге /var/lib/rpm.
С момента создания сета транзакции мы получаем возможность запрашивать БД RPM.
Далее - Запросы к БД RPM
Назад - Программирование с БД RPM
Содержание
16.3.2 Запросы к БД RPM
Для создания шаблонного итератора используется метод dbMatch экземпляра сета транзакции. Подобно C API шаблонный итератор позволяет программе перебирать пакеты, которые соответствуют шаблону по заданному критерию.
Вызов dbMatch без параметров означает, что будут перебираться все установленные пакеты. Базовый синтаксис:
import rpm |
В этом примере вызов dbMatch возвращает шаблонный итератор. Цикл for обходит весь итератор, возвращая один объект хэдера за раз.
Кроме данного синтаксиса можно вызывать метод next для получения следующего объекта хэдера. Например:
import rpm |
Прямой вызов метода next в будущих версиях Python API поддерживаться не будет.
Следующий листинг показывает пример скрипта на Python (rpmqa.py), который выводит имя, версию и релиз для всех установленных пакетов.
#!/usr/bin/python |
При вызове скрипта мы увидим примерно следующий вывод (часть вывода убрана ради экономии места):
$ python rpmqa.py |
Если на скрипт установлены права выполнения, можно избежать вызова интерпретатора вместе с именем скрипта:
$ ./rpmqa.py |
16.3.3 Работа с хэдером пакета
16.3.3.1 Класс hdr
Получить запись из объекта хэдера можно, используя возможности Python для работы со словарями. Это гораздо удобнее, чем вызов headerGetEntry в C-программе. Базовый синтаксис доступа к записи:
value = h['tag_name'] |
Например, для получения имени пакета делаем так:
name = h['name'] |
Также можно использовать стек предопределенных имен тэгов, которые использует C API. Эти константы определены в модуле rpm и их можно задействовать таким образом:
name = h[rpm.RPMTAG_NAME] |
Для записей хэдера, содержащих массив строк, например, список файлов пакета, данные, возвращаемые методом будут представлять собой список Python:
print "Files:" |
Кроме того, можно использовать file info для организации более компактного кода. Например:
print "Files:" |
Зависимости, возможности, несовместимости и конфликты всякий раз получаются как три отдельных, но ссылающихся друг на друга списка. Например, три списка для зависимостей, три для возможностей и так далее. Эту информацию можно извлекать, используя сет зависимости таким образом:
print h.dsFromHeader('providename') |
16.3.3.2 Вывод информации с помощью sprintf
Кроме возможностей Python по работе со словарями для форматирования вывода можно использовать метод sprintf, при этом применяется в точности такой же синтаксис, как для установки формата запросов при запуске утилиты rpm в режиме запросов:
h.sprintf("%{tag_name}") |
Можно добавлять специальные директивы форматирования к имени тега:
print "Header signature: ", h.sprintf("%{DSAHEADER:pgpsig}") |
Эти подходы можно включать в пользовательские функции, выводящие записи хэдера со специфическим форматированием:
def nvr(h): |
Далее - Запросы о конкретных пакетах
Назад - Запросы к БД RPM
Содержание
16.3.4 Запросы о конкретных пакетах
Когда для объекта хэдера вызывается dbMatch, пустые параметры означают, что надо обойти весь список установленных пакетов, имеющийся в БД RPM. Также можно запросить информацию об отдельном пакете (пакетах), используя dbMatch. Для этого в качестве параметров необходимо передать имя тега и его значение:
mi = ts.dbMatch(tag_name, value) |
Например, запрос всех пакетов, имеющих в имени шаблон sendmail, будет выглядеть так:
mi = ts.dbMatch('name', 'sendmail') |
Вызов dbMatch возвращает rpmdbMatchIterator. Вы можете запросить сформировать итератор по нескольким тегам, но наиболее часто встречаются запросы по шаблону имени.
Ниже показан пример скрипта (rpmq.py), который запрашивает пакеты с определенным шаблоном в имени и затем выводит имя, версию и релиз всех пакетов, которые соответствуют шаблону:
#!/usr/bin/python |
При вызове этого скрипта ему нужно передать имя пакета, которое интерпретатор Python будет хранить в sys.argv[1] в вызове dbMatch:
$ python rpmq.py sendmail |
Далее - Вывод информации о пакете
Назад - Работа с хэдером пакета
Содержание
16.3.5 Вывод информации о пакете
Для демонстрации эффективности Python API приведем пример скрипта (rpminfo.py), выполняющего ту же работу, что и команда rpm qi, но умеющего извлекать больше информации о пакете:
#!/usr/bin/python |
Функция printEntry передает в sprintf значение тега в формате "%{NAME}". Можно также передавать более сложные значения, например, "%{NAME}-%{VERSION}".
При запуске скрипта ему передается имя пакета. Вывод будет примерно следующий:
$ python rpminfo.py jikes |
Далее - Уточнение запросов
Назад - Запросы о конкретных пакетах
Содержание
16.3.6 Уточнение запросов
Метод шаблонов, применяемых для генерации шаблонных итераторов, позволяет совершенствовать запросы. Сужение области действия готового итератора позволит получить иформацию только о тех пакетах, которые мы действительно имели в виду. Синтаксис:
mi.pattern(tag_name, mode, pattern) |
Два основных способа сужения области действия запроса: 1. задать более чем один тег для уточнения, например, имя и версию; 2. отфильтровать результаты запроса, используя богатые возможности типизации шаблонов. Параметр mode указывает на тип шаблона, который будет использоваться. Тип может быть одним из следующих:
Тип |
Означает |
rpm.RPMMIRE_DEFAULT |
То же, что регулярное выражение, но с добавлением \., .*, и ^..$ |
rpm.RPMMIRE_GLOB |
Шаблон в стиле globe с использованием fnmatch |
rpm.RPMMIRE_REGEX |
Регулярные выражения с использованием regcomp |
rpm.RPMMIRE_STRCMP |
Сравнение строк с использованием strcmp |
Например, для запроса всех пакетов, имена которых начинаются на py, используем следующий код:
import rpm |
Следующий пример (rpmglobe.py) показывает запрос в стиле glob.
#!/usr/bin/python |
При запуске скрипта увидим следующее:
$ python rpmglob.py "py*" |
Кроме работы с БД RPM функционал Python API позволяет непосредственно анализировать файлы rpm-пакетов.
Далее - Чтение хэдера из файла пакета
Назад - Вывод информации о пакете
Содержание
16.4.1 Чтение хэдера из файла пакета
Подобно рассмотренным функциям C, например, rpmReadPackageFile, Python API предоставляет удобный способ чтения для создания объекта хэдера из файла rpm-пакета. Метод hdrFromFdno считывает хэдер из файла в соответствии с переданным дескриптором:
h = ts.hdrFromFdno(fdno) |
Метод hdrFromFdno опирается на низкоуровневый дескриптор вместо высокоуровневого объекта файла языка Python. В RPM библиотеке C FD_t - это FILE**.
Следующий пример показывает функцию, открывающую файл, считывающую хэдер и затем закрывающую файл.
def readRpmHeader(ts, filename): |
Метод hdrFromFdno обрабатывает ряд исключений, основываясь на событиях, диагностируемых при открытии или чтении файла:
def readRpmHeader(ts, filename): |
Решение о том, будут ли исключения прерывать исполнение кода, зависит от разработчика.
Далее - Установка флагов верификации
Назад - Уточнение запросов
Содержание
16.4.2 Установка флагов верификации
Начиная с RPM версии 4.1 файлы пакетов проверяются в автоматическом режиме, что может вызывать проблемы, особенно в отношении rpm старых версий или неподписанных электронной подписью.
В большинстве случаев автоматическая проверка - это преимущество, поскольку повышается доверие к пакету. Тем не менее, вы можете переопределить флаги верификации, чтобы изменить поведение по умолчанию. Для этой операции вызывается метод сета транзакции setVSFlags:
ts.setVSFlags(flags) |
Например, если имеются проблемы со старыми пакетами, не имеющими правильных подписей, установите игнорирование соответствующих проверок с помощью кода:
# Set to not verify DSA signatures. |
В таблице ниже перечислены флаги, которые можно передать setVSFlags. Это битовые маски. Их можно объединять для установки более чем одного флага. Для этого используется бинарный OR. Вместо зарезервированного Python слова or используется | .
Флаг |
Означает |
rpm.RPMVSF_NEEDPAYLOAD |
Установить смещение в начало нагрузки (то есть пропустить хэдер) |
rpm.RPMVSF_NOHDRCHK |
Не проверять хэдер |
rpm.RPMVSF_ NODSA |
Не проверять подписи DSA для хэдера и нагрузки |
rpm.RPMVSF_ NODSAHEADER |
Не проверять подпись DSA для хэдера |
rpm.RPMVSF_ NOMD5 |
Не проверять MD5 дайджест для хэдера и нагрузки |
rpm.RPMVSF_ NORSA |
Не проверять подписи RSA для хэдера и нагрузки |
rpm.RPMVSF_ NOSHA1HEADER |
Не проверять дайджест SHA1 для хэдера |
rpm._RPMVSF_NODIGESTS |
Не проверять дайджесты |
rpm._RPMVSF_NOSIGNATURES |
Не проверять подписи |
Для отключения всех проверок передайте -1 в setVSFlags:
ts.setVSFlasgs(-1) |
Далее - Сравнение зависимостей
Назад - Чтение хэдера из файла пакета
Содержание
16.5 Сравнение зависимостей
Сет зависимости кроме всего прочего позволяет сравнить версии двух пакетов. Наиболее употребим этот подход в случае, если нужно сравнить версии установленного пакета и какого-либо имеющегося на диске пакета с целью определения возможности апдейта.
Для получения сета зависимости по умолчанию используют метод dsOfHeader в отношении объекта хэдера. Получив сет зависимости двух хэдеров, можно их сравнить:
file_h = ts.hdrFromFdno(fd) |
Пример скрипта (vercompare.py), который сравнивает файл rpm пакета с данными установленного пакета и выводит информацию о том, чья версия новее:
#!/usr/bin/python |
Этот скрипт берет имя файла rpm пакета в качестве параметра из командной строки, получает хэдер пакета, ищет в базе данных RPM все пакеты с таким именем, получает объект хэдера для каждого, сравнивает все хэдеры в поисках новейшего и выводит сообщение.
Модифицируйте скрипт, например, добавив сообщение о том, что такой пакет не установлен.
Далее - Установка и обновление пакетов
Назад - Установка флагов верификации
Содержание
16.6 Установка и обновление пакетов
Благодаря существованию RPM имеется целый спектр возможностей. Можно установить или обновить пакет с помощью утилиты rpm. Можно сделать то же самое из программы, написанной с помощью C API или Python API. На Python API это сделать существенно легче. Большая часть операций реализуется с помощью сета транзакции.
Для установки или обновления пакета создайте сет транзакции, постройте транзакцию с элементами транзакции, которые будут представлены пакетами, нуждающимися в установке, проверьте неразрешенные зависимости и запустите сет. Запуск сета транзакции установит или обновит пакеты.
Далее - Построение сета транзакции
Назад - Сравнение зависимостей
Содержание
16.6.1 Построение сета транзакции
Установка или обновление пакетов должны быть выполнены в рамках контекста сета транзакции. Для установки или обновления набора пакетов выполняется addInstall с соответствующими хэдерами. Синтаксис:
ts.addInstall(header, key_data, mode) |
Когда вызывается addInstall, ему передаются объект хэдера, определенные ключевые данные и флаг режима. Флаг должен быть i для установки и u - для обновления, или a - для специального режима, в котором пакет доступен для прохождения проверок сета транзакции, но не устанавливается (не обновляется). Флаг а используется сравнительно редко. В большинстве случаев нужно использовать u, подобно тому, что при установке с помощью утилиты rpm пакеты должны устанавливаться с ключом -U.
Параметр ключевых данных key_data позволяет осуществить обратный вызов.
Для удаления пакетов используется addErase вместо addInstall:
ts.addErase(package_name) |
Для выбора пакета, который должен быть установлен (обновлен), используйте следующий код:
h = readRpmHeader( ts, sys.argv[1] ) |
Этот пример передает имя файла пакета в addInstall, забирая его из командной строки.
Вызов addInstall добавляет объект хэдера и ассоциированный с ним файл пакета для обновления. Имя файла пакета передается в sys.argv[1] в качестве key_data для обратного вызова из сета транзакции.
Далее - Элементы транзакции
Назад - Установка и обновление пакетов
Содержание
2.3 Команды RPM
Основные команды
Основная утилита системы RPM - rpm (странно, не правда ли). Одна из главных целей системы есть упрощение управления пакетами. Для достижения этой цели все операции кроме сборки предоставлены через эту утилиту. Параметры командной строки переключают утилиту для работы в одном из доступных режимов.
На вопрос, что доступно администратору в rpm, можно ответить: вагон типичных функций пакетного менеджмента и вагон и маленькая тележка нетипичных/малоизвестных операций.
В нижеследующей таблице приводится список основных режимов работы rpm.
Режим работы | Короткая нотация | Длинная нотация |
---|---|---|
Обновление/установка | -U | --upgrade |
Установка | -i | --install |
Удаление | -e | --erase |
Режим запросов | -q | --query |
Верификация | -V | --verify |
Проверка подписи | -K | --cheking |
Обновление в режиме freshen | -F | --freshen |
Инициализация БД | Нет | --initdb |
Перестройка БД | Нет | --rebuilddb |
Используя таблицу как руководство, можно исследовать характер работы опций утилиты. Для установки (а если уже установлен - обновления) пакета используется, например, команда:
# rpm -U foo-0.1-1.i386.rpm |
В целях получения некоторой обратной связи администраторы обычно используют эту команду в форме:
# rpm -Uhv foo-0.1-1.i386.rpm |
где v - многословное поведение, а h - указание выводить прогресс-бар из пятидесяти символов #.
Для удаления пакета:
# rpm -e foo |
Обратите внимание, имя пакета без версии и постфикса, так как rpm обращается за информацией о пакете в БД. При установке же используется имя файла rpm.
Для запроса списка всех установленных пакетов:
# rpm -qa |
Этот список может занимать не один экран. Хотите иметь возможность полистать его? Не проблема:
# rpm -qa | less |
Прочие команды
В дополнение к rpm, о которой мы не узнали еще и десятой части, имеются некоторые дополнительные команды, такие как rpmbuild (для сборки) и rpm2cpio.
Сборка пакетов будет обсуждаться в Разделе 8 этой книги.
Команда rpm2cpio экспортирует файл rpm в формат архиватора cpio. А cpio умеет вычленять из архива отдельные файлы. Например, для получения списка файлов в rpm-пакете можно использовать примерно следующее:
# rpm2cpio foo-0.1-1.i386.rpm | cpio -t |
Опция -i скажет архиватору cpio извлечь все файлы. Опция -d задаст создание при необходимости локальных директорий, опция -v переведет в многословный режим работы.
Далее - Раздел 3. Использование RPM
Назад - База данных RPM
Содержание
16.6.2 Элементы транзакции
Сеты транзакций состоят из элементов транзакций. Элемент транзакции составляет одну элементарную часть транзакции и содержит один пакет для одной операции (установка или удаление) в каждом сете. Таким образом: один элемент приходится на один пакет на одну операцию. Можно обходить сет транзакции для получения каждого элемента. Имея элемент, можно вызывать его методы для получения зависимостей или проверки записей хэдера.
Таблица ниже показывает информационные методы, которые можно вызывать для элемента транзакции. Большинство методов возвращает единственное значение.
Метод |
Возвращает |
A |
Возвращает архитектуру пакета |
E |
Возвращает эпоху |
O |
Возвращает операционную систему |
R |
Возвращает номер релиза |
V |
Возвращает версию |
N |
Возвращает имя пакета |
NEVR |
Возвращает имя-эпоху-версию-релиз |
DS |
Возвращает сет зависимости пакета для указанного тэга |
FI |
Возвращает сет информации о пакете |
Для сложных проверок метод DS возвращает сет зависимости пакета для заданного имени поля:
ds = te.DS(tag_name) |
Возможные поля (значения tag_name): Providename, Requirename, Obsoletename или Conflictname. Например:
ds = te.DS('Requirename') |
Метод FI возвращает сет информации о пакете:
fi = te.FI(tag_name) |
Для метода FI необходимо передавать в качестве tag_name 'Basenames'.
Пример ниже (te.py) показывает, как обходить сет транзакции для получения элементов транзакции:
#!/usr/bin/python |
Скрипт te.py устанавливает транзакцию и печатает элементы, никогда не выполняя транзакцию. Цель в данном случае - показать, что находится в транзакции. Вторая группа печатного вывода показывает результаты проверок и сортировки порядка транзакции.
Далее - Проверка и переопределение порядка элементов транзакции
Назад - Построение сета транзакции
Содержание
16.6.3 Проверка и переопределение порядка элементов транзакции
После того, как методом addInstall или addErase набраны пакеты для установки (удаления), вызываются два метода для проверки и выстраивания правильного порядка установки (удаления). Это методы check и order.
16.6.3.1 Проверка зависимостей
Метод check проверяет зависимости в сете транзакции.
unresolved_dependencies = ts.check() |
Метод возвращает None, если все зависимости разрешены, или сложный список для каждой неразрешенной зависимости. В целом, если метод вернул что-либо помимо None, транзакция не может быть выполнена.
Список, возвращаемые методом check в случае неразрешенных зависимостей, имеет следующий формат:
((N,V,R), (reqN, reqV), needsFlags, suggestedPackage, sense) |
Первый элемент списка - имя, версия, релиз пакета, который вы пытаетесь установить. Следующая последовательность - имя и версия пакета, от которого зависит устанавливаемый (или с которым конфликтует). Версия не выводится, если зависимость от библиотеки или другого файла (а не пакета целиком).
needsFlags говорят о том, что произошло - имеет место зависимость или конфликт. Значение флага - битовая маска, которая может содержать флаги для rpm.RPMSENSE_EQUAL, rpm.RPMSENSE_GREATER и rpm.RPMSENSE_LESS. Эти флаги могут дать понять, что имеется зависимость от пакета версии старше, чем 4.1, например.
Параметр suggestedPackage именует пакет, который разрешает зависимость. Эта переменная будет иметь значение None, если не известен пакет, разрешающий зависимость.
Также можно понять, является ли зависимость требованием или конфликтом с помощью rpm.RPMSENSE_CONFLICTS или rpm.RPMSENSE_REQUIRES. Это возможные значения sense.
Пример. Следующий список показывает зависимость от пакета:
(('eruby-devel', '0.9.8', '2'), ('eruby-libs', '0.9.8'), 8, None, 0) |
А этот - зависимость от разделяемой библиотеки:
(('jpilot', '0.97', '1'), ('libpisock.so.3', None), 0, None, 0) |
Данный формат вывода будет меняться в будущих версиях. Для установления текущей версии формата просмотрите онлайн-документацию Python API.
16.6.3.2 Обратный вызов метода check
В вызов check опционально может быть добавлена callback-функция. Эта функция будет вызываться для каждой неразрешенной зависимости в сете транзакции. Вы можете использовать этот обратный вызов для обработки ситуации. Базовый синтаксис:
def checkCallback(ts, TagN, N, EVR, Flags): |
Подобную callback-функцию можно использовать, например, для автоматического добавления пакетов в транзакцию (тех пакетов, от которых имеется неразрешенная зависимость). Информацию о пакетах можно брать из базы данных пакетов Red Hat, пакета rpmdb-redhat. Используя вышеописанный трюк, можно открыть транзакцию, используя более чем одну БД одновременно. Определите макрос _dbpath как путь "/usr/lib/rpmdb/i386-redhat-linux/redhat", или какой-то еще, где расположена БД из пакета rpmdb-redhat, и создайте сет транзакции. Тогда обратный вызов check найдет данные по пакетам в экстра-базе данных и добавит их текущую, реальную БД RPM.
Также check callback можно использовать для поиска файлов, от которых имеются зависимости, на диске или в сетевом архиве. Нижеследующий код показывает обратный вызов, который может быть заполнен в попытке удовлетворить зависимости по ходу проверки. Этот обратный вызов устанавливает формат поиска в альтернативной БД или где-либо еще. Данный скелет должен быть заполнен реальными данными для того, чтобы осуществлялся настоящий поиск пакетов для разрешения зависимостей.
def checkCallback(ts, TagN, N, EVR, Flags): |
В зависимости от того, какие параметры вы передадите в обратный вызов, код должен искать пакет как таковой, или пакет, который предоставляет нужный файл. Если имеется альтернативная БД пакетов, используйте dbMatch для поиска. Если же вы работаете с директорией, в которой лежат rpm-пакеты, потребуется построить полные имена пакетов, соединяя имена, версии и релизы.
16.6.3.3 Изменение порядка пакетов в сете транзакции
Добавление пакетов в сет может быть осуществлено в любом порядке. Метод order выстраивает свой порядок установки (удаления) в соответствии с зависимостями. Перед вызовом order должен быть выполнен check.
Далее - Запуск транзакции
Назад - Элементы транзакции
Содержание
16.6.4 Запуск транзакции
После инициализации сета транзакции, запуск операций осуществляется вызовом run. Требуется два параметра:
ts.run(callback, client_data) |
Параметр callback должен быть функцией Python. Параметр client_data - любые данные, которые вы хотите поместить в обратный вызов. Поскольку в сете транзакции может быть много пакетов, это не должны быть данные, относящиеся к определенному пакету. В качестве client_data не может быть передано значение None.
16.6.4.1 Обратные вызовы метода run
Callback, который был передан в run, необходим. Обратный вызов должен сработать правильно, иначе транзакция завершится с ошибкой. Вы должны предоставить callback.
Поскольку callback будет вызываться не один раз, это отличный способ получить обратную связь для определения степени выполнения транзакции (особенно в разработке графических приложений для прогресс-баров).
Базовый синтаксис обратного вызова для сета транзакции:
def runCallback(reason, amount, total, key, client_data): |
key - это данные, предоставленные вами для метода addInstall. client_data - данные, которые будут передаваться в run.
Всякий раз, когда происходит обратный вызов, сет транзакции будет задавать флаг reason. Возможные значения флага показаны в таблице.
Значение |
На что указывает |
rpm.RPMCALLBACK_UNKNOWN |
Неизвестная ошибка |
rpm.RPMCALLBACK_INST_PROGRESS |
Прогресс установки |
rpm.RPMCALLBACK_INST_START |
Старт установки |
rpm.RPMCALLBACK_INST_OPEN_FILE |
Callback должен открыть файл пакета |
rpm.RPMCALLBACK_INST_CLOSE_FILE |
Callback должен закрыть файл пакета |
rpm.RPMCALLBACK_TRANS_PROGRESS |
Прогресс транзакции |
rpm.RPMCALLBACK_TRANS_START |
Старт транзакции |
rpm.RPMCALLBACK_TRANS_STOP |
Стоп транзакции |
rpm.RPMCALLBACK_UNINST_PROGRESS |
Прогресс удаления |
rpm.RPMCALLBACK_UNINST_START |
Старт удаления |
rpm.RPMCALLBACK_UNINST_STOP |
Стоп удаления |
rpm.RPMCALLBACK_REPACKAGE_PROGRESS |
Прогресс пересборки |
rpm.RPMCALLBACK_REPACKAGE_START |
Старт пересборки |
rpm.RPMCALLBACK_REPACKAGE_STOP |
Стоп пересборки |
rpm.RPMCALLBACK_UNPACK_ERROR |
Ошибка распаковки |
rpm.RPMCALLBACK_CPIO_ERROR |
Ошибка cpio при попытке разархивировать нагрузку |
Ваш callback должен обрабатывать по меньшей мере два случая: rpm.RPMCALLBACK_INST_OPEN_FILE и rpm.RPMCALLBACK_INST_CLOSE_FILE.
С reason, равным rpm.RPMCALLBACK_INST_OPEN_FILE, необходимо открыть файл rpm-пакета и вернуть дескриптор. Этот дескриптор необходимо хранить в глобально-доступной переменной, или по крайней мере в зоне видимости, так как потребуется файл закрыть, когда reason будет rpm.RPMCALLBACK_INST_CLOSE_FILE.
16.6.4.2 Кодируем пример callback-функции
Код ниже показывает работающий пример callback для установки или обновления пакетов.
# Global file descriptor for the callback. |
Этот callback подразумевает, что в вызов addInstall передается в качестве пользовательских данных имя файла rpm-пакета. client_data в метод run игнорируется, но это хорошее место для передачи объекта. Можно, например, использовать эту возможность для того, чтобы избежать хранения дескриптора файла в глобальной переменной.
16.6.4.3 Обновление пакета
Пример ниже содержит скрипт (rpmupgrade.py) для обновления или установки пакета.
#!/usr/bin/python |
Этот скрипт ожидает, что имя файла rpm-пакета будет передано в параметре командной строки, получив имя он выполняет обновление пакета (а если это новый пакет - то установку).
При запуске скрипта увидим следующее:
# rpm -q jikes |
Если запустить скрипт без прав суперпользователя, увидим сообщение об ошибке типа следующего:
$ python rpmupgrade.py jikes-1.18-1.i386.rpm |
Если пакет имеет зависимости от файла, например, разделяемой библиотеки, вывод будет таким:
# python rpmupgrade.py jikes-1.17-glibc2.2-1.i386.rpm jpilot-0_97-1_i386.rpm |
Если имеется неразрешаемая зависимость от другого пакета, вывод будет таким:
# python rpmupgrade.py eruby-devel-0.9.8-2.i386.rpm |
Далее - Раздел 17. Программирование RPM на Perl
Назад - Проверка и переопределение порядка элементов транзакции
Содержание
17. Программирование RPM на Perl
Perl - один из наиболее популярных скриптовых языков. Его охотно используют системные администраторы, разработчики системного ПО и пользователи. Perl работает на Linux, UNIX и Windows.
Из-за своей продолжительной биографии, связанной с обработкой текстовых файлов, Perl пользуется особой любовью системных администраторов. Он поддерживает подключаемые пакеты, которые называются модулями. Можно найти тысячи модулей для обработки текстов, работы с сетью и других задач. Имеется столько готовых инструментов, что даже люди, не жалующие Perl из-за его синтаксиса частенько прибегают к использованию модулей Perl, экономя время.
На search.cpan.org можно найти множество модулей Perl, пригодных для решения различных задач.
Далее - Получение и использование RPM-модулей Perl
Назад - Запуск транзакции
Содержание
17.1 Получение и использование RPM-модулей Perl
Несмотря на обилие модулей, ни один из них, вероятно, не удовлетворит всех ваших потребностей, хотя со временем модули постепенно консолидируются в бОльшие модули, причем конечное число их используется почти всеми разработчиками. Например, модуль RPM2, написанный Chip Turner из Red Hat, предоставляет большинство возможностей, необходимых для работы с RPM из Perl.
Для работы должны быть установлены пакеты perl-RPM2 и сам интерпретатор Perl.
Модуль RPM2 содержит методы для работы с двумя типами объектов RPM: rpm-файлами и инсталлированными пакетами.
Далее - Открытие rpm-файла
Назад - Программирование RPM на Perl
Содержание
17.2.1 Открытие rpm-файла
Процедура open_package открывает файл пакета и возвращает объект хэдера (RPM2::Header). Базовый синтаксис следующий:
my $header = RPM2->open_package( $filename ); |
Например:
my $header = RPM2->open_package("jikes-1.14-1-glibc-2.2.i386.rpm"); |
После открытия файла может быть выполнен ряд операций запросов к объекту хэдера.
Далее - Получение значений полей хэдера из файла пакета
Назад - Получение и использование RPM-модулей Perl
Содержание
17.2.2 Получение значений полей хэдера из файла пакета
Каждый rpm-пакет содержит информацию, хранящуюся в определенных полях (тегах) хэдера пакета. Например, имя пакета содержится в поле NAME, а длинное описание в поле DESCRIPTION.
Процедура tag возвращает значение данного тега (поля). Например, для получения имени пакета используйте значение поля NAME:
use RPM2; |
Комбинируя несколько полей, можно получить полезные скрипты. Листинг ниже содержит скрипт (rpmsum.pl), который выводит имя пакета и его короткое описание:
#!/usr/bin/perl |
При запуске скрипта необходимо передать ему имя файла пакета в качестве параметра. Например:
$ ./rpmsum.pl jikes-1.14-1-glibc-2.2.i386.rpm |
Далее - Удобные методы
Назад - Открытие файла пакета
Содержание
17.2.3 Удобные методы
Модуль RPM2 включает методы для обработки ВСЕХ полей хэдера. Таким образом, вместо tag("NAME") можно употребить name() и также для всех прочих тегов. Например:
print $header->name(), ": ", $header->summary(), "\n"; |
Далее - Вывод имени и версии
Назад - Получение значений полей хэдера из файла пакета
Содержание
17.2.4 Вывод имени и версии
Модуль RPM2 предоставляет удобную процедуру для получения значений полей NAME, VERSION, RELEASE, и EPOCH, часто это сочетание заменяют аббревиатурой NVRE. Процедура, as_nvre, возвращает одиночную строку с этими значениями в стандартном формате, разделенными знаком минус.
Тег EPOCH обычно имеет пустое значение. Если значение имеется, оно будет на первом месте в строке вывода, и, далее, после двоеточия, имя, релиз и версия. Например:
5:redhat-config-httpd-1.0.1-13 |
Эту процедуру можно вызывать для любого объекта хэдера или любого объекта пакета для получения полного имени. Например:
print $header->as_nvre(), "\n"; |
Далее - Проверка, является ли файл пакета пакетом с исходными кодами
Назад - Удобные методы
Содержание
17.2.5 Проверка, является ли файл пакета пакетом с исходными кодами
Другая удобная процедура проанализирует, является ли пакет пакетом с исходными кодами, или бинарным. Процедура is_source_package возвращает true если проверяется пакет с исходниками, и false в противном случае.
Следующий скрипт, rpmpkg.pl, показывает, как использовать as_nvre и is_source_package в одном флаконе:
#!/usr/bin/perl |
Далее - Открытие БД RPM
Назад - Вывод имени и версии
Содержание
3.1 Утилита rpm
Все, что необходимо для управления пакетами, это утилита rpm.
Такие операции, как установка или удаление пакета могут быть выполнены быстро и эффективно.
Навыки использования утилиты вырабатываются быстро и могут быть задействованы уже через несколько минут после начала чтения этого раздела.
Далее - Установка и обновление пакетов
Назад - Команды RPM
Содержание
17.3.1 Открытие БД RPM
Помимо доступа к информации внутри файла rpm-пакета, RPM2 содержит также и процедуры для работы с БД RPM.
Для открытия БД используется процедура to open_rpm_db для объекта RPM2:
my $rpm_db = RPM2->open_rpm_db(); |
Также можно задать каталог, где размещена (например, альтернативная) БД RPM:
my $rpm_db = RPM2->open_rpm_db( "-path" => "/var/lib/rpm" ); |
Единожды получив объект БД RPM, можно неоднократно вызывать процедуры запросов для поиска пакетов по различным критериям в стиле команды rpm q.
Далее - Поиск пакетов
Назад - Проверка, является ли файл пакета пакетом с исходными кодами
Содержание
17.3.2 Поиск пакетов
Процедура find_by_name находит пакет или пакеты по имени. Она возвращает список Perl из найденных записей. Например, если установлена более чем одна версия пакета, find_by_name должна вернуть итератор для обхода списка, полученного с помощью шаблона имени.
Подход с применением итераторов обычно более эффективен.
Далее - Обход списка пакетов
Назад - Открытие БД RPM
Содержание
17.3.3 Обход списка пакетов
Итераторы весьма важны в пакете RPM2. Они предоставляют эффективный интерфейс к большим (в потенциале) наборам пакетов, а также итераторы более близки к реализации нижележащих функций в C API. Они просты для использования. Вызов процедуры next переводит нас к следующему в наборе пакету.
Например:
my $pkg_iter = $rpm_db->find_by_name_iter( "kernel" ); |
Следующий листинг содержит скрипт (rpmname.pl), который работает подобно команде rpm q без прочих опций командной строки.
#!/usr/bin/perl |
При запуске скрипта ему необходимо передать имя пакета в качестве параметра. Например:
$ ./rpmname.pl kernel |
Далее - Дополнительные функции поиска
Назад - Поиск пакетов
Содержание
17.3.4 Дополнительные функции поиска
Процедура find_by_name_iter ищет пакет по имени. Модуль RPM2 также поддерживает ряд функций для повышения информативности запросов. Описание функций приводится в таблице ниже.
Функция |
Использование |
find_all() |
Возвращает список всех пакетов из БД RPM |
find_all_iter() |
Возвращает итератор для обхода всех пакетов в БД RPM |
find_by_file($filename) |
Возвращает список пакетов, каждый из которых содержит данный файл |
find_by_file_iter($filename) |
Возвращает итератор для обхода пакетов, каждый из которых содержит данный файл |
find_by_name($package_name) |
Возвращает список пакетов, имена которых соответствуют шаблону имени |
find_by_name_iter($package_name) |
Возвращает итератор для обхода пакетов, имена которых соответствуют шаблону имени |
find_by_provides($capability) |
Возвращает список пакетов, предоставляющих заданную возможность |
find_by_provides_iter($capability) |
Возвращает итератор для обхода пакетов, предоставляющих заданную возможность |
find_by_requires($capability) |
Возвращает список пакетов, имеющих данную зависимость |
find_by_requires_iter($capability) |
Возвращает итератор для обхода пакетов, имеющих данную зависимость |
Для демонстрации возможностей этих функций предлагается следующий скрипт (rpmprovides.pl), результат работы которого можно сравнить с результатом работы команды rpm с соответствующими ключами. Скрипт ищет пакеты, предоставляющие определенную возможность и пакеты, которые зависят от нее.
#!/usr/bin/perl |
При запуске скрипта с именем зависимости в качестве параметра, получим следующий вывод:
$ ./rpmprovides.pl httpd |
Для проверки работы скрипта выполним команду rpm -q для сравнения полученных списков:
$ rpm -q --whatprovides httpd |
В обоих случаях увидим одинаковые списки пакетов.
Процедура find_by_provides_iter возвращает имя пакета, например, bash. Нет возможности передать имя файла, такое как /bin/bash, для получения имени пакета, предоставляющего этот файл.
Далее - Получение информации о пакетах
Назад - Обход списка пакетов
Содержание
17.3.5 Получение информации о пакетах
Процедуры tag, as_nvre и is_source_package, работающие с объектом хэдера, полученным из файла (как было показано ранее), также умеют работать и с записями, полученными из БД RPM.
Скрипт, приведенный ниже (rpminfo.pl), распечатывает описательную информацию о заданном пакете.
#!/usr/bin/perl |
Запуская скрипт, увидим вывод, подобный следующему:
$ ./rpminfo.pl XFree86 |
17.3.5.1 Вывод даты установки
Дата установки - это поле числового типа, представляющее количество секунд, прошедших со времени начала эпохи UNIX (1 января 1970 года) до момента установки пакета. Для придания этому значению (тег INSTALLTIME) человекочитаемого смысла используется функция Perl localtime. Скрипт rpmdate.pl показывает реализацию ее использования:
#!/usr/bin/perl |
Функция printf в этом скрипте умеет нечто, чего команда rpm делать не умеет. Даже используя опцию --queryformat, мы не можем сгруппировать значения нескольких полей в одну строку, задавая формат вывода, с Perl это возможно. Просто ассоциируйте несколько значений в строку, используя имена полей или такие процедуры, как as_nvre.
Передав имя пакета скрипту в качестве параметра, увидим дату установки пакета:
$ ./rpmdate.pl kernel |
17.3.5.2 Обработка полей, представляющих собой массивы строк
Не только формат даты может вызвать сложности. Ряд тегов (полей хэдера) представляют собой массивы строк. Это означает, что необходимо предусмотреть, каким образом эти массивы будут выводится в консолидированном виде.
Для помощи в обработке таких ситуаций процедура, показанная ниже, получает на вход массив строк, возвращая одну строку с заданным разделителем между частями строки:
sub arrayToString { |
Следующий список показывает имена полей, значения которых являются массивами строк:
*BASENAMES |
17.3.5.3 Получение списка файлов в пакете
Процедура files предоставляет список файлов, содержащихся в данном пакете. Нижеследующий листинг показывает, как с помощью скрипта rpmfiles.pl получить доступ к такому списку:
#!/usr/bin/perl |
Запустив скрипт, увидим следующее:
$ ./rpmfiles.pl jikes |
Далее - Сравнение версий
Назад - Дополнительные функции поиска
Содержание
17.3.6 Сравнение версий
Модуль RPM2 использует оператор сравнения, <=>, для сравнения версий одноименных пакетов. Следующий скрипт (rpmver.pl) показывает, как сравнить все локальные файлы rpm-пакета с определенным именем с новейшей установленной версией этого пакета (если он установлен).
#!/usr/bin/perl -w |
Сортировка { $a <=> $b } перед вызовом find_by_name сортирует все пакеты с заданным именем по версии, поэтому сравнение происходит с новейшей версией установленного пакета. ($h <=> $installed) сравнивает информацию хэдера файла на диске с информацией об установленном пакете из БД.
При запуске скрипта вывод будет зависеть от локальных rpm-файлов, имеющихся в каталоге на диске:
$ perl rpmver.pl |
Далее - Закрытие БД RPM
Назад - Получение информации о пакетах
Содержание
17.3.7 Закрытие БД
После завершения работы с БД вызовите close_rpm_db как показано ниже:
$rpm_db->close_rpm_db(); |
В принципе этот вызов не является обязательным, поскольку модуль RPM2 закроет БД, когда объект, в данном случае $rpm_db, выйдет из области видимости.
Далее - Раздел 18. Использование RPM в не-Red Hat Линуксах
Назад - Получение информации о пакетах
Содержание
18.1 О проблемах установки rpm-пакетов в не-Red Hat Линуксах
Когда мы имеем дело с установкой пакетов в не-Red Hat Линуксах, основные проблемы сосредотачиваются в следующих областях:
* Различия в версиях самой системы RPM
* Различное разделение ПО по пакетам
* Зависимости
* Отличающиеся пути размещения файлов
Следующие главы освещают эти темы.
Прим. перев. : Многие блоки информации в этом разделе сильно устарели. Главы переводятся "as is" без корректуры, с целью сохранения основных идей и подходов к решению проблем.
18.1.1 Версии системы RPM
Red Hat Linux 8.0 поставляется с RPM версии 4.1. Другие дистрибутивы могут поставлятся с иными версиями RPM. Поэтому, дабы не оставаться в неведении, первой командой перед работой с rpm в не-Red Hat дистрибутиве может быть rpm --version:
$ rpm --version |
Зная версию, мы можем предвосхищать возникновение проблем при установке пакетов. Например, RPM 4.0 и выше включает информацию о зависимостях в пакет автоматически. Если ваш дистрибутив имеет, например, версию RPM 3.х, придется отключить некоторые проверки при установке. Например, проверку зависимостей можно отключить с помощью --nodeps. В этом случае потребуется ручной контроль за удовлетворением зависимостей.
С другой стороны, если пакеты собирались под RPM 3.x и есть желание установить их в RPM 4.x систему, потребуется отключить проверку подписей с помощью --nosignature.
Далее - Разделение ПО по пакетам
Назад - О проблемах установки пакетов в не-Red Hat Линуксах
Содержание
18.1.2 Разделение ПО по пакетам
Не существует стандарта, придерживаясь которого разработчики разделяли бы большие проекты на пакеты в различных дистрибутивах Линукс. Это означает, что зависимости между пакетами будут отличаться.
Если возникли зависимости в отношении пакета, включающие множество других пакетов, проблема должна быть локализована, потому что пакеты, от которых возникли зависимости могут не существовать в определенном дистрибутиве или могут быть вовсе не нужны.
Если же зависимости имеются от файлов, в особенности от разделяемых библиотек, в большинстве случаев проблемы легко разрешимы, если файлы не устанавливаются в отличные от ожидаемых локации.
В этом случае единственное решение - использовать --nodeps. Устанавливать нужные зависимости или ссылки на нужные локации приходится вручную.
Кроме того, использование --nodeps может привести к коллапсу БД RPM. Поэтому оно оправдано, если зависимости действительно могут быть удовлетворены, например, из пакетов, имена которых отличаются от ожидаемых.
3.2. Установка и обновление пакетов
Для установки ПО в систему необходимо иметь нечто для установки. В нашем случае это rpm-пакет, то есть файл с расширением .rpm. Например, следующий файл содержит готовый для установки пакет ПО:
jikes-1.16-1.i386.rpm |
Этот пакет несет в себе приложение jikes - компилятор Java. По имени файла, как уже отмечалось, можно определить версию ПО и номер сборки.
Другие пакеты содержат исходный код для сборки приложений или библиотек. Например, этот файл - пакет с исходниками:
jikes-1.16-1.i386.src.rpm |
Нижеследующие главы описывают различные опции и способы применения rpm в ситуациях установки и обновления пакетов.
18.1.3 Зависимости
Один из мощнейших источников проблем - зависимости. Здесь случаи могут варьироваться от простейшего случая установки необходимого пакета, до сложных случаев различия в версиях разделяемых библиотек или модулей Perl.
Рекомендации можно свести к следующим: используйте репозитории вендора вашего дистрибутива, используйте автоматизированные средства, вроде yum или yast для удовлетворения зависимостей пакета.
Если зависимость возникла от системной библиотеки, например, библиотеки C определенной версии, проще пересобрать пакет из исходников, чтобы получить пакет, оптимизированный на использование данной версии.
Более серьезные случаи возникают, когда пакет зависит от прикладной разделяемой библиотеки, например, смена API в библиотеке может привести приложение к неработоспособному состоянию. Установите пакет-собственник прикладной библиотеки нужной версии и вновь пересоберите пакет из исходников.
Ряд пакетов являются devel-пакетами. Они зависят от базового пакета. Например, rpm-devel зависит от базового пакета rpm. rpm-python зависит как от python, так и от rpm базовых пакетов определенной версии. Подход в разделении пакетов на базовый и devel-пакеты применяется в Red Hat, но другие вендоры не обязаны ему следовать. Исследуйте документацию для ознакомления с подходами вендора вашего дистрибутива.
Многие пакеты зависят от интерпретаторов языков сценариев (скриптовых языков), например, Perl. Подчас зависимость возникает из-за скрипта, использованного в пакете или из-за триггера, в котором задействованы какие-то скрипты. Проблемы могут возникнуть из-за расположения интерпретаторов на файловой системе. Например, Perl может быть установлен в /usr/bin/perl или в /usr/local/bin/perl. Кроме того, пакет может зависеть от определенного модуля Perl.
Пути к файлам также могут быть источниками проблем. Файл может находится по другому пути или принадлежать другому пакету. В этом случае следует найти пакет, которому принадлежит файл и убедиться, что пакет установлен.
Поисковые системы по пакетам, например, rpmseek.com также умеют обнаруживать пакеты, содержащие определенный файл.
Далее - Пути установки
Назад - Разделение ПО по пакетам
Содержание
18.1.4 Пути установки
Вендоры Линукс могут устанавливать ПО где угодно. Например, многие дистрибьюторы устанавливают значительное количество пакетов в /opt вместо /usr. Следование межфирменным соглашениям о стандартизации путей в будущем может помочь ограничению потока проблем с этой стороны.
В качестве способа переопределения путей можно предложить использовать опцию --badreloc. Но у этого подхода есть естественное ограничение - можно переопределить пути к файлам, но не содержимое файлов. Если внутри файлов имеются определенные ожидания, по каким путям находятся необходимые компоненты, ПО будет неработоспособным, если ожидания не оправдываются.
Единственным реальным путем является путь редактирования жестко заданных путей в скриптах пакетов. В случае же, когда жестко заданные пути внесены в бинарные файлы, без исправления исходного кода не обойтись.
Далее - Если ничего не помогает, пересоберите пакет из исходников
Назад - Зависимости
Содержание
18.1.5 Когда ничего не помогает, пересоберите пакет из исходников
Если проделано множество манипуляций, а пакет все равно не устанавливается, имеется последнее радикальное средство - установить src.rpm-пакет и пересобрать его, отредактировав spec-файл.
Например, в конце секции %install обычно выполняется группа скриптов brp (Build Root Policy). Эти скрипты выполняют такие задачи, как упаковка файлов помощи man. brp-скрипты Mandriva используют компрессор bzip2, скрипты Red Hat - gzip. В этом случае пересборка и установка пакета при переносе из одной системы в другую - наилучший ход.
Далее - Решение проблем сборки пакетов
Назад - Пути установки
Содержание
18.2 Решение проблем сборки пакетов
Учитывая все вышеперечисленные различия дистрибутивов, каким образом можно собирать пакеты на других Линуксах? Используя некоторые известные подходы следует таким образом настроить окружение сборки rpm, чтобы настройки покрывали большую часть порождающих проблемы моментов.
При сборке пакетов вам встретятся почти все те же проблемы, что и при их установке, то есть отличия в делении ПО на пакеты, путях, соответственно, зависимости. Существует также ряд проблем, встречающихся только при сборке.
Далее - Создание пакетов, специфичных для конкретного дистрибутива
Назад - Когда ничего не помогает, соберите пакет из исходников
Содержание
18.2.1 Создание пакетов, специфичных для конкретного дистрибутива
Один из путей, обходящих проблемы различия дистрибутивов Линукс - создавать пакеты для конкретного дистрибутива. Для того, чтобы пойти этим путем, разработчик вынужден собирать ПО в пакеты для каждого дистрибутива из тех, что он поддерживает.
Это составляет довольно значительный объем работы. Если это возможно, поместите все различия в макросы и используйте единственный spec-файл для всех сборок, чтобы уменьшить количество непроизводительного труда. Иногда, однако, сложность spec-файла при этом превышает некоторые разумные пределы, в этом случае проще создать разные spec-файлы, по одному на каждый поддерживаемый дистрибутив.
Один из путей, облегчающих создание вендор-зависимых пакетов - рассмотреть пакет, содержащий специфичную для дистрибутива конфигурацию RPM. Например, в Red Hat Linux конфигурация RPM определяется пакетом redhat-rpm-config.
Получив список файлов этого пакета, увидим, в каких файлах Red Hat определяет специфичные для Red Hat Linux макросы.
$ rpm -ql redhat-rpm-config |
Эти файлы, например, /usr/lib/rpm/redhat/macros, демонстрируют отличия настроек Red Hat Linux от других дистрибутивов. Вооружившись знаниями о специфических настройках, разработчик с меньшими затратами сможет создавать портируемые rpm-пакеты.
Далее - Работа с автоматической генерацией зависимостей
Назад - Решение проблем сборки пакетов
Содержание
18.2.2 Работа с автоматической генерацией зависимостей
Одной из характерных черт RPM 4.х является автоматическая генерация зависимостей. По различным причинам, включающим отличия раскладки ПО по пакетам, структуры каталогов, или версии RPM, вам может потребоваться отключить некоторые или все возможности системы генерации зависимостей.
Для отключения генерации зависимостей следует поместить специальную директиву в spec-файл:
Autoreq: 0 |
Если эта директива присутствует, необходимо ручное вмешательство в редактирование поля Requires: для определения всех зависимостей. Такое решение не всегда оптимально, возможно лучшим выбором будет переопределение макросов %{__find_requires} и %{__find_provides} для отфильтрования некоторых неактуальных зависимостей.
Эти два макроса разворачиваются в shell-скрипты, которые выполняют автоматические проверки зависимостей:
$ rpm --eval "%__find_provides" |
Вы можете переписать эти скрипты с тем, чтобы исключить зависимости, создающие проблемы для вашего пакета.
Далее - Работа с различающимися макросами
Назад - Создание пакетов, специфичных для конкретного дистрибутива
Содержание
18.2.3 Работа с различающимися макросами
Различные производители Линукс определяют отличающиеся макросы RPM. Это может означать не только другие значения макросов, но также и другие их имена. Из-за этого наилучшей практикой является определение собственного набора макросов при сборке пакетов.
В максимальной степени, насколько это возможно, рекомендуется зависеть от своих макросов, но не от макросов стороннего производителя. Можно задать свои макросы в стиле вендор-зависимых определений в spec-файле. Также можно воспользоваться примерами главы "Построение окружения и макросов" из этого раздела.
Далее - Создание пакетов с переопределяемыми путями
Назад - Работа с автоматической генерацией зависимостей
Содержание
18.2.4 Создание пакетов с переопределимыми путями
Следует ставить целью делать свои пакеты пакетами с переопределимыми путями, чтобы пользователи могли устанавливать их в любые каталоги. Это облегчит решение проблем с выбором каталогов для установки дополнительного ПО в других дистрибутивах.
Можно использовать макрос %{_bindir} в spec-файле. Он поможет созданию пакетов для различных дистрибутивов, сохраняя правильные пути.
Кроме того, можно задать макросы, определяющие пути к зависимостям. Далее с помощью опции --define утилиты rpmbuild установить условия, которые и определят значения макросов (а следовательно и пути).
Далее - Построение окружения сборки RPM
Назад - Работа с различающимися макросами
Содержание
18.2.5 Построение окружения сборки RPM
Если дело начато с идеи сборки пакета для нескольких версий Linux, можно установить такое окружение сборки, которое поможет четко разделить вендор-зависимые характеристики.
Ключевые темы в данном подходе:
* Определение вендора
* Использование макросов для задания процесса сборки
* Обработка различных зависимостей
18.2.5.1 Определение вендора
Для получения чистой среды сборки необходима возможность определения вендора, и, в зависимости от этого, нужных установок при сборке. Для облегчения решения этой задачи большинство вендоров помещают в определенное место файловой системы файл или устанавливают специальный пакет, который содержит имя производителя. Это имя может быть получено в процессе сборки.
Соглашение для файлов следующее:
/etc/vendor-release |
Например:
$ more /etc/redhat-release |
Для пакетов соглашение касается имен пакетов:
$ rpm -q redhat-release |
Также можно использовать простое определение макроса для имени вендора и затем опцию --define для rpmbuild. Например:
# rpmbuild ba --define 'linuxVendor suse' |
С такой опцией используется макрос %linuxVendor.
Альтернативный подход - автоматическое определение вендора скриптами сборки. Ручное определение работает, однако требует введения информации от пересборщика пакета.
18.2.5.2 Построение окружения сборки и макросов
Будучи единожды установленным, значение макроса "вендор" позволяет задать другие макросы, учитывающие различия дистрибутивов, которые влияют на ваши приложения.
Для учета платформы используют главным образом условные определения, заключенные между %if и %endif. Кроме того, часто бывает полезным использование опций --with, --without и --target утилиты rpmbuild, позволяющие контролировать особенности сборки путем передачи условий в скрипты и макросы, определенные в spec-файле.
Макрос %if позволяет задать условия. Например:
%if %{old_5x} && %{old_6x} |
Также %if можно использовать для настройки таких полей, как
Requires: (показано в нижеследующем примере):
%if %{build6x} |
Опция командной строки --with задает значение специального макроса _with_. Например, следующая команда вносит установки в макросы spec-файла: