Scriptlets

RPM-Spezifikationsdateien enthalten mehrere Abschnitte, die es Paketen ermöglichen, bei der Installation und Deinstallation Code auszuführen. Diese Codeabschnitte werden als Scriptlets bezeichnet und dienen hauptsächlich dazu, das laufende System mit Informationen aus dem Paket zu aktualisieren. Diese Seite bietet einen kurzen Überblick über RPM-Scriptlets und einige gängige Beispiele für deren Verwendung in Paketen. Eine ausführlichere Beschreibung von Scriptlets finden Sie im Buch Maximum RPM.

Die in Fedora integrierte RPM-Version bietet außerdem die Möglichkeit, Skripte automatisch auszuführen, sobald Dateien an bestimmten Orten abgelegt werden. (Siehe 1.) Dadurch erübrigt sich potenziell der Bedarf an den meisten Skripten auf dieser Seite, diese Funktion ist jedoch noch nicht in allen Fällen implementiert, in denen sie sinnvoll wäre.

Vorgabe-Shell

In Fedora können alle Skripte davon ausgehen, dass sie unter der Bash-Shell ausgeführt werden, sofern keine andere Sprache angegeben wurde.

Syntax

Die grundlegende Syntax ähnelt den Abschnitten %build, %install und anderen Abschnitten der RPM-Spec-Datei. Die Skripte unterstützen das spezielle Flag -p, mit dem das Skript ein einzelnes Programm direkt aufrufen kann, anstatt eine Shell zum Starten der Programme öffnen zu müssen (z.B. %post -p /usr/bin/ldconfig).

Wenn Scriptlets aufgerufen werden, erhalten sie ein Argument. Dieses Argument, auf das über $1 (bei Shell-Skripten) zugegriffen wird, gibt die Anzahl der Pakete dieses Namens an, die nach Abschluss der Aktion auf dem System verbleiben. Im Normalfall von Installation, Aktualisierung und Deinstallation ergibt sich Folgendes:

Installation

Aktualisierung

Deinstallation

%pretrans

$1 == 1

$1 == 2

(N/A)

%pre

$1 == 1

$1 == 2

(N/A)

%post

$1 == 1

$1 == 2

(N/A)

%preun

(N/A)

$1 == 1

$1 == 0

%postun

(N/A)

$1 == 1

$1 == 0

%posttrans

$1 == 1

$1 == 2

(N/A)

Beachten Sie, dass diese Werte variieren, wenn mehrere Versionen desselben Pakets installiert sind (dies tritt hauptsächlich bei parallel installierbaren Paketen wie dem Kernel und Multilib-Paketen auf. Es kann jedoch auch vorkommen, wenn Fehler eine Paketaktualisierung verhindern). Daher empfiehlt sich die Verwendung des folgender Konstrukts:

%pre
if [ $1 -gt 1 ] ; then
fi

für %pre`- und `%post-Skripte, anstatt zu prüfen, ob es gleich 2 ist.

Alle Scriptlets MÜSSEN mit dem Exit-Status 0 beendet werden. Da RPM in seiner Standardkonfiguration Shell-Skripte nicht mit dem Argument -e ausführt (ausgenommen explizite exit-Aufrufe, die mit einem Argument ungleich 0 nicht empfohlen werden!), bestimmt der Exit-Status des letzten Befehls im Scriptlet dessen Exit-Status. Die meisten Befehle in den Codebeispielen dieses Dokuments haben ein angehängtes "|| :", was einen allgemeinen Trick darstellt, um den Exit-Status 0 für diese Befehle zu erzwingen, unabhängig davon, ob sie ausgeführt wurden oder nicht. In der Regel ist es am wichtigsten, dies auf den letzten ausgeführten Befehl in einem Skript anzuwenden oder einen separaten Befehl wie ":" oder "exit 0" als letzten Befehl hinzuzufügen. Beachten Sie, dass je nach Anwendungsfall andere Fehlerprüfungs- und -vermeidungsmaßnahmen besser geeignet sein können.

