Скриптлеты
Spec-файлы RPM имеют несколько разделов, позволяющих пакетам выполнять код при установке и удалении. Эти фрагменты кода называются скриптлетами и в основном используются для обновления работающей системы информацией из пакета. На этой странице представлен краткий обзор скриптлетов RPM и ряд типовых рецептов для скриптлетов в пакетах. Для более полного описания скриптлетов см. книгу Maximum RPM.
Версия RPM в Fedora также имеет функциональность автоматического запуска скриптов, когда файлы помещаются в определённые места. (См. 1.) Это потенциально устраняет необходимость в большинстве скриптлетов на этой странице, но в настоящее время реализовано не во всех случаях, где это возможно.
Пакеты НЕ ДОЛЖНЫ полагаться на запись в stdout / stderr в скриптлетах RPM для передачи пользователю несмертельных проблем, предупреждений или других сообщений.
Оболочка по умолчанию
В Fedora все скриптлеты могут безопасно предполагать, что они выполняются в оболочке bash, если не указан другой язык.
Синтаксис
Базовый синтаксис аналогичен разделам %build, %install и другим разделам spec-файла rpm. Скрипты поддерживают специальный флаг -p, который позволяет скриптлету напрямую вызывать одну программу вместо того, чтобы порождать оболочку для вызова программ. (т. е. %post -p /usr/bin/ldconfig)
При вызове скриптлетам передаётся аргумент. Этот аргумент, доступный через $1 (для shell-скриптов), — это количество пакетов с этим именем, которые останутся в системе после завершения действия. Таким образом, для распространённых случаев установки, обновления и удаления имеем:
установка |
обновление |
удаление |
|
|
|
|
(Н/Д) |
|
|
|
(Н/Д) |
|
|
|
(Н/Д) |
|
(Н/Д) |
|
|
|
(Н/Д) |
|
|
|
|
|
(Н/Д) |
Обратите внимание, что эти значения будут различаться, если установлено несколько версий одного и того же пакета (в основном это происходит с параллельно устанавливаемыми пакетами, такими как ядро и multilib-пакеты. Однако это также может произойти, когда ошибки препятствуют завершению обновления пакета.) Поэтому хорошей идеей является использование такой конструкции:
%pre
if [ $1 -gt 1 ] ; then
fi
для скриптов %pre и %post, а не проверять, что оно равно 2.
Все скриптлеты ДОЛЖНЫ завершаться с нулевым кодом выхода. Поскольку RPM в своей конфигурации по умолчанию не выполняет shell-скриптлеты с аргументом -e для оболочки, за исключением явных вызовов exit (что неприветствуется с ненулевым аргументом!), код выхода последней команды в скриптлете определяет его код выхода. Большинство команд в фрагментах в этом документе имеют добавленное "|| :", что является универсальным приёмом для принудительного получения нулевого кода выхода для этих команд независимо от того, сработали они или нет. Обычно наиболее важно применить это к последней выполняемой команде в скриптлете или добавить отдельную команду, такую как простой ":" или "exit 0", в качестве последней в скриптлете. Обратите внимание, что в зависимости от случая другие меры проверки/предотвращения ошибок могут быть более уместны.
Ненулевые коды выхода из скриптлетов могут нарушить установку/обновление/удаление так, что для этого пакета в транзакции больше не будет предприниматься никаких действий (см. [Ordering]), что может, например, помешать удалению старой версии пакета при обновлении, оставив дублирующиеся записи в rpmdb и, возможно, устаревшие, никому не принадлежащие файлы в файловой системе. Есть случаи, когда позволение транзакции продолжиться, когда что-то в скриптлетах не удалось, может привести к частично сломанной установке. Однако часто это ограничивается только этим пакетом, тогда как позволение транзакции продолжиться с выпавшими на лету некоторыми пакетами скорее приведёт к более широким общесистемным проблемам.
Порядок
Скриптлеты в %pre и %post выполняются соответственно до и после установки пакета. Скриптлеты %preun и %postun выполняются до и после удаления пакета. Скриптлеты %pretrans и %posttrans выполняются в начале и в конце транзакции. При обновлении скрипты выполняются в следующем порядке:
-
%pretransнового пакета -
%preнового пакета -
(установка пакета)
-
%postнового пакета -
%triggerinдругих пакетов (запускается установкой нового пакета) -
%triggerinнового пакета (если какие-либо истинны) -
%triggerunстарого пакета (если он запускается удалением старого пакета) -
%triggerunдругих пакетов (запускается удалением старого пакета) -
%preunстарого пакета -
(удаление старого пакета)
-
%postunстарого пакета -
%triggerpostunстарого пакета (если он запускается удалением старого пакета) -
%triggerpostunдругих пакетов (если они запускаются удалением старого пакета) -
%posttransнового пакета
Скриптлет %pretrans
Обратите внимание, что скриптлет %pretrans в частности при установке системы будет выполнен до того, как вообще что-либо было установлено. Это означает, что он не может иметь никаких зависимостей вообще. По этой причине %pretrans лучше избегать, но если он используется, он ДОЛЖЕН (по необходимости) быть написан на Lua. См. https://rpm-software-management.github.io/rpm/manual/lua.html для получения дополнительной информации.
Написание скриптлетов
Вот несколько советов по написанию хороших скриптлетов:
Сохранение состояния между скриптлетами
Иногда скриптлету нужно сохранить некоторое состояние из ранее выполненного скриптлета, чтобы использовать его в позже выполняемом скриптлете. Это особенно часто встречается при попытке оптимизировать скриптлеты. Примеры:
-
Если
%posttransдолжен отменить регистрацию некоторой информации при обновлении, но файл, содержащий эту информацию, удаляется при удалении старого пакета, скриптлетам нужно сохранить этот файл во время%preили%post, чтобы скрипт в%posttransмог получить к нему доступ. -
Если мы хотим, чтобы программа в
%posttransвыполняла свою работу только один раз за транзакцию, нам может понадобиться записать файл-флаг, чтобы%posttransзнал, выполнять ли действие.
Для решения этих проблем скриптлеты, выполняемые раньше, должны записывать информацию, используемую в %posttrans. Мы рекомендуем использовать для этого подкаталог %{_localstatedir}/lib/rpm-state/. Например, скрипты плагина eclipse при установке создают файл в %{_localstatedir}/lib/rpm-state/eclipse/. %posttrans запускает скрипт, который проверяет, существует ли этот файл. Если да, он выполняет своё действие и затем удаляет файл. Таким образом скрипт выполняет своё действие только один раз за транзакцию.
Макросы
Если триггеры файлов RPM не подходят, сложные скриптлеты, разделяемые между несколькими пакетами, МОГУТ быть помещены в макросы RPM. Это имеет два преимущества:
-
Авторам стандартных пакетов нужно помнить только макросы, а не сложные вещи, которые они делают
-
Реализации макросов могут изменяться без необходимости обновлять пакет
При написании макросов FPC всё равно захочет проверить макросы (и, возможно, включить реализацию макросов в руководство, чтобы показать упаковщикам, что происходит за кулисами).
Один из принципов, которого придерживается FPC, заключается в том, что макросы обычно не содержат начала тегов скриптлетов (например, %pre), потому что это затрудняет выполнение дополнительной работы в скриптлете. Это также означает, что один макрос не может быть определён для выполнения действий как в %pre, так и в %post. Вместо этого напишите один макрос, выполняющий действия в %pre, и отдельный макрос, выполняющий действия в %post. Этот принцип позволяет всем spec-файлам использовать ваши макросы одинаковым образом, даже если у них уже определён %pre или %post.
Конечно, в описанной выше ситуации лучше использовать триггеры файлов RPM, если это вообще возможно.
Фрагменты
Некоторые скриптлеты для использования в конкретных ситуациях.
Конфигурационные файлы компоновщика
Пакеты, помещающие конфигурационные файлы компоновщика в /etc/ld.so.conf.d, ДОЛЖНЫ вызывать ldconfig в %post и %postun (во всех выпусках Fedora), даже если они не устанавливают никаких фактических библиотек. Они НЕ ДОЛЖНЫ использовать макросы %ldconfig, %ldconfig_post, %ldconfig_postun или %ldconfig_scriptlets для этого, поскольку эти макросы не оказывают никакого влияния на Fedora. Вместо этого просто вызовите ldconfig напрямую как в %post, так и в %postun, а также добавьте необходимые зависимости, когда это необходимо:
%post -p /usr/bin/ldconfig
%postun -p /usr/bin/ldconfig
или как часть существующих скриптлетов %post или %postun:
Requires(post): /usr/bin/ldconfig
Requires(postun): /usr/bin/ldconfig
[...]
%post
[...]
ldconfig
[...]
%postun
[...]
ldconfig
[...]
Если конфигурационный файл, добавленный в /etc/ld.so.conf.d, указывает каталог, в который другие пакеты могут устанавливать файлы, и этот каталог не находится в иерархии каталогов под одним из /lib, /usr/lib, /lib64 или /usr/lib64, тогда пакет, добавляющий конфигурационный файл, ДОЛЖЕН также включить следующие триггеры файлов, которые заставляют ldconfig запускаться автоматически при необходимости:
%transfiletriggerin -P 2000000 -- DIRECTORIES
ldconfig
%transfiletriggerpostun -P 2000000 -- DIRECTORIES
ldconfig
Замените DIRECTORIES на разделённый пробелами список каталогов, которые пакет добавляет в путь поиска библиотек через конфигурационные файлы в /etc/ld.so.conf.d.
Пользователи и группы
Они обсуждаются на отдельной странице.
GConf
Устаревшая система конфигурации GNOME 2 (всё ещё используемая лишь несколькими пакетами) задокументирована для потомков на другой странице.
Systemd
Пакеты, содержащие файлы юнитов systemd, должны использовать скриптлеты для обеспечения правильной обработки этих служб. Службы могут быть либо включены, либо отключены по умолчанию. Чтобы определить, к какому случаю относится ваша конкретная служба, обратитесь к политике FESCo здесь. При обновлении пакет может только перезапустить службу, если она работает; он не может запустить её, если она выключена. Также служба не может включить себя, если она в данный момент отключена.
Скриптлеты
Пакет systemd предоставляет набор вспомогательных макросов для обработки операций скриптлетов systemd. Эти макросы поддерживают «пресеты» systemd, как описано в systemd.preset(5).
BuildRequires: systemd-rpm-macros
[...]
%post
%systemd_post apache-httpd.service
%preun
%systemd_preun apache-httpd.service
%postun
%systemd_postun_with_restart apache-httpd.service
Некоторые службы не поддерживают перезапуск (например, D-Bus и различные демоны хранилищ). Если ваша служба не должна перезапускаться при обновлении, а вместо этого должна перезагружаться, используйте следующий скриптлет %postun вместо показанного выше:
%postun
%systemd_postun_with_reload apache-httpd.service
Если ваша служба не должна перезапускаться или перезагружаться, используйте вместо этого следующий скриптлет %postun:
%postun
%systemd_postun apache-httpd.service
Эти макросы принимают несколько аргументов с именами юнитов. Лучше использовать один вызов, чтобы уменьшить количество вызовов.
Если ваш пакет включает один или несколько юнитов systemd, которые должны быть включены по умолчанию при установке пакета, они ДОЛЖНЫ быть охвачены политикой пресетов Fedora.
Пользовательские юниты
Существуют дополнительные макросы для пользовательских юнитов (тех, что устанавливаются в %_userunitdir), которые следует использовать аналогично макросам для системных юнитов. Они включают и отключают пользовательские юниты в соответствии с пресетами и представляют собой %systemd_user_post (для использования в %post) и %systemd_user_preun (для использования в %preun).
BuildRequires: systemd-rpm-macros
[...]
%post
%systemd_user_post %{name}.service
%preun
%systemd_user_preun %{name}.service
%postun
%systemd_user_postun_with_restart %{name}.service
%systemd_user_postun_with_reload %{name}.service
%systemd_user_postun %{name}.service
Макросы %systemd_user_postun_with_restart и %systemd_user_postun_with_reload перебирают работающие экземпляры менеджера пользователя и запрашивают операции перезапуска и перезагрузки для указанных юнитов в каждом из них.
Зависимости от пакета systemd
Если скриптлеты пакета вызывают другие инструменты systemd, например systemd-tmpfiles, пакет ДОЛЖЕН объявить соответствующие зависимости. Макрос %{?systemd_requires} — это сокращение для требования systemd для скриптлетов %post, %preun и %postun. Обратите внимание, что эти зависимости не требуются для макросов %systemd_{post,preun,postun_with_restart,user_post,user_preun}, перечисленных выше.
Если пакет хочет использовать инструменты systemd, если они доступны, но не хочет объявлять зависимость, тогда макрос %{?systemd_ordering} МОЖЕТ быть использован как более слабая форма %{?systemd_requires}, которая объявляет только порядок во время транзакции RPM.
Макросы имеют форму %{?systemd_requires} вместо %systemd_requires, чтобы spec-файл корректно разбирался в случае, когда макросы systemd ещё не установлены.
|
Детали макросов
Для получения подробной информации о том, во что раскрываются эти макросы, обратитесь к следующим источникам: macros.systemd.in, triggers.systemd.in и daemon(7).
Оболочки
/etc/shells — это текстовый файл, который управляет тем, может ли приложение использоваться в качестве системной оболочки входа пользователей. Он содержит набор допустимых оболочек, которые можно использовать в системе. Если вы упаковываете новую оболочку, вам нужно добавить в этот файл записи, ссылающиеся на добавленные оболочки. См. man 5 SHELLS для получения дополнительной информации.
Поскольку этот файл может редактироваться системными администраторами, нам сначала нужно определить, есть ли соответствующие строки уже в файле. Если их там ещё нет, нам просто нужно вывести путь к двоичному файлу оболочки в файл. Поскольку функция UsrMove в Fedora 17 сделала /bin символьной ссылкой на /usr/bin, нам нужно поместить оба пути в файл /etc/shells. Вот пример скриптлета для пакета с оболочкой под названием «foo»:
%post
if [ "$1" = 1 ]; then
if [ ! -f %{_sysconfdir}/shells ] ; then
echo "%{_bindir}/foo" > %{_sysconfdir}/shells
echo "/bin/foo" >> %{_sysconfdir}/shells
else
grep -q "^%{_bindir}/foo$" %{_sysconfdir}/shells || echo "%{_bindir}/foo" >> %{_sysconfdir}/shells
grep -q "^/bin/foo$" %{_sysconfdir}/shells || echo "/bin/foo" >> %{_sysconfdir}/shells
fi
%postun
if [ "$1" = 0 ] && [ -f %{_sysconfdir}/shells ] ; then
sed -i '\!^%{_bindir}/foo$!d' %{_sysconfdir}/shells
sed -i '\!^/bin/foo$!d' %{_sysconfdir}/shells
fi
Want to help? Learn how to contribute to Fedora Docs ›