Wer die Wahl hat, hat die Qual?

Wer jetzt ein politisches Statement zur Wahl erwartet, wird wohl enttäuscht werden, da sich dieser Blogpost um ein nettes kleines Werkzeug namens update-alternatives (oder je nach Distribution auch nur alternatives) dreht. Aber vorweg: Im Gegensatz zur Wahl kann ich hier meine Entscheidung jederzeit und nicht erst nach 4 Jahren überdenken.
Aktueller Auslöser für den Post ist mal wieder eine Kundendiskussion wegen verschiedener Java-Versionen, aber hierauf ist update-alternatives nicht beschränkt. Das Werkzeug dient der Konfiguration eines netten Features unter Linux. Statt dem eigentlich Programmaufruf findet hier eine Verlinkung statt, die es ermöglicht schnell zwischen den Alternativen umzuschalten um einen Standard festzulegen. Wer statt dem Standard seine persönliche Wunschalternative benutzen möchte hat hierbei trotzdem noch die Wahl diese direkt aufzurufen.
Da sich die Funktion am besten an einem Beispiel erklärt, möchte ich als erstes Beispiel den Editor unter Debian verwenden.
Debian verwendet update-alternatives um den Standardeditor festzulegen, aufzurufen ist dieser mit dem Kommando editor. Überprüft man nun was sich dahinter verbirgt sieht man folgendes:
# ls -l $(which editor)
lrwxrwxrwx 1 root root 24 14. Mär 10:42 /usr/bin/editor -> /etc/alternatives/editor

Ok, der Programmaufruf editor zeigt also auf eine Datei unter /etc/alternatives. /etc ist laut Filesystem Hierarchy Standard für “Host-specific system configuration”, handelt es sich also bei dieser datei um eine Konfigurationsdatei?
# ls -l /etc/alternatives/editor
lrwxrwxrwx 1 root root 9 14. Mär 10:42 /etc/alternatives/editor -> /bin/nano

Wie man sieht ist es keine Konfigurationsdatei sondern ein weiterer Link auf das eigentliche Programm.
Da mir persönlich diese Vorgabe nicht gefällt und ich die Kontrolle über das System habe, möchte ich mir vim als Standard setzen, wofür folgender Aufruf reicht (vorausgesetzt vim ist installiert):
# update-alternatives --config editor
Es gibt 3 Auswahlmöglichkeiten für die Alternative editor (welche /usr/bin/editor bereitstellen).
Auswahl Pfad Priorität Status
------------------------------------------------------------
* 0 /bin/nano 40 Auto-Modus
1 /bin/nano 40 manueller Modus
2 /usr/bin/vim.basic 30 manueller Modus
3 /usr/bin/vim.tiny 10 manueller Modus
Drücken Sie die Eingabetaste, um die aktuelle Wahl[*] beizubehalten,
oder geben Sie die Auswahlnummer ein: 2
update-alternatives: /usr/bin/vim.basic wird verwendet, um /usr/bin/editor (editor) im manueller Modus bereitzustellen.

Dies hat folgende Änderung zur Folge:
# ls -l /etc/alternatives/editor
lrwxrwxrwx 1 root root 18 9. Sep 13:51 /etc/alternatives/editor -> /usr/bin/vim.basic

Anhand dieses einfachen Beispiels kann man bereits sehen wie einfach es ist dieses System zu nutzen, dies ist aber nur möglich wenn dies die Packager entsprechend gut unterstützen. Hier kommen wir dann wieder zu meinem Beispiel java, diesmal auf einem Fedora-System.
Hier wird in unserem openjdk-1.7.0-Paket folgendes Skript verwendet um bei der Installation openjdk als eine Alternative für Java einzurichten:
ext=.gz
alternatives \
--install /usr/bin/java java /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/java 170025 \
--slave /usr/lib/jvm/jre jre /usr/lib/jvm/jre-1.7.0-openjdk.x86_64 \
--slave /usr/lib/jvm-exports/jre jre_exports /usr/lib/jvm-exports/jre-1.7.0-openjdk.x86_64 \
--slave /usr/bin/keytool keytool /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/keytool \
--slave /usr/bin/orbd orbd /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/orbd \
--slave /usr/bin/pack200 pack200 /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/pack200 \
--slave /usr/bin/rmid rmid /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/rmid \
--slave /usr/bin/rmiregistry rmiregistry /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/rmiregistry \
--slave /usr/bin/servertool servertool /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/servertool \
--slave /usr/bin/tnameserv tnameserv /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/tnameserv \
--slave /usr/bin/unpack200 unpack200 /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/unpack200 \
--slave /usr/share/man/man1/java.1$ext java.1$ext /usr/share/man/man1/java-java-1.7.0-openjdk.1$ext \
--slave /usr/share/man/man1/keytool.1$ext keytool.1$ext /usr/share/man/man1/keytool-java-1.7.0-openjdk.1$ext \
--slave /usr/share/man/man1/orbd.1$ext orbd.1$ext /usr/share/man/man1/orbd-java-1.7.0-openjdk.1$ext \
--slave /usr/share/man/man1/pack200.1$ext pack200.1$ext /usr/share/man/man1/pack200-java-1.7.0-openjdk.1$ext \
--slave /usr/share/man/man1/rmid.1$ext rmid.1$ext /usr/share/man/man1/rmid-java-1.7.0-openjdk.1$ext \
--slave /usr/share/man/man1/rmiregistry.1$ext rmiregistry.1$ext /usr/share/man/man1/rmiregistry-java-1.7.0-openjdk.1$ext \
--slave /usr/share/man/man1/servertool.1$ext servertool.1$ext /usr/share/man/man1/servertool-java-1.7.0-openjdk.1$ext \
--slave /usr/share/man/man1/tnameserv.1$ext tnameserv.1$ext /usr/share/man/man1/tnameserv-java-1.7.0-openjdk.1$ext \
--slave /usr/share/man/man1/unpack200.1$ext unpack200.1$ext /usr/share/man/man1/unpack200-java-1.7.0-openjdk.1$ext