Fehlerhafte Exit-Codes von Scriptlets können Installationen, Aktualisierungen und Löschungen so unterbrechen, dass für das betreffende Paket in einer Transaktion keine weiteren Aktionen ausgeführt werden (siehe [Ordering]). Dies kann beispielsweise verhindern, dass eine alte Version eines Pakets bei Aktualisierungen gelöscht wird, wodurch doppelte rpmdb-Einträge und möglicherweise veraltete, nicht zugeordnete Dateien im Dateisystem zurückbleiben. In manchen Fällen kann das Fortsetzen der Transaktion trotz Fehlern in Scriptlets zu einer teilweise fehlerhaften Konfiguration führen. Dies beschränkt sich jedoch häufig nur auf das betroffene Paket. Das Fortsetzen einer Transaktion, bei der Pakete während der Ausführung entfernt werden, führt hingegen eher zu umfassenderen systemweiten Problemen.

Reihenfolge

Die Scriptlets in %pre und %post werden vor bzw. nach der Installation eines Pakets ausgeführt. Die Scriptlets in %preun und %postun werden vor bzw. nach der Deinstallation eines Pakets ausgeführt. Die Scriptlets in %pretrans und %posttrans werden zu Beginn und am Ende einer Transaktion ausgeführt. Bei einer Aktualisierung werden die Skripte in folgender Reihenfolge ausgeführt:

  1. %pretrans des neuen Pakets

  2. %pre des neuen Pakets

  3. (Paketinstallation)

  4. %post des neuen Pakets

  5. %triggerin anderer Pakete (ausgelöst durch die Installation eines neuen Pakets)

  6. %triggerin eines neuen Pakets (falls zutreffend)

  7. %triggerun eines alten Pakets (falls es durch die Deinstallation des alten Pakets ausgelöst wurde)

  8. %triggerun anderer Pakete (ausgelöst durch die Deinstallation des alten Pakets)

  9. %preun des alten Pakets

  10. (Entfernung des alten Pakets)

  11. %postun des alten Pakets

  12. %triggerpostun eines alten Pakets (falls es durch die Deinstallation des alten Pakets ausgelöst wurde)

  13. %triggerpostun anderer Pakete (falls es durch die Deinstallation des alten Pakets ausgelöst wurde)

  14. %posttrans des neuen Pakets

Das %pretrans-Scriptlet

Beachten Sie, dass das %pretrans-Scriptlet im Falle einer Systeminstallation ausgeführt wird, bevor überhaupt etwas installiert wurde. Daher darf es keinerlei Abhängigkeiten haben. Aus diesem Grund sollte %pretrans möglichst vermieden werden. Falls es verwendet wird, muss es zwingend in Lua geschrieben sein. Weitere Informationen finden Sie unter https://rpm-software-management.github.io/rpm/manual/lua.html.

Scriptlets schreiben

Hier sind einige Tipps zum Schreiben guter Scriptlets:

Speichern des Status zwischen Scriptlets

Manchmal muss ein Scriptlet den Zustand eines zuvor ausgeführten Scriptlets speichern, um ihn in einem später ausgeführten Scriptlet wiederzuverwenden. Dies ist besonders häufig der Fall, wenn versucht wird, die Scriptlets zu optimieren. Beispiele:

  • Wenn ein %posttrans beim Aktualisieren bestimmte Informationen deregistrieren muss, aber die Datei, die diese Informationen enthält, beim Entfernen des alten Pakets gelöscht wird, müssen die Scriptlets diese Datei während %pre oder %post speichern, damit das Skript in %posttrans darauf zugreifen kann.

  • Wenn wir wollen, dass das Programm in %posttrans seine Arbeit nur einmal pro Transaktion ausführt, müssen wir möglicherweise eine Flag-Datei schreiben, damit %posttrans weiß, ob eine Aktion ausgeführt werden soll.

Um diese Probleme zu beheben, müssen zuvor ausgeführte Scriptlets Informationen ausgeben, die in %posttrans verwendet werden. Wir empfehlen, dafür ein Unterverzeichnis von %{_localstatedir}/lib/rpm-state/ zu verwenden. Beispielsweise erstellen die Eclipse-Plugin-Skripte bei ihrer Installation eine Datei in %{_localstatedir}/lib/rpm-state/eclipse/. Das Skript in %posttrans prüft, ob diese Datei existiert. Falls ja, führt es seine Aktion aus und löscht die Datei anschließend. Dadurch wird die Aktion des Skripts nur einmal pro Transaktion ausgeführt.

Makros

Falls RPM-Datei-Trigger nicht geeignet sind, KÖNNEN komplexe Skripte, die von mehreren Paketen gemeinsam genutzt werden, in RPM-Makros platziert werden. Dies bietet zwei Vorteile:

  • Die Autoren der Standardpakete müssen sich nur die Makros merken, nicht die komplexen Vorgänge, die damit verbunden sind

  • Die Implementierungen der Makros können sich ändern, ohne dass das Paket aktualisiert werden muss

Bei der Erstellung der Makros wird das FPC die Makros dennoch überprüfen wollen (und möglicherweise die Implementierung der Makros in die Richtlinie aufnehmen, um den Paketierern zu zeigen, was hinter den Kulissen passiert).

Ein Grundprinzip des FPC ist, dass Makros im Allgemeinen keine Scriptlet-Tags (z. B. %pre) enthalten, da dies zusätzliche Aktionen im Scriptlet erschwert. Daher kann ein einzelnes Makro nicht sowohl Aktionen in %pre als auch in %post ausführen. Schreiben Sie stattdessen ein Makro für die Aktionen in %pre und ein separates Makro für die Aktionen in %post. Dieses Prinzip gewährleistet, dass alle Spec-Dateien Ihre Makros auf dieselbe Weise verwenden können, selbst wenn bereits %pre oder %post definiert ist.

Selbstverständlich ist es in der oben beschriebenen Situation besser, RPM-Datei-Trigger zu verwenden, wenn möglich.

Schnipsel

Einige Scriptlets zur Verwendung in bestimmten Situationen.

Linker-Konfigurationsdateien

Pakete, die Linker-Konfigurationsdateien in /etc/ld.so.conf.d ablegen, MÜSSEN ldconfig in %post und %postun aufrufen (auf allen Fedora-Versionen), selbst wenn sie keine Bibliotheken installieren. Sie DÜRFEN dazu NICHT die Makros %ldconfig, %ldconfig_post, %ldconfig_postun oder %ldconfig_scriptlets verwenden, da diese unter Fedora keine Wirkung haben. Rufen Sie stattdessen einfach ldconfig direkt in %post und %postun auf und fügen Sie gegebenenfalls die erforderlichen Abhängigkeiten hinzu:

%post -p /usr/bin/ldconfig
%postun -p /usr/bin/ldconfig

oder, als Teil vorhandener %post- oder %postun-Scriptlets:

Requires(post): /usr/bin/ldconfig
Requires(postun): /usr/bin/ldconfig
[...]
%post
[...]
ldconfig
[...]
%postun
[...]
ldconfig
[...]

Wenn die zu /etc/ld.so.conf.d hinzugefügte Konfigurationsdatei ein Verzeichnis angibt, in das andere Pakete Dateien installieren können, und sich dieses Verzeichnis nicht in der Verzeichnishierarchie unterhalb von /lib, /usr/lib, /lib64 oder /usr/lib64 befindet, dann MUSS das Paket, das die Konfigurationsdatei hinzufügt, auch die folgenden Dateiauslöser enthalten, die bewirken, dass ldconfig bei Bedarf automatisch ausgeführt wird:

%transfiletriggerin -P 2000000 -- VERZEICHNISSE
ldconfig

%transfiletriggerpostun -P 2000000 -- VERZEICHNISSE
ldconfig

Ersetzen Sie VERZEICHNISSE durch die durch Leerzeichen getrennte Liste der Verzeichnisse, die das Paket über die Konfigurationsdateien in /etc/ld.so.conf.d zum Bibliothekssuchpfad hinzufügt.

Benutzer und Gruppen

Diese werden auf einer separaten Seite besprochen.

GConf

Das veraltete Konfigurationssystem aus GNOME 2 (das nur noch von einer Handvoll Paketen verwendet wird) ist für die Nachwelt auf einer anderen Seite dokumentiert.

Systemd

Pakete mit systemd-Unit-Dateien benötigen Scriptlets, um die korrekte Verarbeitung dieser Dienste sicherzustellen. Dienste können standardmäßig entweder aktiviert oder deaktiviert sein. Um festzustellen, welcher Fall für Ihren spezifischen Dienst gilt, lesen Sie bitte die FESCo-Richtlinie hier. Bei einer Aktualisierung darf ein Paket einen Dienst nur dann neu starten, wenn dieser ausgeführt wird; es darf ihn nicht starten, wenn er deaktiviert ist. Außerdem darf sich der Dienst nicht selbst aktivieren, wenn er aktuell deaktiviert ist.

Scriptlets