Wie man sieht ist es also nicht nur möglich ein einzelnes Programm zu verlinken sondern auch alle dazugehörigen Unterprogramme und Hilfen, so dass nicht eine einzelne Komponente vergessen wird. Die Syntax hierbei ist:
alternatives --install <Link> <Name> <Pfad> <Priorität>
[--slave <Link> <Name> <Pfad>]*

Der Link ist hierbei der Programmaufruf, der Name legt fest wie die Datei unter /etc/alternatives heißt, der Pfad ist der Pfad zum eigentlich Programm und die Priorität wird nur für das Hauptprogramm festgelegt und ist für die automatische Auswahl der best-möglichen Alternative zuständig, wobei die höchste Zahl gewinnt.
Damit wäre ich auch wieder bei meinem Ausgangspunkt für die geführte Diskussion beim Kunden, denn leider nutzt das Java-Paket von Oracle update-alternatives nicht. Schlimmer sogar ist dass es seine eigene Logik hierfür mitbringt, die update-alternatives für java zerstört. Wer es trotzdem nutzen möchte, kann auf den Tar-Ball zurückgreifen und diesen selbst mit dem oben stehenden Kommando und angepassten Pfaden einrichten. Dadurch wäre es dann möglich als Admin festzulegen welches Java standardmäßig zu verwenden ist um die Version mit den aktuellsten Sicherheitsfixes vorzugeben. Läuft etwas mit dieser Version nicht muss dort nur statt java aus dem Suchpfad java mit vollem Pfad aufgerufen werden.
Natürlich kann ich es auch nutzen um meine eigene Software parallel zu installieren, zu testen und irgendwann nach erfolgreichem Test auf die neue Version umzuschalten. Ein Beispiel gefällig?
LConf mit Versionsnummer im Präfix:
update-alternatives \
--install /usr/bin/LConfExport LConfExport /usr/local/LConf-1.2.1/bin/LConfExport.pl 10201 \
--slave /usr/bin/LConfImport LConfImport /usr/local/LConf-1.2.1/bin/LConfImport.pl \
--slave /usr/bin/LConfSlaveExport LConfSlaveExport /usr/local/LConf-1.2.1/bin/LConfSlaveExport.pl \
--slave /usr/bin/LConfSlaveExportRules LConfSlaveExportRules /usr/local/LConf-1.2.1/bin/LConfSlaveExportRules.pl \
--slave /usr/bin/LConfSlaveSync LConfSlaveSync /usr/local/LConf-1.2.1/bin/LConfSlaveSync.pl \
--slave /usr/bin/LConfDeploy LConfDeploy /usr/local/LConf-1.2.1/bin/LConfDeploy.sh
update-alternatives \
--install /usr/bin/LConfExport LConfExport /usr/local/LConf-1.3.0/bin/LConfExport.pl 10300 \
--slave /usr/bin/LConfImport LConfImport /usr/local/LConf-1.3.0/bin/LConfImport.pl \
--slave /usr/bin/LConfSlaveExport LConfSlaveExport /usr/local/LConf-1.3.0/bin/LConfSlaveExport.pl \
--slave /usr/bin/LConfSlaveExportRules LConfSlaveExportRules /usr/local/LConf-1.3.0/bin/LConfSlaveExportRules.pl \
--slave /usr/bin/LConfSlaveSync LConfSlaveSync /usr/local/LConf-1.3.0/bin/LConfSlaveSync.pl \
--slave /usr/bin/LConfDeploy LConfDeploy /usr/local/LConf-1.3.0/bin/LConfDeploy.sh

Damit hätte man automatisch durch die höhere Priorität Version 1.3.0 im Einsatz, könnte es manuell auf 1.2.1 umschalten (update-alternatives --config) und solange mit den neuen Optionen experimentieren bis man bereit ist dieses produktiv zu nutzen (update-alternatives --auto). Und man kann auch anschließend noch jederzeit prüfen ob sich die beiden Versionen unterschiedlich verhalten.
Ich hoffe man sieht den Nutzen dieses Werkzeugs, dass es keine Hexenwerk ist dieses zu verwenden und welche Situationen im Adminleben es einem einfacher macht!

Dirk Götz
Dirk Götz
Senior Consultant

Dirk ist Red Hat Spezialist und arbeitet bei NETWAYS im Bereich Consulting für Icinga, Puppet, Ansible, Foreman und andere Systems-Management-Lösungen. Früher war er bei einem Träger der gesetzlichen Rentenversicherung als Senior Administrator beschäftigt und auch für die Ausbildung der Azubis verantwortlich wie nun bei NETWAYS.