Das systemd-Paket stellt eine Reihe von Hilfsmakros zur Verfügung, um systemd-Scriptlet-Aktionen zu handhaben. Diese Makros unterstützen systemd-„Presets“, wie in systemd.preset(5) dokumentiert.

BuildRequires: systemd-rpm-macros

[...]
%post
%systemd_post apache-httpd.service

%preun
%systemd_preun apache-httpd.service

%postun
%systemd_postun_with_restart apache-httpd.service

Manche Dienste unterstützen keinen Neustart (zum Beispiel D-Bus und verschiedene Speicherdienste). Wenn Ihr Dienst bei der Aktualisierung nicht neu gestartet, sondern stattdessen neu geladen werden soll, verwenden Sie bitte das folgende %postun-Scriptlet anstelle des oben gezeigten:

%postun
%systemd_postun_with_reload apache-httpd.service

Sollte Ihr Dienst nicht neu gestartet oder neu geladen werden, verwenden Sie stattdessen das folgende %postun-Scriptlet:

%postun
%systemd_postun apache-httpd.service

Diese Makros akzeptieren mehrere Argumente für Unit-Namen. Um die Anzahl der Aufrufe zu reduzieren, ist es besser, sie nur einmal aufzurufen.

Wenn Ihr Paket eine oder mehrere systemd-Units enthält, die bei der Paketinstallation standardmäßig aktiviert werden müssen, MÜSSEN diese der Fedora-Voreinstellungsrichtlinie folgen.

Benutzer-Units

Für Benutzer-Units (die unter %_userunitdir installiert sind) existieren zusätzliche Makros, die analog zu denen für System-Units verwendet werden. Diese aktivieren und deaktivieren Benutzer-Units gemäß den Voreinstellungen und lauten %systemd_user_post (zur Verwendung in %post) und %systemd_user_preun (zur Verwendung in %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

Die Makros %systemd_user_postun_with_restart und %systemd_user_postun_with_reload durchlaufen die laufenden Benutzerverwaltungsinstanzen und fordern die Neustart- und Neuladevorgänge für die angegebenen Units in jeder Instanz an.

Abhängigkeiten vom Systemd-Paket

Wenn Paket-Scriptlets andere systemd-Werkzeuge aufrufen, beispielsweise systemd-tmpfiles, SOLLTE das Paket die entsprechenden Abhängigkeiten deklarieren. Das Makro %{?systemd_requires} ist ein Kürzel, um systemd für die Skripte %post, %preun und %postun einzubinden. Beachten Sie, dass diese Abhängigkeiten für die oben aufgeführten Makros %systemd_{post,preun,postun_with_restart,user_post,user_preun} nicht erforderlich sind.

Wenn das Paket systemd-Werkzeuge verwenden möchte, sofern diese verfügbar sind, aber keine Abhängigkeit deklarieren möchte, dann kann das Makro %{?systemd_ordering} als abgeschwächte Form von %{?systemd_requires} verwendet werden, das nur während einer RPM-Transaktion eine Anfrage deklariert.

Die Makros haben die Form %{?systemd_requires} anstatt %systemd_requires, damit die Spec-Datei auch dann korrekt geparst werden kann, wenn die systemd-Makros noch nicht installiert sind.
Makro-Details

Für Details zu den Expandierung dieser Makros verweisen wir auf die folgenden Quellen: macros.systemd.in, triggers.systemd.in und daemon(7).

Shells

Die Datei /etc/shells ist eine Textdatei, die festlegt, ob eine Anwendung als System-Anmeldeshell für Benutzer verwendet werden darf. Sie enthält die Liste der im System zulässigen Shells. Wenn Sie eine neue Shell paketieren, müssen Sie Einträge in dieser Datei hinzufügen, die auf die hinzugefügten Shells verweisen. Weitere Informationen finden Sie unter man 5 SHELLS.

Da diese Datei von Systemadministratoren bearbeitet werden kann, müssen wir zunächst prüfen, ob die entsprechenden Zeilen bereits vorhanden sind. Falls nicht, geben wir einfach den Binärpfad der Shell in der Datei aus. Da die UsrMove-Funktion in Fedora 17 /bin zu einem symbolischen Link auf /usr/bin gemacht hat, müssen wir beide Pfade in die Datei /etc/shells eintragen. Hier ist ein Beispiel für das Skript, das mit der Shell „foo“ paketiert werden soll:

%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