Druckversion

Integration von Software

Übersicht Weiter

Während der Installation eines Linuxsystems erfolgte eine mehr oder minder detaillierte Selektion zu installierender Software. Der Bedarf der nachträglichen (De)Installation verschiedener Pakete wird irgendwann erwachsen, sei es, um die zu klein gewordene Festplatte von unnötigem Ballast zu befreien oder ein Programm durch eine verbesserte Nachfolgeversion zu ersetzen. Vielleicht lag das gute Stück Software gar nicht »meiner« Distribution bei? Dann muss es nachträglich ins System eingespielt werden.

I.A. ist es nicht erforderlich, jede Version seiner favorisierten Distribution zu erwerben. Solange diese nicht an ihrer Basis schrauben (bspw. eine neue Version der glibc verwenden), schont die alleinige Aktualisierung der häufig verwendeten Programme nicht nur den Geldbeutel, sondern schließt auch den möglichen Ärger aus, den eine Neuinstallation (bzw. Update) so mit sich bringt. »Never change a running system« lautet die Maxime des Profis!

Programme unter Unix bestehen nur in seltenen Fällen aus einer einzelnen Datei. Zum Paket zählen zumeist noch Dokumentationen, Bibliotheken, Konfigurationsdateien u.v.m. Als Struktur eines solchen Pakets haben sich im wesentlichen drei Formate etabliert:

  • Das alteingesessene Tar-Archiv (auch gepackt)
  • Das verbreitete Rpm-Format (bspw. RedHat, SuSE)
  • Das von Debian verwendete Deb-Format
  • Das (hier nicht betrachtete) Slp-Format von Stampede

Bei rpm und deb handelt es sich im Unterschied zu tar-Archiven um Paket-Manager, d.h., diese Formate kombinieren die reine Archivierung zusammen gehöriger Paketkomponenten mit Methoden zur sauberen (De)Installation und Aktualisierung. Sie helfen ungemein, die Konsistenz der installierten Software zu wahren.

Kompilierte Versionen zu aktualisierter Software bieten die Großen der Branche meist wenige Tage nach deren Freigabe auf ihren Servern ein. Das Herunterladen und Installieren solcher Pakete im »richtigen« Format mit den distributionseigenen Werkzeugen birgt selten Probleme in sich und sollte stets bevorzugt werden. Nicht jede Distribution jedoch misst einem Programm die gleiche Aufmerksamkeit zu. Teils existieren vorkompilierte Pakete nur in einem Format, das ausgerechnet »meine« Distribution nicht unterstützt! In einem solchen Fall kann ein kleines Perl-Skript namens alien Wunder wirken, konvertiert es so manches Paket doch anstandslos in ein Fremdformat.

Wer aus dem vollen Fundus des reichhaltigen Angebots an freier Software schöpfen will, der wird vielfach den Tar-Archiven (*.tar, *.tar.gz, *.tgz) begegnen. Der Vorteil des Formats liegt in seiner Unabhängigkeit von jeglicher Dateisystemstruktur. Der gravierende Nachteil gründet sich auf die zumeist fehlende Routine zur sauberen (De)Installation solcher Programme.

Das Tar-Archiv ist auch die bevorzugte Verpackung für Quellcode-Pakete. Hieraus ein lauffähiges Programm zu kompilieren, kann spielend leicht als auch frustrierend bis unmöglich sein. Der Neuling auf dem Gebiet der Programmierung wird einem Fehler hilflos gegenüber stehen. Die Prinzipien, nach denen sich eine Ursache des Fehlers eingrenzen lässt, möchten wir daher erläutern.

Source-Pakete Zurück Anfang Weiter

Open Source lebt von der Mitarbeit Freiwilliger. Natürlich ist in erster Linie der Programmierer gefragt, aber die Beihilfe eines jeden in der Testphase garantiert das Aufspüren möglichst vieler Fehler (»Irgend jemand entdeckt den Fehler. Ein anderer wird ihn beheben können.«). Aus diesem Grund gehen die meisten Projekte bereits in einer sehr frühen Entwicklungsphase an die Öffentlichkeit und bieten Schnappschüsse des aktuellsten Standes auf ihren Servern an. Eine als Beta-Version gekennzeichnete Software muss nicht zwingend fehlerbehaftet sein. Oftmals fehlen noch wesentliche Teile, die die Entwickler für notwendig erachten, der eine oder andere Anwender aber nicht benötigt. Warum sollte man das Programm nicht doch schon nutzen?

Derartige Entwicklerversionen werden nahezu ausschließlich als Quelltext angeboten. Was tut man nun, nachdem das Paket auf die lokale Platte geladen wurde?

Das verbreitete Format für Quellcodepakete ist das Tar- Archiv (Tape Archiver). Typische Endungen solcher Dateien lauten ».tar« bzw. im Fall komprimierter Daten ».tar.gz« oder ».tgz«. In neuerer Zeit finden Sie vermehrt ».tar.bz2«, was auf eine Komprimierung mit dem effektiven Burrows-Wheeler-Algorithmus hindeutet. Auch in den Source-Rpm-Paketen (».src.rpm«), in welcher Form vielfach die Quellen fertiger Projekte verbreitet werden, stecken letztlich Tar-Archive, mit denen analog zur folgenden Beschreibung zu verfahren ist.

Beginnend mit dem Entpacken des Quellpakets betrachten wir die notwendigen Schritte zu einem lauffähigen Programm anhand von Galeon, einem auf Mozilla basierenden Browser-Projekt. Im Januar 2001 war die Version 09pre3 aktuell.

Entpacken der Quellen

user@sonne> tar xzf galeon-0.9pre3.tar.gz
user@sonne> cd galeon-0.9pre3
user@sonne> ls
ABOUT-NLS       INSTALL      TODO         configure       install-sh     po
AUTHORS         Makefile.am  acconfig.h   configure.in    intl           src
COPYING         Makefile.in  aclocal.m4   galeon.desktop  macros         stamp-h.in
COPYING.README  NEWS         anim         galeon.gnorba   missing        ui
ChangeLog       README       autogen.sh   galeon.spec     mkinstalldirs
FAQ             THANKS       config.h.in  galeon.spec.in  myportal.css

Eine Reihe der enthaltenen Dateien sind typisch für Quellcodepakete (nahezu jedes aktuelle Projekt verwendet autoconf/automake, womit die Existenz bestimmter Dateien zwingend gegeben ist). INSTALL sollten Sie auf jeden Fall lesen, beinhaltet die Datei doch eine detaillierte Beschreibung der notwendigen Schritte bis zur Installation der kompilierten Programme. README beschreibt zumeist die Fähigkeiten des Programms und seine Anwendung. In NEWS erfahren Sie die wesentlichen Änderungen der Version gegenüber seinen Vorgängern und TODO gibt einen Ausblick auf die zukünftige Richtung. Falls Probleme bei den weiteren Schritten auftauchen, sollten Sie einen Blick in FAQ (»Häufig gestellte Fragen«) werfen, bevor Sie sich Hilfe suchend an die Projektmitglieder wenden.

Erzeugen des Makefiles

Befindet sich im Stammverzeichnis des Quellpakets keine Datei mit dem Namen »Makefile« (bzw. »makefile«), so muss diese zunächst erstellt werden. Hierfür existieren (fast) immer Shellskripte namens configure (manchmal auch kurz config oder config.sh). Starten Sie dieses:

user@sonne> ./configure
creating cache ./config.cache
checking for a BSD compatible install... /usr/bin/ginstall -c
checking whether build environment is sane... yes
checking whether make sets ${MAKE}... yes
checking for working aclocal... found
checking for working autoconf... found
checking for working automake... found
checking for working autoheader... found
checking for working makeinfo... found
checking for gnome-config... /opt/gnome/bin/gnome-config
...
checking for Mozilla... no
configure: error:
        *** Mozilla 0.7 is required
        *** A package for 0.7 is available here:
        *** http://people.redhat.com/blizzard/software/.

Sinn solcher configure-Skripte ist das Erzeugen eines auf die lokalen Gegebenheiten zugeschnittenen Makefiles. Was konkret passiert, hängt natürlich vom Verfasser des Skripts ab (i.d.R. greift der Verfasser hierzu auf vorgefertigte Makros des autoconf/automake-Pakets zurück). Typische Punkte sind:

  • Test, ob vom Programm benötigte Software (Programme, Bibliotheken,...) installiert ist
  • Prüfen der Hardware (bspw. um festzustellen, für welchen Prozessortyp das Programm zu optimieren ist)
  • Prüfen der zum Kompilieren notwendigen Software (bspw. wird der Compiler getestet, welche Optionen er kennt)

Unser Beispiel deckt eine der häufigsten Ursachen für einen gescheiterten »configure«-Aufruf auf: Das Fehlen eines Pakets (hier Mozilla der Version 0.7). Abhilfe sollte die nachträgliche Installation des erforderlichen Pakets bringen.

Nicht selten jedoch stottert »configure« über fehlende Dateien, die aber definitiv installiert sind. »configure« selbst sucht allerdings nur in Standardpfaden; wird es dort nicht fündig, ist die Fehlermeldung die Konsequenz. Spätestens hier sollten Sie die Option --help konsultieren. Eventuell findet sich eine Option, mit der »configure« der rechte Fleck gewiesen werden kann.

user@sonne> ./configure --help
Usage: configure [options] [host]
Options: [defaults in brackets after descriptions]
Configuration:
  --cache-file=FILE       cache test results in FILE
  --help                  print this message
  --no-create             do not create output files
  --quiet, --silent       do not print `checking...' messages
  --version               print the version of autoconf that created configure
Directory and file names:
  --prefix=PREFIX         install architecture-independent files in PREFIX
                          [/usr/local]
  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
                          [same as prefix]
  --bindir=DIR            user executables in DIR [EPREFIX/bin]
...

Dem (Shell-)Programmierer bleibt als letzte Option noch die direkte Suche des Kodeabschnitts, der den Fehler verursachte. Ein hartes Setzen der dortigen Variablen kann über so manche Klippe helfen, erfordert jedoch tiefe Kenntnisse der Programmierung.

Wenn »configure« mit der Systemumgebung zufrieden ist, wird es abschließend ein Makefile erzeugen:

user@sonne> ./configure
...
creating Makefile
user@sonne>

Übersetzen

Bei dem im vorigen Schritt erzeugten Makefile handelt es sich um eine Steuerdatei, die die weiteren Schritte zur Kompilierung automatisiert. I.d.R. beinhaltet sie eine Reihe so genannter »Ziele« (targets). Jedes dieser Targets führt zu einem anderen Kontrollfluss. Detailliertere Informationen finden Sie im Kapitel Unix-Werkzeuge.

Das erste im Makefile formulierte Ziel ist das Default-Target, d.h. wird make ohne den Namen eines Ziels aufgerufen, beginnt es mit der Ausführung dieses ersten Targets. Die Übersetzung des Paketes beginnt somit i.A. durch den Aufruf von make:

user@sonne> make
cd /home/user/galeon-0.9pre3/src
...

Die zahlreichen Ausgaben während des »Builds« protokollieren die Tätigkeit von make. Schritte des Kompilierens finden (bei C- und C++-Programmen) in den mit »gcc ...« bzw. »g++...« beginnenden Zeilen statt. Interessant sind die Ausgaben wohl eher für den Programmierer. Bewahren Sie die Ruhe, wenn »Warnings« über den Bildschirm rauschen. Die Compiler weisen hier auf mögliche Fallstricke des Programms hin, die (zumeist;-) vom Programmierer beabsichtigt sind.

Ein Abbruch des Vorgangs mit einer »Error«-Meldung ist hingegen ein schwerwiegender Fehler. Bei der Fülle der Ursachen würde eine detaillierte Diskussion dieser einen einführenden Programmierkurs bedingen, aber zu einigen Fehlerklassen lassen sich prinzipielle Ansatzpunkte zur Fehlereingrenzung angeben:

Datei oder Verzeichnis nicht gefunden

In file included from foobar.c:2:
/opt/gnome/include/gnome.h:12: gnomesupport.h: Datei oder Verzeichnis nicht gefunden

Eine typische Ausgabe für eine fehlende Datei oder eine Datei, die nicht gefunden werden kann (»gnomesupport.h« im Beispiel). Hier sollen Sie zunächst feststellen, ob die Datei im System existiert: »locate Dateiname«. Falls nicht, so durchsuchen Sie die Installations-CDs bzw. im Web nach dem Paket, das diese Datei enthält, und installieren es nach.

Ist die Datei allerdings vorhanden, so bieten sich im Falle einer Header-Datei (Endung ».h«) drei Lösungswege an:

Setzen der Compiler-Umgebungsvariablen

Die Variable »C_INCLUDE_PATH« (im Falle des C-Compilers [gcc, cc, egcs]) bzw. »CPLUS_INCLUDE_PATH« (C++-Compiler [g++, c++]) wird mit dem Pfad zur fehlenden Datei belegt:

user@sonne> locate gnomesupport.h
/opt/gnome/lib/gnome-libs/include/gnomesupport.h
user@sonne> export C_INCLUDE_PATH=$(C_INCLUDE_PATH):/opt/gnome/lib/gnome- libs/include

Anpassen des Makefiles

Im Makefile kann nach dem den Fehler verursachenden Compileraufruf gesucht werden. Am Ende der Zeile ist -IPfad_zur_Datei hinzuzufügen (diese Möglichkeit bleibt wohl eher dem Profi vorbehalten).

Link in ein Standard-Include-Verzeichnis

Es kann ein Link auf die vermisste Datei in einem der Standard-Include-Verzeichnisse (bspw. »/usr/include«) gesetzt werden. Bei dieser »unsauberen« Methode empfiehlt es sich, den Link nach erfolgreicher Kompilierung des Pakets wieder zu entfernen.

root@sonne> ln -s /opt/gnome/lib/gnome-libs/include/gnomesupport.h /usr/include/

Eine Bibliothek wurde nicht gefunden

/usr/i486-suse-linux/bin/ld: cannot find -lgnome
collect2: ld returned 1 exit status

Der Linker kann eine Bibliothek nicht lokalisieren. »-lgnome« ist eine Compileranweisung, die Bibliothek »libgnome.a« hinzuzulinken (Beachten Sie die Namensgebung: Als Compileroption wird vom Dateinamen der Bibliothek sowohl das Prefix »lib« als auch das Suffix ».a« weggelassen!). Falls die alleinige Installation des die Bibliothek enthaltenden Pakets das Problem nicht beheben kann, sollte die Variable »LIBRARY_PATH« gesetzt und exportiert werden (mehrere Pfade lassen sich durch Doppelpunkt getrennt angeben):

user@sonne> locate libgnome.a
/opt/gnome/lib/libgnome.a
user@sonne> export LIBRARY_PATH=$(LIBRARY_PATH):/opt/gnome/lib

Undefined Reference...

/opt/gnome/lib/libgnome.a(gnome-config.o): In function `_gnome_config_set_vector':
gnome-config.o(.text+0x2630): undefined reference to `g_free'/

Derartige Meldungen können so ziemlich alles bedeuten. Für 90% der Fälle zeichnet eine falsche Bibliotheksversion dafür verantwortlich; eine Aktualisierung kann notwendig werden. Wer über etwas Programmiererfahrung verfügt, könnte vorerst das Kommando nm bemühen und die Bibliothek suchen, die das fehlende Symbol (im Beispiel »g_free«) definiert. Existiert sie, so kann die Compilerzeile im Makefile um die Anweisung des Linkens dieser Bibliothek ergänzt werden. Weitere Hinweise zur Problematik von Bibliotheken und Symbolen finden Sie unter Bibliotheken und im Abschnitt zur Bashprogrammierung (Komplexe Anwendungen).

Sie merken schon, dass die Behandlung von Compiler-Fehlern besser dem Experten vorbehalten sein sollte. Mit Ausnahme der nicht gefundenen Bibliothek erfordert eine saubere Lösung stets den Eingriff in die Quellen des Pakets (zumindest des Makefiles). Ich möchte es bei diesem kleinen Ausflug in die Trickkiste der Programmierer belassen...

Installation

Die genaue Anweisung kann der Datei INSTALL aus dem Quellverzeichnis entnommen werden. Der Ort der Installation lässt sich häufig beim einführenden »configure« per Option variieren, der Vorgang selbst wird üblicherweise im Makefile realisiert:

root@sonne> make install

Weniger verbreitet ist die Beigabe eines Skripts install oder install.sh. Falls vorhanden, finden Sie dieses im Basisverzeichnis des Pakets oder unterhalb des dortigen ./bin-Verzeichnisses.

Deinstallation

Nur wenige Projekte bringen eine eigene Routine zum Entfernen der ins System eingespielten Dateien mit. Denn fast ebenso wie die Dokumentation scheut der Entwickler derartige Nebensächlichkeiten, sodass automatische »Hilfsroutinen« - falls überhaupt - erst bei Abschluss eines Projekts zu diesem hinzugefügt werden.

Für den Fall, dass eine Deinstallation per Skript vorgesehen ist, finden Sie im Basisverzeichnis des Quellpakets ein Skript uninstall bzw. uninstall.sh, in manch anderen Fällen beinhaltet das Makefile ein »Ziel« zur Deinstallation:

# Nur selten bietet ein Makefile dieses...
root@sonne> make uninstall

So sehr der Anwender die Möglichkeit der automatischen Deinstallation auch missen mag; die Entwickler haben teils bewusst auf deren Implementierung verzichtet. Die Problematik steckt in der fehlenden Datenbasis, die die Zugehörigkeiten und Abhängigkeiten der einzelnen Dateien protokolliert. Stellen Sie sich vor, was passiert, wenn Sie Bibliotheken deinstallieren, die von einer Reihe anderer Anwendungen gefordert werden. Diese Anwendungen würden nicht mehr funktionieren!

Wünschenswert wäre, wenn per Makefile aus den kompilierten Quellen ein Rpm-oder Deb-Paket erzeugt werden würde. Dass dies mit relativ vertretbarem Aufwand möglich ist, soll in späteren Abschnitten demonstriert werden. Vielleicht inspiriert dies den Leser, die Installation solcher »handerzeugten« Pakete über den Bau eines eigenen RPM-Pakets zu realisieren...

Patch Zurück Anfang Weiter

Wenn Entwickler eines Projekts es für sinnvoll erachten, eine neue Version des Quellkodes zu veröffentlichen, so ist der Weg des geringsten Aufwands das Schnüren eines aktualisierten Komplettpakets.

Bei geringem Datenaufkommen ist das Verfahren durchaus legitim, aber bei mehrere Megabytes umfassenden Quellkode-Dateien wird so mancher freiwillige Tester das langwierige und kostspielige Herunterladen der letzten Version scheuen. Effizient wäre, ein Paket nur einmalig komplett zu übertragen und nachfolgend einzig die Änderungen zwischen den Versionen einzupflegen. Ein solches Verfahren wird als Patchen (»korrigieren«) bezeichnet und die Datei, die die Unterschiede zwischen zwei Versionen beinhaltet, nennt sich Patch.

Erstellen eines Patches

Nichts schult das Verständnis für die Funktionsweise eines Programms eindrucksvoller als ein Beispiel. Deshalb soll dem »Patchen« eine kurze Abhandlung über das Erstellen der benötigten Patchdateien voran stehen. Sicher wird nur der Programmierer in die Verlegenheit gelangen, Patches zu erstellen. Deswegen werden wir uns auf das minimal erforderliche Wissen beschränken.

Ausgangspunkt sei eine einfache Textdatei:

user@sonne> cat ErsteVersion.txt
Ein einfaches Beispiel,
um die Arbeitsweise von "patch"
zu demonstrieren.

Aufgrund der immensen Wichtigkeit der Aussagen des Inhalts der Datei kopieren sie sich eine Menge Leute. Der Autor indess modifiziert den Text in einer seiner kreativen Phasen:

user@sonne> cat ZweiteVersion.txt
Dies ist ein einfaches Beispiel,
um die Arbeitsweise von "patch"
zu demonstrieren.

Allen Eignern der Kopie der ersten Version die neue Datei zukommen zu lassen ist bei solch kleinen »Projekten« praktikabel. Aber ebenso genügt es, die Änderungen kund zu geben. Und hierbei gelangt das Kommando diff zum Einsatz:

user@sonne> diff -u ErsteVersion.txt ZweiteVersion.txt
--- ErsteVersion.txt    Wed Apr 25 21:06:20 2001
+++ ZweiteVersion.txt   Wed Apr 25 21:06:42 2001
@@ -1,3 +1,3 @@
-Ein einfaches Beispiel,
+Dies ist ein einfaches Beispiel,
um die Arbeitsweise von "patch"
zu demonstrieren.

Zugegeben... das Beispiel ist schlecht gewählt, da die Ausgabe von »diff« den Umfang der »neuen« Datei bereits überschreitet. Aber bekanntlich heiligt der Zweck die Mittel.

Die Ausgabe von diff bedarf einer Aufbereitung, um patch die notwendigen Informationen zukommen zu lassen. Im Beispiel wählten wir das unified Ausgabeformat (Option -u), das eine »leserliche« Form des context Formats (Option -c) darstellt. Genau diesen »Kontext« benötigt später patch, um die Änderungen an den richtigen Positionen der Zieldatei einpflegen zu können.

Mit den vorgestellten Optionen werden neben den Unterschieden auch die ersten drei unveränderten Zeilen in die Ausgabe von diff übernommen. Sie dienen als »Anhaltspunkt« für patch, um auch dann noch »sinngemäß korrekte« Korrekturen zu ermöglichen, wenn Zeilen in das Original eingefügt oder daraus entfernt wurden. Genügt der Kontext nicht, um eine eindeutige Abbildung zu erzielen, kann er mittels -U n (unified) bzw. -C n auf n Zeilen gesetzt werden.

Nun bestehen große Projekte nicht nur aus einer Datei. Und für jede einen eigenen Patch zu erstellen, würde das Interesse am Patchen schnell schwinden lassen. Aus diesem Grund kann diff auch auf Verzeichnisse angesetzt werden. Ist nur eine der beiden Angaben in der Kommandozeile ein Verzeichnis, so wird der Name der anderen Datei in diesem Verzeichnis gesucht. Ist diff fündig, vergleicht es diese beiden Dateien.

Handelt es sich bei beiden Argumenten um Verzeichnisse, vergleicht diff alle Dateien mit identischen Namen und erzeugt eine gemeinsame Ausgabe. Dateien, die nur in einem Verzeichnis vorhanden sind, erscheinen als »Only in Verzeichnis: Datei« in der Ausgabe. Hier kann die Option --new-file hilfreich sein, um »neue« Dateien (die aus dem zweiten Verzeichnis) vollständig in die Ausgabe einfließen zu lassen (diff arbeitet so, als wäre die Datei vorhanden aber leer). Schließlich werden mit der Option -r auch Unterverzeichnisse rekursiv betrachtet.

Um endlich zum Erstellen des notwendigen Patches zu gelangen... leiten wir die Ausgabe von diff in eine Datei:

user@sonne> diff -u ErsteVersion.txt ZweiteVersion.txt > patch_ErsteVersion

Bei umfangreichen Patchdateien lohnt sich das Komprimieren derselben.

user@sonne> gzip patch_ErsteVersion
user@sonne> ls
patch_ErsteVersion.gz

Einspielen eines Patches

Die Aktualisierung der Originaldatei anhand des Patches gestaltet sich für unser Beispiel äußerst einfach. Die Datei mit dem Patch ist zu entpacken und dem Programm patch über die Standardeingabe zuzuführen:

user@sonne> ls
ErsteVersion.txt
user@sonne> gunzip -c patch_ErsteVersion.gz | patch
patching file ErsteVersion.txt

Wichtig ist, dass der Kommandoaufruf aus dem Verzeichnis mit der Originaldatei heraus erfolgt. Sonst würde patch die zu »patchende« Datei nicht finden.

Patchdateien, die rekursiv über Verzeichnisse erzeugt wurden, enthalten anstatt des reinen Dateinamens den kompletten Pfad zu jeder zu »patchenden« Datei. Stimmen nun die Verzeichnisstrukturen beim Patch-Erzeuger und Patch-Anwender nicht überein, so wird »patch« die zu korrigierende Datei nicht finden und reagiert leicht verärgert:

user@sonne> pwd
/home/user/sonstwo
user@sonne> gunzip -c ../linuxfibel-0.6.0-0.7.0.patch.gz | patch
can't find file to patch at input line 4
Perhaps you should have used the -p or --strip option?
The text leading up to this was:
--------------------------
|diff -u -r --new-file fibel_old/access.htm linuxfibel/access.htm
|--- fibel_old/access.htm       Sun May 6 17:14:57 2001
|+++ linuxfibel/access.htm       Fri Jun 29 15:08:22 2001
--------------------------
File to patch:

Die erste Option, die Ihnen bleibt, wäre nun tatsächlich, den Zugriffspfad zur betreffenden Datei an der Eingabeaufforderung anzugeben. Bei wenigen Dateien ein legitimes Vorgehen; bei umfangreichen Patches artet die Methode nur zu schnell in echte Arbeit aus. Die Alternative, die lokale Verzeichnisstruktur dem des Patch-Erzeugers anzugleichen, ist ein gangbarer, aber ebenso unbequemer Weg. Besser, wir bemühen die Option -pAnzahl, die »patch« veranlasst, die angegebene Anzahl von Verzeichnisebenen aus den Pfadangaben in der Patchdatei zu entfernen. Aus einer Angabe von -p1 in obigem Beispiel resultiert, dass sowohl die Dateinamen »fibel_old/access.htm« als auch »linuxfibel/access.htm« als »access.htm« angenommen werden. Befinden Sie sich also im »Wurzelverzeichnis« des zu patchenden Zweigs, ist »patch« nun in der Lage, alle Dateien zu finden.

Patchen ist genau das, was dieser Abschnitt vermuten lässt: Es ist schwierig und fehleranfällig. Aus diesem Grund sollte eine Sicherung der originalen Daten zum allgemeinen Vorgehen zählen; es genügt die Option -b (Backup), um die Arbeit von »patch« persönlich erledigen zu lassen. Für jede Datei, die nachfolgend verändert wird, wird zuvor eine Kopie als »Dateiname.orig« erzeugt (Suffix per Option änderbar). Im Fehlerfall - und bei etwas Kenntnis der Shellprogrammierung - richtet ein kleiner Einzeiler den Schaden:

user@sonne> for i in `find . -name "*.orig"`; do mv $i ${i%*.orig}; done

Nicht jeder Fehler ist so schwerwiegend, als dass der ganze Patch gleich über den Haufen geworfen werden müsste. Manchmal betrifft es nur einzelne Dateien, die das Kommando nicht korrekt zu behandeln weiß. Gerade wer gern mit neuesten Kernelpatches experimentiert, wird einmal Patches einspielen, die sich mit den Änderungen eines früher angewandten Patches nicht vertragen. Kann »patch« Korrekturen nur zum Teil eindeutig in eine Datei einarbeiten, so legt es alle Bestandteile des Patches, mit denen es nichts anzufangen weiß, in eine Datei »Dateiname.rej« ab. Eventuell kann dieser »Rest« manuell an die richtigen Positionen geschrieben werden...

Linuxfibel-Patch

Exemplarisch soll das Erzeugen und Einspielen eines Patches anhand der Linuxfibel demonstriert werden. Hierbei liegen die alten Quellen im Verzeichnis »fibel_old « und die neue Version befindet sich unterhalb von »linuxfibel«. Für »diff« verwenden wir die Optionen -u zum Erzeugen eines Kontexts, -r zum rekursiven Absteigen in die Unterverzeichnisse und --new-file zur Übernahme neuer Dateien in den Patch. Da es sich bei der Ausgabe von »diff« um Text handelt, lohnt sich das anschließende Komprimieren enorm:

user@sonne> diff -u -r --new-file fibel_old linuxfibel > linuxfibel-0.6.0-0.7.0.patch
user@sonne> ls -l linuxfibel-0.6.0-0.7.0.patch
-rw-r--r--   1 user      users      2141291 Jul  5 16:44 linuxfibel-0.6.0-0.7.0.patch
user@sonne> gzip -9 linuxfibel-0.6.0-0.7.0.patch
user@sonne> ls -l linuxfibel-0.6.0-0.7.0.patch
-rw-r--r--   1 user      users       363441 Jul  5 16:49 linuxfibel-0.6.0-0.7.0.patch

Für das Einspielen des Patches nehmen wir nachfolgend an, dass sich die Dateien unterhalb von »/usr/share/doc/linuxfibel« und der Patch im Heimatverzeichnis von »user« befinden. Der folgende Aufruf aktualisiert die Dateien:

# vermutlich darf nur Root in /usr/share/doc/linuxfibel schreiben...
root@sonne> cd /usr/share/doc/linuxfibel
root@sonne> gunzip -c ~user/linuxfibel-0.6.0-0.7.0.patch.gz | patch -p1
patching file access.htm
patching file allekapitel.htm
patching file anxious.htm
patching file archiv.htm
...

Tar-Archive Zurück Anfang Weiter

Seit dem Durchbruch der Paketformate Rpm und Deb hat die Bereitstellung kompilierter Software in Form von Tape Archiven stark abgenommen; die Gründe sind u.a. in der Einleitung zum Abschnitt des RedHat Package Managers zu lesen. Von den namhaften Distributionen ist Slackware als einzige diesem historischen Format treu geblieben.

Sollten Sie in die Zwangslage geraten, ein solches Paket in ihrem (Nicht-Slackware-) System zu installieren, dann vergewissern Sie sich auf jeden Fall, dass die Verzeichnisstruktur im Paket auch relative Pfadangaben verwendet:

root@sonne> tar tzf SoftwarePaket.bin.tgz
./SoftwarePaket/README
./SoftwarePaket/INSTALL
./SoftwarePaket/bin/proggy
...

Nutzt das Paket absolute Pfadangaben (mit dem Backslash beginnend), so verwenden Sie sicherheitshalber die Option -C /Installationspfad, um die Dateien des Pakets nicht gleich ohne vorherigen Test direkt im Dateisystem zu verstreuen. Gerade hier handeln Sie sich eine Menge Probleme ein, wenn das Paket nicht auf die Verzeichnisstruktur Ihrer Distribution abgestimmt wurde.

Nach der Installation (Option -x anstatt -t bei tar verwenden) sollten Sie zunächst das Programm in Augenschein nehmen, ob es Ihren Ansprüchen gerecht wird. Ist es erst einmal im Dateisystem installiert, ist das Entfernen aller zum Paket gehörigen Dateien doch vielfach recht aufwändig.

Pakete mit enthaltenen relativen Pfadangaben beinhalten häufig ein Skript zur Installation desselben, typische Namen lauten »install« oder »install.sh«. Trotz solcher Installationshilfen bleibt das Entfernen schwierig, da jegliche Protokollierung zur Installation ausblieb. Sicherer ist die Konvertierung des Paketformats nach *.rpm oder *.deb mit Hilfe des Kommandos alien und anschließender Verwendung eines »richtigen« Paketverwaltungswerkzeugs.

Bei Slackware - und ebenso in älteren Versionen nahezu aller weiteren Distributionen - finden Sie noch die Programme installpkg und removepkg, die - der Name spricht für sich - die Installation/Deinstallation vereinfachen. Beide Werkzeuge vermerken die Paketzugehörigkeit in Listen, sodass zumindest das saubere Entfernen eines Pakets gewährleistet ist. Und dennoch fehlt diesen Programmen die Mächtigkeit eines modernen Paketmanagers, um auch Abhängigkeiten zwischen verschiedenen Paketen auflösen zu können.

Ebenfalls in die Werkzeuggruppe zur Verwaltung von "tgz"-Paketen gehörend, ist pkgtool, das letztlich nur eine augenfällige Verpackung für installpkg und removepkg ist:

pkgtool von Slackware

Abbildung 1: pgktool von Slackware

Nicht zuletzt Sicherheitslücken, die diesen Programmen anhaften, führten zu ihrem heutigen Schattendasein...

Rpm-Pakete Zurück Anfang Weiter

Management von Softwarepaketen

Sowohl bei Quellkode als auch bei fertig kompilierten Programmen, die in Form von Tape Archiven verbreitet werden, entpuppt sich die Verträglichkeit mit bereits installierten Komponenten als Quelle häufigen Ärgers. Quellkodepakete schwächen das Problem ab, indem zumeist ein beiliegendes Skript zwingend abzuarbeiten ist (»configure«), sodass die Kompilierung und Installation erst gelingt, wenn gewisse Bedingungen erfüllt sind. Bei Binärpaketen offenbart sich die Lauffähigkeit meist erst nach der Installation. Hat ein solches Paket seine Dateien erst einmal in den Verzeichnissen des Systems verstreut, gleicht dessen saubere Entfernung oft einem Hürdenlauf...

Den ersten Ansatz, durch eine Verwaltung der Dateilisten an zentraler Stelle den Problemen zu entgegnen, präsentierte »Slackware« mit dem schon genannten »pkgtool«. Bei Bezug auf die Pakete einer einzigen Distribution und einer einzigen Version (der von Slackware) war die saubere (De)Installation zwar gegeben, jedoch fehlten Mechanismen zur Versionsverwaltung, die erst ein Update - und damit eine der wesentlichen Anforderungen an eine Paketverwaltung - ermöglichten. Erst »RedHat« schuf mit dem RedHat Package Manager rpm eine ausgefeilte Lösung.

Rpm selbst ist ein Programm, das mannigfaltige Aufgaben wahr nimmt, u.a.:

  • Verwaltung der Informationen zu allen installierten Paketen in Datenbanken (im Berkeley-DB-Format); diese liegen unter »/var/lib/rpm«
  • Installation, Update und Deinstallation von Paketen (diese müssen in einem für rpm verständlichen Format vorliegen)
  • Erzeugen von Rpm-Paketen
  • Abfrage der Informationen von (nicht) installierten Paketen
  • Überprüfung/Zertifizierung von Paketen

Versionsabfrage

Sämtliche Abfragen werden durch die Option -q (query) eingeleitet:

rpm -q [Optionen]

Abfragen können sich auf Einträge aus der RPM-Datenbank oder auf RPM-Pakete beziehen oder, anders ausgedrückt, auf installierte bzw. nicht installierte Pakete. Wichtig ist die Angabe der Quelle, insofern sich die Abfrage nicht auf ein installiertes Paket bezieht!

Eine Auskunft über alle in der Datenbank erfassten RPM-Pakete samt ihren Versionsnummern veranlasst die Option -a:

user@sonne> rpm -qa
aaa_base-2000.7.24-4
aaa_dir-2000.7.29-0
aaa_skel-2000.7.16-1
at-3.1.8-225
...

Eine solche Anfrage ist bei der Suche nach einem installierten Paket sinnvoll, falls der genaue Name des Pakets unbekannt ist.

Häufiger interessiert man sich für die Version einer installierten Komponente. Hierzu ist der »Query-Option« -q der Paketname nach zu stellen. Im Falle des gezielten Bezugs auf eine konkrete Version (wenn mehrere parallel installiert sind) kann der Name um Versions- und/oder Revisionsnummer ergänzt werden. Zulässige Anfragen bez. des Pakets »cpio« sind bspw:

user@sonne> rpm -q cpio            # Paketname
cpio-2.4.2-237
user@sonne> rpm -q cpio-2.4.2      # Paketname + Version
cpio-2.4.2-237
user@sonne> rpm -q cpio-2.4.2-237  # Paketname + Version + Revision
cpio-2.4.2-237

Bezieht sich eine Anfrage auf eine RPM-Datei, so ist neben deren Namen die Option -p zu verwenden:

user@sonne> rpm -q linuxfibel_basis*rpm  # -p fehlt!
Paket linuxfibel_basis-0.6-0.i386.rpm ist nicht installiert
user@sonne> rpm -qp linuxfibel_basis*rpm
linuxfibel_basis-0.6-0

Beachten Sie, dass sich der Name einer Rpm-Datei nicht zwingend mit dem Namen des Pakets decken muss!

Detailliertere Auskunft

Mit der reinen Versionsabfrage sind die Möglichkeiten noch lange nicht erschöpft. Aus Anwendersicht sind zunächst die Zugehörigkeiten von Dateien zu Paketen von Interesse. So setzt bspw. die Suche nach der Dokumentation zu einem Paket günstiger Weise bei einem Blick auf die Dateiliste an:

user@sonne> rpm -qld ext2fs
/usr/share/doc/packages/ext2fs/RELEASE-NOTES
/usr/share/info/libext2fs.info.gz
/usr/share/man/man1/chattr.1.gz
/usr/share/man/man1/lsattr.1.gz
/usr/share/man/man1/uuidgen.1.gz
/usr/share/man/man8/badblocks.8.gz
/usr/share/man/man8/debugfs.8.gz
/usr/share/man/man8/dumpe2fs.8.gz
/usr/share/man/man8/e2fsck.8.gz
/usr/share/man/man8/e2label.8.gz
/usr/share/man/man8/fsck.8.gz
/usr/share/man/man8/mke2fs.8.gz
/usr/share/man/man8/mklost+found.8.gz
/usr/share/man/man8/tune2fs.8.gz

Die Option -l zeigt alle zu einem Paket gehörigen Dateien mit vollständigem Pfad an; -d beschränkt die Ausgabe auf die zugehörigen Dokumentationsdateien.

Obiges Vorgehen bedingt allerdings die Kenntnis des Paketnamens, zu dem eine Datei gehört. Auch für diese Anfrage sieht rpm eine Option vor (-f), wobei die komplette Pfadangabe zur Datei erforderlich ist:

user@sonne> rpm -qf /usr/bin/chattr
ext2fs-1.18-125

Der Vollständigkeit halber soll an dieser Stelle die Abfrage einer »SPEC«-Datei Erwähnung finden. Diese enthalten letztlich die Informationen zum Erzeugen eines Rpm-Pakets (ihr Aufbau wird uns im Rahmen des Paketbaus beschäftigen). Eine Anfrage liefert den Namen des Pakets, das durch diese SPEC-Datei erstellt werden würde:

user@sonne> rpm -q --specfile linuxfibel_basis.spec
linuxfibel_basis-0.6-0

Tiefer in den Gründen des Pakets forscht die Option -i, die neben den bereits genannten Informationen Weiteres zum Inhalt offenbart:

user@sonne> rpm -qi ext2fs
Name        : ext2fs                       Relocations: (not relocateable)
Version     : 1.18                              Vendor: SuSE GmbH, Nuernberg, Germany
Release     : 125                           Build Date: Sam 29 Jul 2000 16:33:15 CEST
Install date: Die 27 Mär 2001 19:47:57 CEST      Build Host: Euklid.suse.de
Group       : System Environment/Base       Source RPM: ext2fs-1.18-125.src.rpm
Size        : 519223                           License: Remy Card, Theodore Ts'o
Packager    : feedback@suse.de
Summary     : Utilities for the second extended file system
Description :
Utilities needed to create and maintain ext2 filesystems under Linux.
Included in this package are: chattr, lsattr, mke2fs, mklost+found,
tune2fs, e2fsck, and badblocks.

Authors:
--------
    Remy Card <card@masi.ibp.fr>
    Theodore Ts'o <tytso@mit.edu>

SuSE series: a

Um den Status der zu einem Paket gehörigen Dateien zu erfahren, bedienen Sie sich der Option -s:

user@sonne> rpm -qfs /sbin/fsck
normal        /lib/libcom_err.so.2
normal        /lib/libcom_err.so.2.0
normal        /lib/libe2p.so.2
normal        /lib/libe2p.so.2.3
...

Die Angaben entsprechen der Information in der Datenbank und decken sich nicht zwangsläufig mit den Gegebenheiten im Dateisystem. So besagt »normal«, dass - aus Sicht der Datenbank - die Datei so vorhanden ist, wie sie mit dem Paket installiert wurde. Ein »not installed« wird sichtbar, wenn ein Paket nur teilweise installiert wurde (gewisse »exclude«-Optionen); »replaced« erscheint bei Dateien, die durch eine Version aus einem Fremdpaket ersetzt wurden.

Abfrage von Abhängigkeiten

Es macht i.A. selten Sinn, Programme zu installieren, während von diesen benötigte Bibliotheken, Programme oder Interpreter fehlen. Sie würden ja doch nicht laufen. Das Rpm-Paket beinhaltet daher die Informationen, welche Pakete in welcher Version zuvor installiert sein müssen.

Rpm wertet u.a. während der Installation und Deinstallation die Bedingungen für ein Paket intern aus, sodass sich die manuelle Abfrage oft erübrigt. In manchen Situationen finden sich allerdings Abhängigkeiten, die sich nicht automatisch auflösen lassen, bspw. wenn Paket A Fähigkeiten eines Pakets B benötigt, Paket B bedingt aber die vorherige Installation von Paket A. Natürlich vermag man solche Pakete auch unter Missachtung der Abhängigkeiten installieren. Günstig ist dennoch, durch vorherige Abfrage sicher zu stellen, dass im Nachhinein eine konsistente Installation vorliegt.

user@sonne> rpm -q --requires rsync
/bin/sh
/usr/bin/perl
ld-linux.so.2
libc.so.6
libc.so.6(GLIBC_2.0)
libc.so.6(GLIBC_2.1)

Wenn ein Paket bestimmte Anforderungen stellt, so sollte auch ein Paket existieren, das sie erfüllt. Der logische Schritt zur Verifizierung, ob sich ein Paket zur Installation eignet, ist somit der Test, ob die notwendige Software auf dem System existiert. Bei einer sauber geführten locatedb sollte auch ein Aufruf von »locate <Datei>« die Lösung offenbaren. Sicher ist jedoch die Anfrage an die Rpm-Datenbank:

user@sonne> rpm -q --whatprovides /bin/sh
bash-2.04-30
user@sonne> rpm -q --whatprovides /usr/bin/perl
perl-5.005_03-182

Umgangssprachlich ließe sich letztere Abfrage als »Welches Paket bietet diese Fähigkeit?« interpretieren. Die Frage nach »Welche Fähigkeiten bietet dieses Paket an?«, würde eine --provide-Anfrage beantworten:

user@sonne> rpm -q --provides rsync
rsync

Und letztlich ist auch die Fragestellung »Welche Pakete benötigen diese Fähigkeit?« legitim, bspw. um festzustellen, ob ein Paket gefahrlos gelöscht werden kann:

user@sonne> rpm -q --whatrequires /usr/bin/perl
aaa_base-2000.7.24-4
groff-1.16-26
lilo-21-132
ps-2000.7.16-4
rpm-3.0.4-68
texinfo-4.0-112
util-2.10m-41
...

Natürlich beschränken sich die Abhängigkeiten nicht allein auf den Paketnamen, sondern sie berücksichtigen ebenso Versionen, denn neuere Software bedingt zumeist auch neuere Bibliotheken...

Alles in Ordnung?

Wer die durch eine Distribution vorgegebenen Pfade zur Administration verlässt, der wird hier und da durch manuelle Eingriffe ins System nicht zuletzt die Belange der Rpm-Paketverwaltung berühren. Einfachstes Beispiel hierfür ist die Aktualisierung eines Pakets, das wiederum nur als Quellkode vorlag und somit unter Umgehung von rpm ins System gelangte. Oder aber bewusst »gelockerte« Rechte, um minder-privilegierten Benutzern das Ausführen mancher Programme zu ermöglichen.

»Das Genie beherrscht das Chaos«. Doch da die Wenigsten aus unserer Mitte den Status eines Genies genießen und Dokumentation »per Voreinstellung« stiefmütterlich vernachlässigt wird, wird der Überblick über all die Änderungen bald verloren gehen. Kein Problem... bis zum Auftreten eines Problems. Dann ist es an der Suche, welche Änderung das Übel verbockt haben könnte.

Damit derartige Integritätstests möglich werden, enthalten Rpm-Pakete mehrere Informationen. Eine Anfrage mit der Option --dump ist zwar eher zum internen Gebrauch von rpm gedacht, schult aber den Sinn für die nachfolgend vorgestellten Tests:

user@sonne> rpm -q --dump ext2fs | head
/lib/libcom_err.so.2 17 964881186 0120777 root root 0 0 0 libcom_err.so.2.0
/lib/libcom_err.so.2.0 8133 964881186 c682f94b908a606d053430e2a8078669 0100755 root root 0 0 0 X
/lib/libe2p.so.2 13 964881187 0120777 root root 0 0 0 libe2p.so.2.3
/lib/libe2p.so.2.3 17466 964881187 709a01c3824feccf4b3b35090211902f 0100755 root root 0 0 0 X
/lib/libext2fs.so.2 16 964881187 0120777 root root 0 0 12296 libext2fs.so.2.4
/lib/libext2fs.so.2.4 83724 964881187 3ea426260c3f6c02b629bec7ba636ae6 0100755 root root 0 0 41476 X
/lib/libss.so.2 12 964881186 0120777 root root 0 0 0 libss.so.2.0
/lib/libss.so.2.0 22186 964881186 203ab396745ad6160e318c024734da3d 0100755 root root 0 0 0 X
/lib/libuuid.so.1 14 964881187 0120777 root root 0 0 0 libuuid.so.1.2
/lib/libuuid.so.1.2 11889 964881187 2447a19ed4091e1a22541f1fa2d2e8cb 0100755 root root 0 0 3405 X

Von links nach rechts bedeuten die einzelnen Felder:

  • Dateiname inklusive Pfad
  • Dateigröße in Bytes
  • Zeit der letzten Modifikation
  • MD5-Prüfsumme (fehlt bei symbolischen Links)
  • Zugriffsrechte und Dateityp
  • Eigentümer
  • Besitzende Gruppe
  • Handelt es sich um eine Konfigurationsdatei? 1 - ja; 0 - nein
  • Handelt es sich um eine Dokumentationsdatei? 1 - ja; 0 - nein
  • Ist die Datei im Speicher verschiebbar (relocatable)? 1 - ja; 0 - nein
  • Bei einem symbolischen Link der Name der Zieldatei; »X« sonst

Aus Sicht des Administrators sind einzig die Änderungen relevant, die sich zwischen den Informationen in der Datenbank und der tatsächlichen Installation ergeben und hierfür bietet sich die Option -V an. Alle Dateien zu einem angegebenen Paket (bzw. zu allen installierten Paketen bei Kombination mit -a) werden einer Reihe von Tests unterzogen, wobei nur Dateien in der Ausgabe erscheinen, die mindestens einen der Tests nicht bestanden haben.

user@sonne> rpm -V xf86
......G.    /usr/X11R6/bin
S.5....T    /usr/X11R6/bin/SuperProbe
......GT    /usr/X11R6/bin/Xmark
S.5...GT    /usr/X11R6/bin/appres
S.5...GT    /usr/X11R6/bin/atobm
...

Der Punkt steht hierbei für einen bestandenen Test, die Buchstaben - bzw. die eine Ziffer - kodiert den Grund für ein Scheitern:

S Abweichende Dateigröße
M Die Rechte oder der Dateityp wurden verändert
5 Die MD5-Prüfsumme stimmt nicht
D Eine Gerätedatei hat sich verändert
L Ein symbolischer Link zeigt auf eine andere Datei
U Der Eigentümer hat sich geändert
G Die besitzende Gruppe hat sich geändert
T Datei besitzt andere Modifikationszeit

Konfigurationsdateien sind zusätzlich durch ein c vor dem Dateinamen gekennzeichnet, sodass schnell entschieden werden kann, ob eine Abweichung von den Informationen aus der Datenbank gewollt ist. Vermisste Dateien enthalten anstatt der Testdaten ein »missing«.

Rpm-Dateien, die aus unbekannter Quelle stammen, sollten im Zweifelsfall auf ihre Unversehrheit hin untersucht werden. Ein checksig stellt durch Überprüfung der MD5-Prüfsumme die Originalität eines Pakets sicher:

user@sonne> rpm --checksig linuxfibel_basis-0.6.i386.rpm
linuxfibel_basis-0.6-0.i386.rpm: md5 OK

Reparatur installierter Pakete

»Wo gearbeitet wird, fallen auch Späne.«, erklärt ein bekanntes Sprichwort. Und wer hin und wieder am System manipuliert, wird auch Verluste verzeichnen. Irgend wann geht irgend etwas mit Gewissheit schief. Wenn dann gar nichts mehr geht, beginnt man besser von vorn, also mit der Neuinstallation der »verbastelten« Pakete. Eine der Installationsoptionen (siehe nachfolgenden Abschnitt) in Verbindung mit --replacepkgs erweisen sich als schlagkräftiges Argument:

root@sonne> rpm -U --replacepkgs linuxfibel_basis-0.5.i386.rpm

Für den verbreiteten Fall, dass ein sorgloser Umgang mit den Dateirechten die Integrität des Systems in Frage stellt, lassen sich auch diese auf den »Originalzustand« zurück versetzen. Verwenden Sie hierzu die Option --setperms, um die Rechte eines Pakets zu restaurieren:

root@sonne> rpm --setperms linuxfibel_basis-0.5.i386.rpm

Um Eigentümer und Gruppenzugehörigkeit wieder herzustellen, ist --setugids die rechte Option.

Quasi den »Supergau« bedeutet eine beschädigte RPM-Datenbank. Da sich nahezu die gesamte Abfragefunktionalität von rpm auf diese bezieht, ist ohne Datenbasis die gesamte Verwaltung nutzlos. rpm ist daher selbst in der Lage, eine bestehende Datenbank neu zu befüllen bzw. eine neue zu erzeugen:

# Erzeugen einer neuen Datenbank
root@sonne> rpm --initdb
# Aktualisieren einer bestehenden Datenbank
root@sonne> rpm --rebuilddb

In beiden Fällen kann per --dbpath ein zu /var/lib/rpm abweichende Pfad zur Datenbank angegeben werden.

Installation von RPM-Paketen

rpm -i [Optionen] <Paket>
rpm -U [Optionen] <Paket>
rpm -F [Optionen] <Paket>

Die Installation eines Pakets umfasst dessen Erstinstallation als auch die Aktualisierung bereits existierender Pakete. In letzterem Fall wird das alte Paket zuvor implizit aus dem System entfernt.

Die Optionen zum Installieren sind -i und zur Aktualisierung -U (update). Da letztere Option im Falle, dass das Paket noch gar nicht installiert ist, wie -i arbeitet, sollte ihr Einsatz bevorzugt werden.

Bei der erstmaligen Installation ist die Wirkung beider Aufrufe identisch:

root@sonne> rpm -i linuxfibel_basis-0.6.i386.rpm
# oder...
root@sonne> rpm -U linuxfibel_basis-0.6.i386.rpm

Existiert das Paket hingegen bereits, scheitert -i:

# linuxfibel_basis wurde bereits installiert...
root@sonne> rpm -i linuxfibel_basis-0.6.i386.rpm
Fehler...

Bez. der Aktualisierung von Paketen besteht jedoch oft der Wunsch, einzig bereits installierte Pakete zu ersetzen, ohne dieses ggf. zu installieren. In einem solchen Fall muss die Option -F (freshen) anstatt -U verwendet werden.

Werkzeuge, die die Paketverwaltung in grafische Masken oder Skripte packen, präsentieren den Installationsvorgang häufig durch einen fort schreitenden Balken. Rpm trägt dafür selbst Sorge, wenn eine der Installationsoptionen (-i|-U|-F) mit -h (hash) kombiniert wird. Der Zusatz von -v schreibt den Paketnamen vor den Balken:

root@sonne> rpm -Uhv linuxfibel_basis-0.6.i386.rpm
linuxfibel_basis #######################################

Ohne eine Installation zu vollziehen, arbeitet die Option --test. Sie kann zur Kontrolle heran gezogen werden, um von vorn herein Konflikte auszuschließen.

Mitunter erweist sich erst in der Praxis, ob eine neuere Version einer Software das hält, was man sich von ihr verspricht. Dass sie mitunter mehr schadet als nutzt, ist leider nur allzu oft zu verzeichnen. Der Wunsch, auf die alte Version zurückzustellen, wird erwachsen. Es sollte einleuchten, dass -U oder -F eine »Aktualisierung« auf eine ältere Version abweisen und der Weg über eine vorherige Deinstallation ist dann doch unnötig weit. Einfacher ist der Verwendung der Option --oldpackage:

root@sonne> rpm -U --oldpackage linuxfibel_basis-0.5.i386.rpm

Im Zusammenhang mit der Abfrage von Paketabhängigkeiten wurde die Problematik wechselseitiger Bedingungen von Paketen bereits erörtert. Es findet sich mitunter keine Installationsreihenfolge bei mehreren voneinander abhängigen Paketen, sodass obige Mechanismen jeden Versuch einer Installation oder Aktualisierung abweisen. Per --nodeps lässt sich die Überprüfung durch rpm deaktivieren.

Eine weitere explizite Aufforderung an rpm wird nötig, wenn ein Paket Dateien eines anderen Pakets entfernt, dessen Dateien in der RPM-Datenbank erfasst sind. --replacefiles zwingt rpm dieselben durch die »neue« Version zu ersetzen.

Die »brutalste« Option, die --replacefiles, --replacepkg und --oldpackage in sich vereint, ist --force. Kombinieren Sie diese gar mit --nodeps, verdammen Sie rpm zum unbedingten Gehorsam...

RPM-Pakete speichern neben den eigentlichen Dateien auch den Installationspfad, unter dem diese ins System zu spielen sind. Nicht zuletzt diese Festlegung erschwert zuweilen die Portierung eines für Distribution A erzeugten Rpm-Pakets auf die Distribution B, da diese womöglich eine abweichende Verzeichnisstruktur bevorzugt. Zumindest Pakete mit rein distributionsunabhängigen Inhalten (bspw. Dokumentationen) bedingen aber keinerlei Annahmen ob des eigentlichen Installationsorts, sodass deren Installationspfad (oft) als »änderbar« (engl.: relocatable) vermerkt ist.

»Relocatable« Pakete lassen sich sowohl mit --prefix <Pfad> unterhalb eines Verzeichnisses installieren, wobei der komplette Installationspfad dann in diesem erscheint, als auch per --relocate <Alter_Pfad>=<Neuer_Pfad> komplett überschreiben.

Um beim Beispiel des Linuxfibel-Basispakets zu bleiben, könnte ein alternativer Installationspfad wie folgt versucht werden:

root@sonne> rpm -U --relocate /usr/share/doc/linuxfibel=/usr/local/httpd/htdocs linuxfibel_basis-0.6-0.i386.rpm
path /usr/src/linuxfibel is not relocateable for package linuxfibel_basis-0.6-0

Der Versuch scheitert leider, da das Paket nicht als »relocatable« gekennzeichnet wurde. Aber auch hierfür existiert eine Lösung in Form der Option --badreloc, die den Pfadwechsel erzwingt:

root@sonne> rpm -U --badreloc --relocate /usr/share/doc/linuxfibel=/usr/local/httpd/htdocs linuxfibel_basis-0.6-0.i386.rpm

Entfernen von RPM-Paketen

Zum Entfernen von Paketen dient die Option -e:

root@sonne> rpm -e linuxfibel_basis
Fehler: Das Entfernen dieser Pakete würde Paket-Abhängigkeiten missachten:
        linuxfibel_basis wird von linuxfibel_kap10-0.6-0 gebraucht
        linuxfibel_basis wird von linuxfibel_kap1-0.6-0 gebraucht
...

Das Beispiel verdeutlicht, dass rpm auch hierbei die Abhängigkeiten zu weiteren installierten Paketen berücksichtigt. Die Deinstallation glückt erst, wenn kein anderes Paket mehr das zu entfernende bedingt. Natürlich kann hier ebenso mit --nodeps nachgeholfen werden, was allerdings zu unbrauchbaren Paketen führen kann.

Ist ein Paket gleichzeitig in mehreren Versionen installiert, entfernt -e in Kombination mit --allmatches alle Vorkommen; einen sehr ausführlichen Report zu den einzelnen Abhängigkeiten bringen die Optionen -e --test --vv zum Vorschein.

Rpm-Pakete selbstgebaut Zurück Anfang Weiter

Voraussetzungen

Die meisten Rpm-Pakete beinhalten Programme nebst zugehöriger Bibliotheken, Dokumentationen, Konfigurationsdateien usw. Beim Erzeuger eines Pakets handelt es sich i.A. auch um den Entwickler des jeweiligen Programms, deshalb werden Rpm-Pakete zumeist direkt aus den Quellen gebaut, d.h. die zu verpackenden Dateien werden erst zum Zeitpunkt des Paketbaus erzeugt.

Somit ergibt sich als erste Anforderung, die zum Erstellen eines Pakets erfüllt sein muss, dass die Quellen auf dem System korrekt kompilieren müssen (für das nachfolgend diskutierte Beispiel der Linuxfibel erübrigt sich diese Forderung, da sie keine Kompilierung bedingt).

Die Arbeitsweise des Kommandos rpm wird durch eine oder mehrerer Konfigurationsdateien gesteuert. Beim Start durchsucht Rpm die Dateien »/usr/lib/rpmrc«, »/etc/rpmrc« und »~/.rpmrc« in benannter Reihenfolge und setzt alle Optionen anhand der zuletzt vorgefundenen Definition. Da die Datei »~/.rpmrc« aus dem Heimatverzeichnis eines Benutzers stets zuletzt gelesen wird, lassen sich selbst nutzerspezifische Anpassungen vornehmen. Aber vermutlich werden Sie nie in Verlegenheit gelangen, die Voreinstellungen korrigieren zu müssen... Existiert keine der obigen Dateien (auch nicht in anderen Verzeichnissen), so könnte es an einer unvollständigen Installation liegen. Zumindest in neueren RedHat-basierten Distributionen ist die Funktionalität zum Bau oft in ein »rpm-devel-Paket« ausgelagert.

Schon eher Ziel eines Eingriffs dürfte die Forderung nach der Existenz einer Reihe von Verzeichnissen sein, die u.a. als Ablage für die Quellen, für die Bauanleitungen und für die fertigen RPM-Pakete dienen. Bspw. finden Sie in einem SuSE-System (ab 7.0) die folgenden Verzeichnisse vor:

/usr/src/packages/BUILD

In diesem Verzeichnis werden die Dateien vor dem Paketbau erzeugt

/usr/src/packages/RPMS

Enthält die fertigen RPM-Pakete

/usr/src/packages/SOURCES

Enthält die Quellen und Patches

/usr/src/packages/SPECS

Enthält die SPEC-Dateien zu jedem Paket (quasi die Anleitung, wie ein Paket zu erzeugen ist).

/usr/src/packages/SRPMS

Enthält die fertigen SRPM-Pakete (Quellen)

Um Pakete in der vordefinierten Umgebung zu erzeugen, sind Rootrechte erforderlich, was natürlich auf etlichen Entwicklersystemen aus Sicherheitsgründen nicht erwünscht ist. Einem Benutzer steht es deshalb frei, die vom Kommando rpm verwendete Struktur anzupassen, indem er die zu ändernden Makros in einer Datei »~/.rpmmacros« definiert. Die Syntax orientiert sich an der aus der »globalen« Makro-Datei »/usr/lib/rpm/macros«:

# Auszug aus /usr/lib/rpm/macros
...
%_usr /usr
%_usrsrc %{_usr}/src
...
%_topdir %{_usrsrc}/packages
...
%_rpmdir %{_topdir}/RPMS
%_sourcedir %{_topdir}/SOURCES
%_specdir %{_topdir}/SPECS
%_srcrpmdir %{_topdir}/SRPMS

Wie im Auszug der Datei unschwer zu erkennen ist, würde das Überschreiben der Variablen »_topdir« genügen, um den Paketbau in einem alternativen Verzeichnisbaum zu vollziehen (diese Verzeichnisse müssten Sie natürlich noch anlegen):

user@sonne> vi ~/.rpmmacros
%_topdir /home/user/myrpms

user@sonne> mkdir -p ~/myrpms/{RPMS,SOURCES,SPECS,SRPMS}

Um auf Ihrem System die Makros mit den Verzeichnisdefinitionen in Erfahrung zu bringen, müssen Sie nicht erst die Konfigurationsdateien durchforsten. Bemühen Sie die Option »--showrc« von Rpm und suchen in der Ausgabe nach den Variablen »_rpmdir«, »_sourcedir«, »_specdir« und »_srcrpmdir«.

user@sonne> rpm --showrc | egrep '_rpmdir|_sourcedir|_specdir|_srcrpmdir'
RPM_SOURCE_DIR="%{u2p:%{_sourcedir}}"
RPM_SOURCE_DIR="%{_sourcedir}"
-14: _rpmdir    %{_topdir}/RPMS
-14: _sourcedir %{_topdir}/SOURCES
-14: _specdir   %{_topdir}/SPECS
-14: _srcrpmdir %{_topdir}/SRPMS

Für die nachfolgende Ausführung gehen wir von oben skizzierter Verzeichnisstruktur eines SuSE-Linux aus.

Der Bauplan

Die entscheidende Datei für den Paketbau ist die so genannte SPEC-Datei, die allgemeine Informationen und eine Art Bauanleitung zum Paket beinhaltet. Die Datei selbst besteht aus mehreren Sektionen:

Die Präambel

Die Präambel enthält allgemeine Informationen zum RPM-Paket und zum Bau. Jeder Eintrag besitzt die Form:

<Name> : <Beschreibung>

wobei folgende Namen (»Tags«) möglich sind:

Buildroot

(Alternatives) Verzeichnis, indem das Paket gebildet wird. Wird auf die Angabe verzichtet, so werden die Dateien an ihren endgültigen Positionen im Dateisystem erzeugt. Zumindest für Software im Teststadium ist dieses Verhalten denkbar ungeeignet. Bei gesetztem »Buildroot« wird die benötigte Verzeichnisstruktur unterhalb des angegebenem Verzeichnisses erzeugt (bezogen auf das Paket der Linuxfibel würde während des Paketbaus bspw. ein Verzeichnis »/tmp/usr/share/doc/linuxfibel« angelegt werden, falls »Buildroot« auf »/tmp« stünde).

Copyright

Copyright-Informationen zur enthaltenen Software.

Conflicts

Namen von Paketen, die sich mit diesem RPM-Paket nicht vertragen. Die Installation wird bei einem Konflikt abgelehnt, kann aber dennoch mittels --nodeps erzwungen werden.

Distribution

Linuxdistribution, für die das Paket erzeugt wurde.

Group

Art der enthaltenen Software (mögliche Einträge werden von RedHat empfohlen und liegen in einer ASCII-Datei »GROUPS«der Dokumentation zu Rpm bei)

Name

Name des RPM-Pakets

Packager

Name des Erzeugers des Pakets

Patch

Name einer Patchdatei, die zum Bau des Pakets erforderlich ist; mehrere Patches können mittels mehrerer Patch-Einträge angegeben werden.

Prefix

»Empfohlener« Installationspfad für ein verschiebbares Paket

Provides

Ein virtueller Paketname, der eine Einordnung in eine Anwendungsgruppe ermöglichen soll. Somit kann der Anwender in manchen Installationsprogrammen aus einer Reihe von Programmen auswählen, die nahezu dasselbe tun (bspw. bieten sowohl »sendmail« als auch »qmail« einen »mail-server«).

Release

Versionsnummer des Pakets; "0" wenn es erstmals gepackt wird, "1" beim zweiten Mal.... D.h. Versionsnummern sollten erhöht werden, wenn das Paket neu erzeugt wird ohne das sich die Version der enthaltenen Software geändert hat.

Requires

Paketnamen und Versionen, die auf dem System vorhanden sein müssen (Paketabhängigkeiten). Hier können ebenso die Namen virtueller Paketgruppen stehen, falls kein konkretes Programm bedingt wird. Bez. Versionsnummern sind auch relative Angaben erlaubt: requires perl >= 5.0.

Source

Name des enthaltenen Quell-Archivs; mehrere Archive lassen sich mittels mehrerer Source-Tags angeben

Summary

Kurze Beschreibung zum Inhalt des Pakets

Url

URL mit weiterführenden Informationen zum Paket.

Vendor

Anbieter der Software

Version

Versionsnummer der enthaltenen Software

Der entsprechende Abschnitt aus der SPEC-Datei zum Linuxfibel-RPM-Paket sieht wie folgt aus:

Vendor: Saxonia Systems AG
Distribution: Gnu-Linux (i386)
Name: linuxfibel
Version: 0.7
Release: 3
Packager: thomas.ermer@saxsys.de
Copyright: GFDL
Summary: Installiert das Linuxfibel Paket
Url: http://www.linuxfibel.de
Group: Linux/Dokumentation
Provides: Linux Basiswissen
Source: linuxfibel-%{version}.%{release}.tgz
BuildRoot: /tmp/linuxfibel-%{version}.%{release}-root

Die %description-Sektion

Eine aussagekräftige Beschreibung zum Inhalt des Pakets ermöglicht die Sektion »description«. Der Text kann auf beliebig viele Zeilen ausgedehnt werden.

%description
Die Linuxfibel ist ein distributionsunabhängiges Lehrbuch und Referenz zu Themen rund um Linux.

Die %prep-Sektion

Hier findet die Vorbereitung zum Bau des Pakets statt. Letztlich beinhaltet die Sektion verschiedene Shellbefehle, die die Quellen für den anschließenden Kompiliervorgang - sofern ein solcher erforderlich ist - aufbereiten.

Ein gebräuchlicher erster Schritt ist das Entfernen der »Reste« früherer Paketbau-Vorgänge. Dann werden i.d.R. die Quelldateien ins BUILD-Verzeichnis entpackt. Ggf. kann in einem weiteren Schritt das Einspielen von Patches erfolgen.

%define INSTALL_DIR     /usr/share/doc/linuxfibel

%prep
rm -rf %{name}-%{version}.%{release}
mkdir %{name}-%{version}.%{release}
cd %{name}-%{version}.%{release}
mkdir -p ./%{INSTALL_DIR}
tar xzvf ../../SOURCES/%{name}-%{version}.%{release}.tgz -C ./%{INSTALL_DIR}

Die %build-Sektion

Im Falle von Quellpaketen muss die zu installierende Software erst erzeugt werden. Genügt hierfür ein einfacher Aufruf von make, kann die build-Sektion entfallen. Im anderen Fall werden die auszuführenden Schritte angegeben.

%changelog

Beinhaltet Informationen über die wesentlichen Änderungen der enthaltenen Software.

%clean

RPM entsorgt die »Überreste« der Installation stets selbst, mit der Ausnahme der Verwendung eines buildroot-Verzeichnisses. Ein solches sollte im clean-Eintrag entfernt werden.

%clean
rm -r %{buildroot}

Die %files-Sektion

Hier folgen die Dateien, die das RPM-Paket enthalten soll. Jede Datei steht auf einer eigenen Zeile und jeder Dateiname ist mit einem Zeilenumbruch abzuschließen. Alternativ lassen sich die von den Shells bekannten Wildcards verwenden. Innerhalb der files-Sektion können einzelne Dateien mit weiteren Tags versehen werden:

%attr

Für die anzugebende Datei werden bestimmte Attribute gesetzt:

%attr (644, root, root) bla.txt

Anstelle des Wertes kann ein Minus verwendet werden (-, root, root), um den aktuellen Wert beizubehalten.

%config[(missingok|noreplace)]

Die Datei wird als Konfigurationsdatei gekennzeichnet; die optionalen Tags geben an, ob eine Datei [bspw. beim Entfernen des Pakets] fehlen darf (missingok) oder ob das Ersetzen einer existierenden Datei zu verhindern ist (noreplace).

%defattr

Die angegebenen Attribute gelten für alle Dateien aus der files-Sektion (Angabe wie bei »attr«)

%dir

Das angegebene Verzeichnis wird ins Paket integriert, nicht aber die enthaltenen Dateien und Unterverzeichnisse (ohne die Angabe wird bei Verzeichnissen auch deren Inhalt zum Paket addiert)

%doc

Die Datei wird als Dokumentation ausgewiesen, womit die Installation per Rpm-Option »--excludedocs« verhindert werden kann.

%docdir

Dateien aus dem angegebenen Verzeichnis werden ins Paket integriert und als Dokumente gekennzeichnet. Datei aus /usr/[share/]doc, /usr/[share/]info und /usr/[share/]man müssen nicht explizit als Dokumentationen ausgewiesen werden.

%ghost

Die Datei selbst ist nicht im Paket enthalten, sie wird aber bei der Installation als leere Datei erzeugt.

%license

Die angegebene Datei enthält Lizenzinformationen

%readme

Die angegebene Datei enthält lesenswerte Informationen

%verify

Die angegebenen Attribute einer Datei werden bei einer Überprüfung berücksichtigt

%verify (md5, owner, group, size) bla.txt

Die Liste der einzufügenden Dateien kann auch [teilweise] in einer separaten Datei stehen. Diese wird wie folgt eingebunden:

%files -f Datei_mit_Liste

Die %install-Sektion

%post, %postun

Die Sektionen beinhalten Bashskripte oder die Namen der dem RPM-Paket beiliegenden Skriptdateien, die nach der Installation (%post) bzw. nach dem Löschen (%postun) des Pakets ausgeführt werden sollen.

%post
cd %{INSTALL_DIR}
%{INSTALL_DIR}/print_version.sh

%postun
cd %{INSTALL_DIR}
test -d images && rm -r images
rmdir %{INSTALL_DIR}

%pre, %preun

Die Sektionen beinhalten Bashskripte oder die Namen von dem RPM-Paket beiliegenden Skriptdateien, die vor der Installation (%pre) bzw. vor dem Löschen (%preun) des Pakets ausgeführt werden sollen.

%preun
cd %{INSTALL_DIR}
test -d printversion && rm -r printversion

Anmerkung: Die im Beispiel verwendeten Befehle erzeugen bzw. entfernen die Dateien zur Druckversion der Linuxfibel. Das Skript print_version.sh wird ausführlich im Abschnitt Shells, Bash-Programmierung, Komplexe Anwendungen behandelt.

Der eigentliche Bau

Erzeugt wird ein RPM-Paket mit Hilfe des Kommandos rpm selbst:

rpm -bStadium [Optionen] SPEC-Datei [SPEC-Datei]

Die mit Stadium bezeichneten Optionen steuern den Umfang des Paketbaus:

p Ausführen der %prep-Sektion
c Ausführen der %prep- und %build-Sektion
i Ausführen der %prep-, %build- und %install-Sektion
b Ausführen der %prep-, %build- und %install-Sektion; Erzeugen des Binary-Pakets
a Wie b; zusätzlich wird ein Quell-RPM-Paket gebaut
l Test der %files-Liste (Existenz der Dateien, Überprüfung benötigter und bereit gestellter Bibliotheken)

Im Zusammenhang mit c oder i können führende Schritte übersprungen werden, indem mit der Option --short-circuit die Startphase des Baus spezifiziert wird. Sinnvoll ist diese Option beim Aufspüren von Fehlern, indem diese nicht an den Orginaldateien des Pakets gefixt werden, sondern die Suche in der Kopie des BUILD-Verzeichnisses erfolgt. Nun sollte natürlich die %prep-Sektion zum folgenden Testlauf nicht erneut ausgeführt werden, da sonst die originalen Quellen den Inhalt des BUILD-Verzeichnisses überschreiben würden...

Als weitere Optionen stehen zur Verfügung:

--timecheck Sekunde

Gibt eine Warnung aus, wenn der Zeitstempel einer zu packenden Datei älter als die angegebenen Sekunden ist; somit lassen sich leicht versehentliche Einträge der Dateiliste aufspüren (das Zeitintervall sollte größer sein, als der Paketbau dauert)

--clean

Entfernt das BUILD-Verzeichnis nach erfolgtem Bau

--rmsource

Entfernt die Quellen und die SPEC-Datei nach erfolgtem Bau

--test

Simuliert den Bau; anhand der Ausgaben können vorab Fehler erkannt werden

--sign

Fügt eine PGP-Signatur ins Paket ein

--target Plattform

Spezifiziert die Zielplattform, für die das Paket erzeugt werden soll

--buildroot Verzeichnis

Überschreibt den gleichnamigen Eintrag der SPEC-Datei

Beispielhaft sollen die Schritte aufgezeigt werden, die Rpm beim Paketbau mit der Option -ba durchläuft:

  • Lesen und Parsen der Spec-Datei
  • Bearbeitung der %prep-Sektion zum Entpacken der Quellen ins Build-Verzeichnis und ggf. Einspielen der Patches
  • Bearbeitung der %build-Sektion zum Kompilieren der Quellen
  • Bearbeitung der %install-Sektion zur Installation der übersetzen Quellen
  • Lesen der %files-Sektion, Suche aller aufgeführten Dateien und Verzeichnisse und Bau der RPM- und SRPM-Pakete
  • Bearbeitung der %clean-Sektion

Exemplarisch folgt der Aufruf, mit dem wir die Linuxfibel-RPM-Pakete erstellen:

root@sonne> cd /usr/src/packages/SPECS; ls
Linuxfibel.spec
root@sonne> rpm -bb --target noarch Linuxfibel.spec
Erzeuge Zielplattformen: noarch
Paket wird erzeugt für Zielplattform noarch.
Ausführung(%prep): /bin/sh -e /var/tmp/rpm-tmp.38531
+ umask 022
+ cd /usr/src/packages/BUILD
+ rm -rf linuxfibel-0.8.3
+ mkdir linuxfibel-0.8.3
+ cd linuxfibel-0.8.3
+ mkdir -p /usr/share/doc/linuxfibel
+ tar xzvf /usr/src/packages/SOURCES/linuxfibel-0.8.3.tar.gz -C /usr/share/doc/linuxfibel
access.htm
allekapitel.htm
...
xsteuerung.htm
+ exit 0
Verarbeitung der Dateien: linuxfibel-0.8-3
Suche Provides: (benutze /usr/lib/rpm/find-provides)...
Suche Requires: (benutze /usr/lib/rpm/find-requires)...
Provides: Linux Basiswissen
PreReq: /bin/sh
Geschrieben: /usr/src/packages/RPMS/noarch/linuxfibel-0.8-3.noarch.rpm
Ausführung(%clean): /bin/sh -e /var/tmp/rpm-tmp.85181
+ umask 022
+ cd /usr/src/packages/BUILD
+ rm -rf /usr/share/doc/linuxfibel
+ exit 0

Bei Verzicht auf Angabe der Zielplattform wird diese Information vom C-Compiler (gcc) übernommen (es würde also stets ein Architektur abhängiges Paket erzeugt werden).

Patch-Rpm Zurück Anfang Weiter

Hintergrund

Während sich bei der Aktualisierung von Quellcode-Paketen das Patchformat bereits seit langem etabliert hat, werden Updates von RPM-Paketen noch immer als vollständige Pakete bereit gestellt. Leider haben RPM-Pakete, da sie oft Programme und Bibliotheken umfassen, die Eigenschaft, recht umfangreich zu sein. Und insbesondere der Anwender, dessen Rechner nur mit einer lahmen Modemanbindung am Internet teilnimmt, stöhnt bei notwendigen Aktualisierungen.

Die SuSE AG hat eine Erweiterung des RPM-Formats vorgeschlagen, um das Patchen derartiger Pakete zu ermöglichen. Dass es funktioniert, beweisen die zahlreichen Patches, die SuSE zu ihren RPM-Paketen bereits anbietet. Bleibt zu hoffen, dass die Erweiterungen bald zum allgemeinen Funktionsumfang von RPM zählen.

Wir demonstrieren nachfolgend die Verwendung und Erzeugung solcher Patches. Denken Sie immer daran, dass dies nur mit einem angepassten RPM-Kommando funktioniert. Konsultieren Sie das Manual zum RPM, ob dieses u.a. die Option basedon kennt.

user@sonne> man rpm
...
--basedon
      Show what packages a patch-rpm is based on. A
      patch-rpm can only be installed if one of the pack
      ages it is based on is installed.
...

Einspielen eines RPM-Patches

Wie auch bei Quelltextpatches kann nicht jeder RPM-Patch auf jedes Paketversion angewandt werden. Für den Fall, dass Sie es dennoch versuchen, würde rpm die Installation abweisen.

Mit der eingangs erwähnten Option --basedon können Sie vorab erkennen, ob der Patch für die installierte Version »passend« ist:

user@sonne> rpm -qp --basedon tk-8.4-64.i586.patch.rpm
tk = 8.4-48
tk = 8.4-51
tk = 8.4-59

Der Patch im Beispiel kann auf installierte tk-Rpm-Pakete der Versionen 8.4-48, 8.4-51 oder 8.4-5 angewandt werden. Wenden Sie übrigens die Option --basedon auf ein »normales« Rpm-Paket an, wird nichts ausgegeben (Was sollte auch ausgegeben werden?).

Das Einspielen des Rpm-Patches geschieht analog zur Installation eines »normalen« Rpm-Pakets mittels der Option -i:

root@sonne> rpm -i tk-8.4-64.i586.patch.rpm

Ob ein Rpm-Paket gepatcht ist, erfahren Sie mit Hilfe der Option -P bzw. --patches, die alle ersetzten Dateien auflistet:

root@sonne> rpm -i tk-8.4-64.i586.patch.rpm
user@sonne> rpm -ql --patches tk-8.4-48.i586.rpm
root@sonne> rpm -i tk-8.4-64.i586.patch.rpm
user@sonne> rpm -ql --patches tk-8.4-48.i586.rpm
/usr/lib/libtk8.4.so

Erzeugen eines RPM-Patches

user@sonne> cat linuxfibel.patch.spec
#
# spec file for linuxfibel.rpm
#


Vendor:         Saxonia Systems AG
Distribution:   Gnu Linux
Name:           linuxfibel
Version:        __NEW_VERSION__
Release:        __NEW_RELEASE__
Packager:       linuxfibel@gmx.de

Copyright:      GPL
Summary:        Installiert das Linuxfibel Paket
Group:          Linux/Dokumentation
Provides:       linuxfibel

Source:         linuxfibel-%{version}-%{release}.tgz
BuildRoot:      /tmp/linuxfibel-%{version}.%{release}-root
Patches:        Linuxfibel = __INSTALLED_VERSION__

%description
Die Linuxfibel ist ein distributionsunabhängiges Lehrbuch und Referenz zu Themen rund um Linux.
Distribution: Gnu-Linux (i386)

#####################################################################
%define INSTALL_DIR     /usr/share/doc/linuxfibel
#####################################################################
%prep
rm -rf %{name}-%{version}.%{release}
mkdir %{name}-%{version}.%{release}
cd %{name}-%{version}.%{release}
mkdir -p ./%{INSTALL_DIR}
tar xzvf ../../SOURCES/%{name}-%{version}-%{release}.tgz -C ./%{INSTALL_DIR}

#####################################################################
%install
cd %{name}-%{version}.%{release}
rm -f %{buildroot}
ln -s `pwd` %{buildroot}

#####################################################################
%clean rm -r %{buildroot}

#####################################################################
%files

%dir %{INSTALL_DIR}

Deb-Pakete Zurück Anfang Weiter

Der feine Unterschied

Gäbe es keine Debian-Distribution, so könnte der RedHat Package Manager zu Recht den Anspruch eines Standard-Linux-Paketformats erheben. Und würde Debian nicht ausgerechnet bei eingefleischten Linuxkennern so hoch im Kurs stehen, so würden die häufig erklingenden Aussagen, »das Debian-Paket-Format sei dem von RedHat überlegen«, als Pauschalmeinung von Unwissenden ungehört in der Senke des Vergessens verhallen. Doch was unterscheidet dieses Format so von Rpm, dass die Debianer vortrefflich ob der Vorzüge diskutieren mögen?

Als augenscheinlichster Unterschied kommt die Paketverwaltung von Debian mit einer Reihe von Programmen daher, während RedHat's RPM die Funktionalität unter einem Hut vereint. Der Zweck der wesentlichen Werkzeuge sei an dieser Stelle nur kurz vermerkt:

dpkg

Es handelt sich quasi um das »Hauptprogramm« der Debian-Paketverwaltung. Dient der (De)Installation der Pakete, zu dessen Aktualisierung, hilft bei Abfragen und findet außerdem als Frontend zu dpgk-deb Verwendung. Wir werden uns im folgenden Abschnitt verstärkt an diesem Werkzeug orientieren, auch wenn die meisten administrativen Tätigkeiten bequem mit dem Frontend dselect vorgenommen werden können.

dpkg-deb

Erzeugt und verwaltet Debian-Pakete. Obwohl das Kommando separat verwendet werden kann, wird es zumeist durch dpkg gesteuert.

dselect

Ein interaktives Frontend zu dpkg. deselect modifiziert nur die Aktionen aus der Datenbank, anhand derer dpkg entsprechende Handlungen vornimmt.

apt-get

Ein Bestandteil des (in Entwicklung befindlichen) »Advanced Package Tools (APT)«, das als zentrales Paketverwaltungswerkzeug dienen soll. Einige weitere Kommandos aus APT werden im weiteren Text kurz benannt.

Informationen zu installierten Paketen speichert RPM in einer einzigen Datenbank (»/var/lib/rpm«). Debian hingegen legt seine Verwaltungsdateien in Dateien unterhalb von »/var/lib/dpgk/« ab. In »/var/lib/dpgk/available« stehen alle auf dem System verfügbaren (nicht zwingend installierte) Pakete, »/var/lib/dpkg/status« enthält die ggf. auszuführende Aktion und den Status zu jedem verfügbaren Paket.

Der Begriff der Aktion tauchte bereits in der Kurzbeschreibung zu »dselect« auf. Die Paketverwaltung entscheidet anhand dieser Information und dem aktuellen Zustand, wie mit einem Paket zu verfahren ist:

u - Unknown

Keine Aktion

i - Install

Das Paket ist zu installieren

r - Remove

Das Paket ist zu deinstallieren; Konfigurationsdateien bleiben erhalten

p - Purge

Das Paket ist einschließlich seiner Konfigurationsdateien zu löschen

Die Unterscheidung von »Remove« und »Purge« soll verhindern, dass mühsam angepasste Konfigurationsdateien gelöscht werden, falls das Paket während einer Aktualisierung »zeitweilig« entfernt wird.

Der Status zu einem Paket gibt seinen aktuellen Zustand im System an:

config-files

Einzig die Konfigurationsdateien zu einem Paket sind installiert (bspw. per »remove« gelöscht).

half-configured, half-installed

Das Paket ist nicht vollständig konfiguriert/installiert.

installed/not-installed

Das Paket ist installiert/nicht installiert.

unpacked

Das Paket ist installiert (entpackt), aber nicht konfiguriert.

Enthält ein Paket Installationsskripte, so werden diese zum Zeitpunkt der Installation in einem Verzeichnis »/var/log/dpkg/Paketname« abgelegt.

Das Paketformat selbst ist bei RedHat's RPM eine Eigenkreation, das - vereinfacht formuliert - aus dem eigentlichen Datenpaket im *.tgz-Format und den Konfigurationsinformationen besteht. Debian richtet sein Format an dem der statischen Bibliotheken aus, indem die einzelnen Bestandteile per »ar« ins Archiv gelangen. Letztlich landet aber auch das Paket mit den eigentlichen Daten als *.tgz im Archiv. Hinzu kommen die Dateien »debian-binary« mit der Versionsnummer und »control.tar.gz«, die Installationsskripte, eine Beschreibung und die Dateiliste umfasst.

Ist die Wertung der bisherigen Differenzen rein eine Frage des persönlichen Geschmacks, so ist Möglichkeit der Verwendung von Wildcards in Anfragen des Debian-Paketsystems ein nicht zu unterschätzender Vorteil.

Allgemeine Abfragen

Ebenso wie bei RPM konsultiert Debian's Paketverwaltung die Datenbankeinträge, um Informationen zu einem Paket zu gewinnen. Da Debian auch den Status nicht-installierter - aber verfügbarer - Pakete in den Datenbanken vorhält, erfolgt auch eine diesbezügliche Recherche nur anhand der Einträge.

Die Recherche übernimmt das Kommando dpgk in Verbindung mit der Option -l bzw. in der Langform --list. Eventuell folgende Parameter werden als Paketnamen bzw. Namen mit Wildcards interpretiert. Ein Verzicht auf jegliche Angabe kommt einer Abfrage aller verfügbaren Pakete gleich:

user@sonne> dpkg -l
  Desired=Unknown/Install/Remove/Purge/Hold
  |
Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
  |/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err:uppercase=bad)
  ||/ Name          Version     Description

+++-=============-===========-====================================================
  ii  adduser       3.11.1      Add users and groups to the system.
  ii  ae            962-26      Anthony's Editor -- a tiny full-screen editor
  ii  anacron       2.1-5.1     a cron-like program that doesn't go by time
  ii  apt           0.3.19      Advanced front-end for dpkg
  ii  asclassic     1.1b-12     A light window manager with the NEXTSTEP look and feel
  ii  at            3.1.8-10    Delayed job execution and batch processing
  ii  autoclass     3.3.2-2     automatic classification or clustering
  ii  balsa         0.6.0-1.1   GNOME email client
  ii  base-config   0.32        Debian base configuration
  ii  base-files    2.2.0       Debian base system miscellaneous files
  ii  base-passwd   3.1.7       Debian Base System Password/Group Files
  ii  bash          2.03-6      The GNU Bourne Again SHell
  ii  bc            1.05a-11    The GNU bc arbitrary precision calculator language
...

Die einführende Statuszeile beschreibt kurz die einzelnen Felder der Tabelle. In der ersten Spalte steht die zuletzt erwünschte Aktion; Spalte 2 enthält den aktuellen Zustand. In der Beispielabfrage betraf bei allen angeführten Paketen die letzte Aktion deren Installation (i). Dass diese bereits abgeschlossen wurde, markiert im Beispiel das Symbol i der zweiten Spalte.

Eine belegte 3. Spalte deutet auf ein Installationsproblem hin. Eventuell benennt das Symbol den konkreten Fehler.

Die weiteren Spalten enthalten den Paketnamen, die Versionsnummer sowie eine Kurzbeschreibung zum Paket.

Die gezielte Betrachtung nur eines Pakets geschieht durch Angabe seines Namens. Sind nur Bestandteile des Namens bekannt, spielt Debian's Recherche-Vorgehen seine Stärke aus, indem die von den Shells her bekannten Wildcards die Angabe von Namensmustern gestatten. Das folgende Beispiel demonstriert eine Anfrage an alle Pakete mit zweistelligem Namen:

user@sonne> dpkg -l '??'
  Desired=Unknown/Install/Remove/Purge/Hold
  |
Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
  |/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err:uppercase=bad)
  ||/ Name          Version     Description

+++-=============-===========-====================================================
  ii  ae            962-26      Anthony's Editor -- a tiny full-screen editor
  pn  af            <none>      (no description available)
  pn  an            <none>      (no description available)
  ii  at            3.1.8-10    Delayed job execution and batch processing
  pn  bb            <none>      (no description available)
  ii  bc            1.05a-11    The GNU bc arbitrary precision calculator language
...

Beachten Sie das in Hochkommata eingeschlossene Suchmuster. So wird eine unerwünschte Interpretation durch die Shell verhindert!

Um weiter gehende Informationen zu einem Paket zu erhalten, bedarf es einer Statusabfrage per dpgk -s Paketname. Hierunter finden Sie auch die Abhängigkeiten und Konflikte, die Sie bei RPM erst durch gezielte Einzelabfragen in Erfahrung bringen:

user@sonne> dpkg -s xterm
Package: xterm
Status: install ok installed
Priority: optional
Section: x11
Installed-Size: 580
Maintainer: Branden Robinson <branden@debian.org>
Source: xfree86-1
Version: 3.3.6-10
Replaces: xbase (<< 3.3.2.3a-2)
Provides: x-terminal-emulator
Depends: libc6 (>= 2.1.2), libncurses5, xlib6g (>= 3.3.6-4)
Conflicts: xbase (<< 3.3.2.3a-2)
Conffiles: /etc/X11/Xresources/xterm
Description: X terminal emulator xterm is a terminal emulator for the X Window System. It provides DEC VT102 and Tektronix 4014 compatible terminals for programs that cannot use the window system directly. This version implements ISO/ANSI colors and most of the control sequences used by DEC VT220 terminals.

Für den Fall, dass das zu konsultierende Paket nicht installiert ist, benötigen Sie die Optionen -I bzw. --info, um an weiter führende Informationen zu gelangen:

user@sonne> dpkg --info xterm_4.2.1-3_i386.deb
new debian package, version 2.0.
size 488764 bytes: control archive= 4039 bytes.
91 bytes, 3 lines conffiles
734 bytes, 17 lines control
1052 bytes, 16 lines md5sums
7914 bytes, 229 lines * postinst #!/bin/sh
147 bytes, 5 lines * postrm #!/bin/sh
7276 bytes, 218 lines * prerm #!/bin/sh
Package: xterm
Version: 4.2.1-3
Section: x11
Priority: optional
Architecture: i386
Depends: debconf (>> 0.5), libc6 (>= 2.2.4-4), libfreetype6, libncurses5 (>= 5.2.20020112a-1), libxaw7 (>> 4.1.0), xlibs (>> 4.1.0)
Conflicts: xbase (<< 3.3.2.3a-2), suidmanager (<< 0.50)
Replaces: xbase (<< 3.3.2.3a-2)
Provides: x-terminal-emulator
Installed-Size: 900
Maintainer: Branden Robinson
Source: xfree86
Description: X terminal emulator
xterm is a terminal emulator for the X Window System. It provides DEC VT102 and Tektronix 4014 compatible terminals for programs that cannot use the window system directly. This version implements ISO/ANSI colors and most of the control sequences used by DEC VT220 terminals.

Die Liste der von einem Paket bereit gestellten Dateien erhalten Sie mittels dpgk -L Paketname:

user@sonne> dpkg -L xterm
/.
/usr
/usr/share
/usr/share/doc
/usr/share/doc/xterm
/usr/share/doc/xterm/copyright
/usr/share/doc/xterm/README.Debian
/usr/share/doc/xterm/changelog.Debian.gz
/usr/share/doc/xterm/xterm.faq.html
/usr/share/doc/xterm/termcap.gz
/usr/share/doc/xterm/ctlseqs.ms.gz

Den umgekehrten Weg, zu einer gegebenen Datei das Paket zu finden, welches diese enthält, ist mit dpgk -S Dateimuster möglich. Im Unterschied zur Abfrage der Dateiliste ist hier die Angabe von Wildcards gestattet:

user@sonne> dpkg -S 'bin/*term'
eterm: /usr/bin/Eterm
gnome-terminal: /usr/bin/gnome-terminal
rxvt: /usr/X11R6/bin/rxvt-xterm
hanterm: /usr/X11R6/bin/hanterm
kterm: /usr/X11R6/bin/kterm
util-linux: /usr/bin/setterm
xterm: /usr/X11R6/bin/xterm

Abfrage von Abhängigkeiten

Die im vorherigen Abschnitt erwähnte Option -s zeigte sowohl die Paketnamen auf, die ein konkretes Paket benötigt als auch jene, deren parallele Installation zu Konflikten führen würde. Auch die Information, was das Paket zur Verfügung stellt, entlockt dpgk -s einem Paket.

Wie aber können Sie in Erfahrung bringen, welche Pakete das installierte benötigen? Hier ist dpgk mit seinem Latein am Ende. Das aus den Advanced Package Tools (APT) stammende Kommando apt-cache showpkg Paketname kann helfen:

user@sonne> apt-cache showpkg xterm
Package: xterm
Versions:
4.2.1-3(/var/lib/dpkg/status)

Reverse Depends:
xfree86-common,xterm
x-window-system,xterm
pgi,xterm
seyon,xterm
gkdebconf,xterm
tn5250,xterm
debroster,xterm
user-mode-linux,xterm
seaview,xterm
ddd,xterm
lincvs,xterm
texdoctk,xterm
tk-brief,xterm
Dependencies:
4.2.1-3 - debconf (4 0.5) libc6 (2 2.2.4-4) libfreetype6 (0 (null)) libncurses5 (2 5.2.20020112a-1) libxaw7 (4 4.1.0) xlibs (4 4.1.0) xbase (3 3.3.2.3a-2) suidmanager (3 0.50) xbase (3 3.3.2.3a-2)
Provides:
4.2.1-3 - x-terminal-emulator
Reverse Provides:

Welche Pakete die Installation von xterm bedingen, können Sie dem Eintrag »Reverse Depends:« in obigem Beispiel entnehmen.

Anmerkung: Apt kennt Recherchen analog zu Rpm's Optionen --requires, --provides und --whatrequires, nicht jedoch zu --whatprovides.

Alles in Ordnung?

Die Überprüfung der Integrität installierter Dateien geschieht wie auch bei Rpm mittels md5-Prüfsummen. Im Gegensatz zu Rpm erledigt dpkg jedoch nicht selbst die Aufgabe. Die Existenz der Prüfsummen, anhand derer Dateien kontrolliert werden können, liegt allein in der Verantwortung des Paketerstellers. Falls dieser jene überhaupt vorgesehen hat, finden Sie diese im Verzeichnis »/var/lib/dpkg/info« unter Namen »Paketname.md5sum«. Die Überprüfung müssen Sie von Hand vornehmen:

user@sonne> cd /
user@sonne> md5sum -c /var/lib/dpkg/info/xterm.md5sums
md5sum: MD5 check failed for 'usr/X11R6/bin/xterm'

Das Beispiel offenbart ein Problem. Ein erneutes Einspielen des Pakets scheint empfehlenswert.

Anmerkung: Die Dateien »/var/lib/dpkg/info/Paketname.md5sums« enthalten alle für md5sum erforderlichen Informationen (zum Paket gehörigen Dateinamen mit deren Prüfsummen).

Weitere Überprüfungsmechanismen (Rechte, Eigentümer, Modifikationszeit ..) kennt die Debian-Paketverwaltung nicht.

Installation von Deb-Paketen

...noch paar einführende Worte finden...

Installation und Konfiguration mit »dpgk --install«

Die traditionelle Methode zur Installation verwendet das Kommando dpkg. Vorhandene Abhängigkeiten werden zwar erkannt aber nicht aufgelöst, d.h. ein Paket lässt sich stets installieren, selbst wenn es wegen fehlender Vorbedingungen nicht nutzbar sein sollte. Dpkg arbeitet quasi wie Rpm in Verbindung mit den Optionen --nodeps --force.

dpkg -i Paketname
dpkg -i [-R] Verzeichnis

Sowohl ein einzelnes Paket als auch alle in einem Verzeichnis (einschließlich Unterverzeichnisse bei Angabe der Option »-R«) liegenden Pakete lassen sich mit einem einzigen Befehl installieren. Intern arbeitet dpkg folgende Schritte ab:

  1. Die Kontrolldateien aus dem neuen Paket werden entpackt
  2. Ist eine ältere Version des Pakets installiert, so wird, falls vorhanden, dessen prerm-Skript ausgeführt
  3. Das preinst-Skript des neuen Pakets wird ausgeführt (insofern existent)
  4. Die neuen Dateien werden entpackt; die alten gleichzeitig gesichert, um bei gescheiterter Installation diese restaurieren zu können
  5. Ist eine ältere Version des Pakets installiert, so wird, falls vorhanden, dessen postrm-Skript ausgeführt. Die zuvor gesicherten Dateien werden nun endgültig entfernt.
  6. Das neue Paket wird konfiguriert

Auch hierzu ein Beispiel:

user@sonne> find /cdrom/ -name 'ddd*deb'
/cdrom/debian/pool/main/d/ddd/ddd_3.3.1-14_i386.deb
user@sonne> dpkg -i /cdrom/debian/pool/main/d/ddd/ddd_3.3.1-14_i386.deb
Selecting previously deselected package ddd.
(Reading database ... 29164 files and directories currently installed.)
Unpacking ddd (from .../d/ddd/ddd_3.3.1-14_i386.deb) ...
Setting up ddd (3.3.1-14) ...

Installation und Konfiguration mit »apt-get»

dpgk lässt eine Eigenschaft schmerzlich vermissen: die automatische Auflösung von Abhängigkeiten. Aus diesem Grund ist apt-get oft erste Wahl bei den Werkzeugen zur Paketinstallation. Intern deligiert apt-get die Arbeit jedoch auch nur an geeignete dpkg-Aufrufe.

apt-get install Paketname

Damit apt-get seine Arbeit verrichten kann, benötigt es Kenntnis über alle zur Installation zur Verfügung stehenden Pakete. Hierzu sind sämtliche Quellen (CDROM, ein FTP-Verzeichnis, Verzeichnisse mit Paketen auf lokalen Platten...) in die Datei »/etc/apt/sources.list/« einzutragen. Hierzu können Sie sich eines Editors bedienen oder die Oberfläche von apt-setup verwenden.

Apt-get scannt sämtliche Quellen und kann anhand der resultierenden Paketliste entscheiden, ob die Abhängigkeiten bei der Installation eines Pakets erfüllt werden können. Ist dies der Fall, so wird das Kommando sowohl das Paket als auch alle von diesem benötigten Pakete selbsttätig installieren. Bei Nichterfüllung bricht die Installation ab.

user@sonne> pwd
/var/lib/dpkg/info
root@sonne> apt-get install ddd
Reading Package Lists...
Building Dependency Tree...
The following NEW packages will be installed:
ddd
0 packages upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 0B/1573kB of archives. After unpacking 4455kB will be used.
Media Change: Please insert the disc labeled 'Debian GNU/Linux 3.0-testing (woody) , disk 3 _2002-08-14/21:54_' in the drive '/cdrom/' and press enter


Selecting previously deselected package ddd.
(Reading database ... 29165 files and directories currently installed.)
Unpacking ddd (from .../d/ddd/ddd_3.3.1-14_i386.deb) ...
Setting up ddd (3.3.1-14) ...

Anstatt des konkreten Paketnamens können Sie bei apt-get sogar Namensmuster mit den Shell-üblichen Wildcards angeben (Schützen Sie diese vor der Auswertung durch die Shell!). Das Kommando wird alle Pakete, auf deren Name das Muster zutrifft, installieren.

Eine Liste alle verfügbaren Pakete erhalten Sie mittels »apt-cache dump-avail«. Etwas mehr Komfort bieten dselect und das noch in einem frühen Entwicklungsstadium steckende Programm aptitude, die wir hier nicht behandeln werden.

Installation ohne Konfiguration mit »dpgk --unpack»

dpkg --unpack Paketname
dpkg --unpack [-R] Verzeichnis

Die Option --unpack arbeitet wie -i mit dem einzigen Unterschied, dass der abschließende Schritt der Konfiguration des neuen Pakets ausgelassen wird.

user@sonne> dpkg --unpack /cdrom/debian/pool/main/d/ddd/ddd_3.3.1-14_i386.deb
Selecting previously deselected package ddd.
(Reading database ... 29164 files and directories currently installed.)
Unpacking ddd (from .../d/ddd/ddd_3.3.1-14_i386.deb) ...

Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)
||/ Name Version Description
+++-=====================-=====================- ==========================================================
iU ddd 3.3.1-14 The Data Display Debugger, a graphical debugger frontend.

Eine nachträgliche Konfiguration ist möglich und wird im nachfolgenden Abschnitt beschrieben.

Konfiguration und Reparatur installierter Pakete

Erfolgte eine Installation ohne anschließender Konfiguration eines Pakets, können Sie letzteren Schritt nachholen, indem Sie »dpkg --configure Paketname« ausführen:

user@sonne> dpkg --configure ddd
Setting up ddd (3.3.1-14) ...

user@sonne> dpkg --list ddd
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)
||/ Name Version Description
+++-=====================-=====================- ==========================================================
ii ddd 3.3.1-14 The Data Display Debugger, a graphical debugger frontend.

Fehlerhafte Installationen identifizieren Sie anhand der Statusspalten beim Listing eines Pakets:

user@sonne> dpkg -l ddd
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)
||/ Name Version Description
+++-=====================-=====================- ==========================================================
iHR ddd 3.3.1-14

»H« weist auch eine unvollständige Installation hin. Mit »R« wird eine erneute Installation empfohlen, um das Problem zu beheben:

user@sonne> apt-get install ddd
Reading Package Lists...
Building Dependency Tree...
1 packages upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
1 packages not fully installed or removed.
Need to get 0B/1573kB of archives. After unpacking 4455kB will be used.

(Reading database ...
dpkg: serious warning: files list file for package `ddd' missing, assuming package has no files currently installed.
29164 files and directories currently installed.)
Preparing to replace ddd 1:3.3.1-14 (using .../d/ddd/ddd_3.3.1-14_i386.deb) ...
Unpacking replacement ddd ...
Setting up ddd (3.3.1-14) ...

Die Statusspalten sollten sich bei Erfolg folgendermaßen präsentieren:

user@sonne> dpkg -l ddd
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)
||/ Name Version Description
+++-=====================-=====================- ==========================================================
ii ddd 3.3.1-14 The Data Display Debugger, a graphical debugger frontend.

Entfernen von Deb-Paketen

Auch zum Entfernen von Paketen werden wir die Verwendung der beiden Alternativen dpkg und apt-get demonstrieren. Beide Kommandos ermöglichen auf Wunsch das Beibehalten bestehende Konfigurationsdateien, was mitunter bei späterem Wiedereinspielen von Paketen deren Administrationaufwand vermindern kann.

Mit Erhalt der Konfigurationsdateien mit »dpkg -r«

root@sonne> dpkg -r ddd
(Reading database ... 29235 files and directories currently installed.)
Removing ddd ...
user@sonne> dpkg -l ddd
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)
||/ Name Version Description
+++-=====================-=====================- ==========================================================
rc ddd 3.3.1-14 The Data Display Debugger, a graphical debugger frontend.

Mit Erhalt der Konfigurationsdateien mit »apt-get remove«

root@sonne> apt-get remove ddd
Reading Package Lists...
Building Dependency Tree...
The following packages will be REMOVED:
ddd
0 packages upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
Need to get 0B of archives. After unpacking 4455kB will be freed.
Do you want to continue? [Y/n] Y
(Reading database ... 29235 files and directories currently installed.)
Removing ddd ...
user@sonne> dpkg -l ddd
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)
||/ Name Version Description
+++-=====================-=====================- ==========================================================
rc ddd 3.3.1-14 The Data Display Debugger, a graphical debugger frontend.

Inklusive der Konfigurationsdateien mit »dpkg -P«

root@sonne> dpkg -P ddd
(Reading database ... 29235 files and directories currently installed.)
Removing ddd ...
Purging configuration files for ddd ...
user@sonne> dpkg -l ddd
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)
||/ Name Version Description
+++-=====================-=====================- ==========================================================
pn ddd (no description available)

Inklusive der Konfigurationsdateien mit »apt-get --purge remove«

root@sonne> apt-get --purge remove ddd
Reading Package Lists...
Building Dependency Tree...
The following packages will be REMOVED:
ddd*
0 packages upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
Need to get 0B of archives. After unpacking 4455kB will be freed.
Do you want to continue? [Y/n] Y
(Reading database ... 29235 files and directories currently installed.)
Removing ddd ...
Purging configuration files for ddd ...
user@sonne> dpkg -l ddd
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)
||/ Name Version Description
+++-=====================-=====================- ==========================================================
pn ddd (no description available)

Nachträgliches Entfernen der Konfigurationsdateien

Haben Sie die Pakete bereits gelöscht und deren Konfigurationsdateien seinerzeit beibehalten, so müssen Sie auf »dpkg« zurückgreifen, um auch die Konfigurationsdateien aus dem System zu verbannen.

Wir demonstrieren die Arbeitsverweigerung von »apt-get --purge«:

user@sonne> dpkg -l ddd
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)
||/ Name Version Description
+++-=====================-=====================- ==========================================================
rc ddd 3.3.1-14 The Data Display Debugger, a graphical debugger frontend.

root@sonne> apt-get --purge remove ddd
Reading Package Lists...
Building Dependency Tree...
Package ddd is not installed, so not removed
0 packages upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

root@sonne> dpkg -P ddd
(Reading database ... 29165 files and directories currently installed.)
Removing ddd ...
Purging configuration files for ddd ...

Deb-Pakete selbstgebaut Zurück Anfang Weiter

Wir werden das Thema nicht erschöpfend abhandeln, insbesondere verzichten wir auf die registrierenden Schritte zum Erhalt des Status eines offiziellen Debian-Pakets. Als Ergebnis der folgenden Abschnitte wird ein Debian-konformes Paket der Linuxfibel stehen.

Aufbau von Deb-Paketen

Namenskonvention

Die Namenswahl von (offiziellen) Debian-Paketen ist fest vorgegeben:

<Paketname>_<Versionsnummer des Programmes>-<Releasenummer>_<Architektur>.deb

Die Releasenummer ist gleichbedeutend mit der Versionsnummer der Paketerstellung.

Der Aufbau eines Debianpakets

Eines der gebräuchlichsten Kommandos ist sicherlich tar - der Tape-Archiver. Sie haben es sicherlich schon verwendet, um Dateien und Verzeichnisse in eine einzelne Datei zu packen oder selbige aus einem tar-Archiv zu extrahieren. Im Grunde genommen kennen Sie somit bereits den Aufbau von Debian-Paketen: Sie sind nichts anderes als Archive, die eine Sammlung von Dateien und Verzeichnisse beinhalten. Anstelle des tar-Archivs tritt nun das ar-Archiv, anstelle des Kommandos tar tritt ar, das fast dieselben Optionen wie das bereits bekannte Kommando kennt.

Betrachten Sie den Inhalt eines beliebigen Debian-Pakets:

user@sonne> ar tvf xterm_4.2.1-3_i386.deb
rw-r--r-- 0/0      4 Oct 19 23:38 2002 debian-binary
rw-r--r-- 0/0   4039 Oct 19 23:38 2002 control.tar.gz
rw-r--r-- 0/0 484532 Oct 19 23:38 2002 data.tar.gz

Das Entpacken der Dateien erolgt erfolgt analog zur Option des tar-Kommandos mit x:

user@sonne> ar xf xterm_4.2.1-3_i386.deb

Die Dateien eines Debian-Pakets

debian-binary

user@sonne> cat debian-binary
2.0

Die Datei beschreibt die Version des verwendeten Formats der Debian-Datei. Im Beispiel verwenden wir die derzeit aktuelle Version 2.0. Wichtig ist, dass der Eintrag der Versionsnummer mit einem Zeilenumbruch abgeschlossen wird.

control.tar.gz

user@sonne> tar tzvf control.tar.gz drwxr-xr-x root/root         0 2002-10-19 23:37:13 ./
-rw-r--r-- root/root       734 2002-10-19 23:37:09 ./control
-rwxr-xr-x root/root      7276 2002-10-19 23:36:18 ./prerm
-rw-r--r-- root/root        91 2002-10-19 22:22:51 ./conffiles
-rw-r--r-- root/root      1052 2002-10-19 23:37:13 ./md5sums
-rwxr-xr-x root/root      7914 2002-10-19 23:36:18 ./postinst
-rwxr-xr-x root/root       147 2002-10-19 23:36:18 ./postrm

Das Paket der Kontrolldateien umfasst mindestens die Datei control mit der Beschreibung des Pakets. Die Datei dient u.a. zur Versionsprüfung und Abhängigkeitsverifizierung. Sie können ebenso dpkg -p konsultieren, um folgende Ausgabe zu erhalten:

user@sonne> cat control Package: xterm
Version: 4.2.1-3
Section: x11
Priority: optional
Architecture: i386
Depends: debconf (>> 0.5), libc6 (>= 2.2.4-4), libfreetype6, \
         libncurses5 (>= 5.2.20020112a-1), libxaw7 (>> 4.1.0), xlibs (>> 4.1.0)
Conflicts: xbase (<< 3.3.2.3a-2), suidmanager (<< 0.50)
Replaces: xbase (<< 3.3.2.3a-2)
Provides: x-terminal-emulator
Installed-Size: 900
Maintainer: Branden Robinson <branden@debian.org>
Source: xfree86
Description: X terminal emulator
 xterm is a terminal emulator for the X Window System. It provides DEC VT102
 and Tektronix 4014 compatible terminals for programs that cannot use the
 window system directly. This version implements ISO/ANSI colors and most of
 the control sequences used by DEC VT220 terminals.

Optionale Dateien im Kontrollpaket sind die Skripte, die unmittelbar vor bzw. nach einer (De)Installation auszuführen sind. Die Namen der Skripte sind mit preinst, prerm, postinst und postrm fest vorgegeben. Jedes der Skripte kann mit konkreten Parametern aufgerufen werden, sodass eine Steuerung des Skriptablaufs nach gewähltem (De)Installationskontext möglich wird. Die wichtigsten Parameter sind:

preinstinstall, upgrade
postinstconfigure
prermremove, upgrade
postrmremove, purge

Im Debian Policy Manual (Chapter 6 - Package maintainer scripts and installation procedure) können Sie sich über die weiteren Parameter informieren.

Ein Skript sollte bei Fehler freiem Ablauf mit dem Wert 0 zurück kehren und mit einem Wert ungleich 0 sonst.

Gehören Konfigurationsdateien zu einem Paket, so werden deren Namen inklusive Pfadangaben in der Datei conffiles hinterlegt:

user@sonne> cat conffiles
/etc/X11/app-defaults/UXTerm
/etc/X11/app-defaults/XTerm
/etc/X11/app-defaults/XTerm-color

Die Unversehrheit der Dateien in einem Paket kann anhand des Inhalts einer Datei md5sums verifiziert werden, insofern die Paketbauer diese sinnvolle Eigenschaft vorgesehen hatten:

user@sonne> cat md5sums
d672bbf649879f0ce1f340b12f9f8283  usr/lib/menu/xterm
1f68f7ccdb684fb957d7dd983e24e853  usr/X11R6/bin/xterm
ea8870cc936d434239667b3ae5a969bb  usr/X11R6/bin/uxterm
44bb39c5d838a6f062b3735562d459d3  usr/X11R6/man/man1/xterm.1x.gz
096965faecdbdf21e26a667d86a5b415  usr/X11R6/man/man1/uxterm.1x.gz
89fc7c793984ab7c1d8288ceb0dc9f14  usr/share/doc/xterm/xterm.faq.html
526f04b3821954928cde267aeb3306f2  usr/share/doc/xterm/ctlseqs.txt.gz
5ae55d1ac3656abfa4def63d08154852  usr/share/doc/xterm/xterm.faq.text.gz
792df42bff304318c856fd70517c425c  usr/share/doc/xterm/xterm.termcap.gz
3f9346aec8a5fcc6f61d35e0560717fe  usr/share/doc/xterm/xterm.log.html
b6817b9e8bf67218513e02cfec51385a  usr/share/doc/xterm/README.Debian
05469648defd7feef538795654c26b19  usr/share/doc/xterm/xterm.terminfo.gz
2f42d779fe32dc12631e1e5d9a3960c2  usr/share/doc/xterm/copyright
9e3d633a5fb22f4f42b373dd9fc5f45a  usr/share/doc/xterm/ctlseqs.ps.gz
d18d22063d3c60f1cbf27b8d06015102  usr/share/doc/xterm/changelog.Debian.gz
b41453c1f2d3fec3baeb1209fe33f102  usr/share/doc-base/xterm-faq1

data.tar.gz

Dieses Archiv enthält die eigentlichen Paketdateien (Programme, Dokumentationen, Konfigurationsdateien...).

Die Linuxfibel als Debian-Paket

Nach den allgemein gehaltenen Vorbetrachtungen vollführen wir den Schritt zum Debian-Paket anhand des konkreten Beispiels der Linuxfibel.

Vorbereitung der Umgebung

Zunächst benötigen wir ein Verzeichnis, indem der Paketbau vollzogen werden kann. Prinzipiell kann das Verzeichnis irgendwo im Dateisystem liegen und beliebig benannt sein. In Analogie zu Rpm sammeln wir sämtliche Pakete unterhalb eines Verzeichnisses »/usr/src/deb«:

root@sonne> mkdir -p /usr/src/deb/linuxfibel
# Die Arbeit als Root ist u.U. gefährlich!
root@sonne> chown user:users /usr/src/deb/linuxfibel
user@sonne> cd /usr/src/deb/linuxfibel
user@sonne> mkdir DEBIAN
user@sonne> chmod 755 DEBIAN

In das Unterverzeichnis DEBIAN gelangen später die Beschreibungsdateien und (De)Installations-Skripte.

Kopieren der Dateien

In das neue Arbeitsverzeichnis sind alle Dateien zu kopieren, die zum Paket gehören sollen. Im Fall der Linuxfibel sind das die HTML-Dateien und die Bilder. Wir gehen nachfolgend davon aus, dass im Verzeichnis »/usr/share/doc/linuxfibel« unseres Systems all jene Dateien liegen, die das fertige Paket umfassen soll:

user@sonne> (cd /; tar cf - /usr/doc/share/linuxfibel) | tar xvf -

Anlegen der Kontrolldateien

Sämtliche Kontrolldateien und Skripte gehören in das Verzeichnis DEBIAN unterhalb des Build-Verzeichnisses.

debian-binary

Hier hinein gehöert die Versionsnummer des verwendeten Paketformats

user@sonne> cat DEBIAN/debian-binary
2.0

control

Der im Beispiel verwendete Inhalt der Datei »control« umfasst die minimal erforderlichen Einträge.

user@sonne> cat DEBIAN/control
Package: linuxfibel
Version: 0.8-2
Section: base
Priority: optional
Architecture: all
Maintainer: Thomas Ermer <thomas.ermer@saxsys.de>, Michael Meyer <michael.meyer@saxsys.de>
Description: Die Linuxfibel
   Die Linuxfibel ist ein distributionsunabhängiges Lehrbuch und Referenz zu Themen rund um Linux.

Wichtige Einträge darüber hinaus sind:

Depends

Erfordert Ihr Paket die Installation weiterer Software, so können Sie diese hier inklusive der notwendigen Version angeben. Dem Namen des Softwarepakets folgt die in runde Klammern anzugebende Version. Weitere Softwarepakete schließen sich, durch Kommata voneinander getrennt, an ("gcc (>=3.2), glibc (>> 2.2)").

Conflicts

Steht Ihr Paket in Konflikt mit anderen (sodass beide nicht gleichzeitig installiert sein sollten), so können Sie auch dieses in der Kontrolldatei vermerken. Die Syntax eines Eintrags ist analog zu Depends.

Replaces

Ist das Paket ein vollständiger Ersatz für ein anderes, kann der Name des zu ersetzenden Pakets hier angegeben werden.

Provides

Hier steht ein virtueller Paketname, der eine Einordnung in eine Anwendungsgruppe ermöglichen soll. Somit kann der Anwender in Installationsprogrammen (bspw. dselesct) aus einer Reihe von Programmen auswählen, die nahezu dasselbe tun.

Installed-Size

Angabe der Paketgröße.

md5-Prüfsummen

Wir statten unser Paket mit MD5-Prüfsummen über jede der Dateien aus:

user@sonne> find /usr/share/linuxfibel -type f -exec md5sum {} \; > DEBIAN/md5sum

Der eigentliche Bau

Hierfür wechseln Sie aus dem Build- in das übergeordnete Verzeichnis. Rufen Sie dpkg-deb mit der Option --build und dem Namen des Buildverzeichnisses als Argument:

user@sonne> pwd
/usr/src/deb/linuxfibel

user@sonne> cd ..
root@sonne> dpkg-deb --build linuxfibel
dpkg-deb: building package `linuxfibel' in `linuxfibel.deb'.

Das fertige Paket liegt als »linuxfibel.deb« vor. Wir prüfen dessen Kontrolldatei:

user@sonne> dpkg --info linuxfibel.deb
new debian package, version 2.0.
size 7258184 bytes: control archive= 345 bytes.
   303 bytes,     8 lines      control
Package: linuxfibel
Version: 0.8-2
Section: base
Priority: optional
Architecture: all
Maintainer: Thomas Ermer <thomas.ermer@saxsys.de>, Michael Meyer <michael.meyer@saxsys.de>
Description: Die Linuxfibel
   Die Linuxfibel ist ein distributionsunabhängiges Lehrbuch und Referenz zu Themen rund um Linux.

Um der Namenskonvention für Debian-Pakete zu entsprechen, ist das Paket noch umzubenennen:

root@sonne> mv linuxfibel.deb linuxfibel_0.8-2_1_i386.deb

Alien Zurück Anfang Weiter

Falls ein Programm nicht in einem von »meiner« Distribution unterstützten Format vorliegt, so kann eine Formatwandlung eine Installation doch noch ermöglichen.

Als Voraussetzung für den Einsatz des Perl-Skripts alien müssen die Programme zur Verwaltung der »Fremdformate« installiert sein, also:

Zum Lesen/Erzeugen eines Rpm-Pakets: rpm
Zum Lesen/Erzeugen eines Tgz-Pakets: tar
Zum Lesen/Erzeugen eines Deb-Pakets: debmake, dpkg-dev, dpks, make und gcc

Eine Paketverwaltung für ein System

Auch wenn die Programme zur direkten Installation von »Fremdpaketen« vorhanden sind, sollten Sie diese niemals zum Installieren verwenden! Paketverwaltungen basieren auf einer Datenbank und jede Verwaltung verwendet ihre eigene. Leider wissen die Programme nichts von der Existenz der »Konkurrenz«, sodass Vorteile wie Berücksichtigung von Abhängigkeiten, Paketaktualisierung, sauberes Entfernen usw. nicht mehr gegeben sind.

Des Weiteren ist eine Wandlung oft mit einem Verlust an Information behaftet. Deb- und Rpm-Pakete unterscheiden sich im internen Aufbau, sodass manche Aspekte bei der Konvertierung nicht berücksichtigt werden können. Das Tgz-Format beinhaltet gar nur die zu installierenden Dateien ohne jegliche Hinweise auf Version, Abhängigkeiten... Eine Wandlung von Tgz in ein anderes Format ermöglicht einzig die (De)Installation der Daten, aber mehr auch nicht!

Und so gehts...

ie Handhabung von alien gestaltet sich spielend einfach. Das Format des Eingabepakets ermittelt das Programm anhand dessen Struktur. Ohne Angabe weiterer Argumente wird das Debian-Format erzeugt. Alternative Formate sind:

--to-tgz Erzeugen des Tgz-Formats
--to-rpm Erzeugen des Rpm-Formats
--to-deb Erzeugen des Deb-Formats (Voreinstellung)
--to-slp Erzeugen des Slp-Formats

Das nachfolgende Beispiel wandelt das Linuxfibel-Basis-RPM-Paket in das Tgz-Format:

user@sonne> alien --to-tgz linuxfibel_basis-0.6-0.i386.rpm
Warning: alien is not running as root!
Ownerships of files in the generated packages will probably be messed up.
linuxfibel_basis-0.6.tgz generated

Die Warnung ist nicht weiter tragisch. Sie weist nur darauf hin, dass die erzeugte Datei nicht Root gehört und somit ein Sicherheitsrisiko darstellen könnte (da nicht nur Root das Paket bearbeiten darf).

Das Erzeugen von deb-, rpm- oder slp-Formaten bleibt dann aber einzig Root vorbehalten. Um ein Format von alien lesen bzw. schreiben zu können, muss das entsprechende Werkzeug (bspw. rpm oder debmake) installiert sein. Natürlich kann eine solche Konvertierung nicht sämtliche Informationen sauber in ein Paket integrieren, die diese »normalerweise« enthalten würde. Gerade die Erzeugung der ausgereiften rpm- oder deb-Formate aus einem Tape Archiv kann nur eine saubere (De)Installation der Dateien garantieren. Abhängigkeiten o.Ä. bleiben vollends unberücksichtigt:

root@sonne> alien --to-rpm linuxfibel_basis-0.6.tgz
linuxfibel_basis-0.6.2.noarch.rpm generated

Zumindest die fehlende Paketbeschreibung kann bei Konvertierung aus dem tgz-Format per Kommandozeile ergänzt werden. Geben Sie hierzu die Option --description=Text an.

Auch können Sie das Paket unverzüglich installieren, indem Sie die Option -i verwenden.

Bibliotheken Zurück Anfang

Mit dem Einspielen von Bibliotheken ins System ist es oft noch nicht getan. Was nützt es, wenn ein Programm abbricht, weil es »seine« Bibliotheken nicht finden kann?

Zunächst bedarf es eines kleinen Ausflugs, wie in Unix der Umgang mit Bibliotheken erfolgt. Nachfolgend diskutieren wir die Möglichkeiten, um Fehlerausgaben, wie die Folgende, zu vermeiden:

user@sonne> ./MyProgram
ld.so: MyProg: fatal: libMylib.so: can't open file

Statisch, dynamisch, shared

Ein erster naiver Erklärungsversuch könnte »statisch« mit »unveränderbar« gleich setzen. Hieraus ließe sich schlussfolgern, dass dynamisch gelinkte Programme sich ändern (können), doch zum Glück ist dem dann doch nicht so.

Gebräuchlicher ist die Umschreibung eines statisch gelinkten Programms als ein Programm, das zur Laufzeit eine konstante Größe an Hauptspeicher verkonsumiert. Aus diesem Blickwinkel betrachtet, wäre der Speicherbedarf dynamischer gelinkter Programme änderbar. Und tatsächlich entspricht diese Betrachtungsweise dem wahren Wesen statisch bzw. dynamisch gelinkter Programme. Dem statisch gelinkten Programm wurde die komplette Funktionalität bereits zur Kompilierzeit einverleibt. Es ist unabhängig von jeglichen Bibliotheken und somit prinzipiell auf jedem System lauffähig, das das Programmformat unterstützt. Das dynamisch gelinkte Programm beinhaltet anstatt »verbreiteter« Funktionen nur die Schnittstellen zu diesen; die Funktionen selbst liegen in Form von Bibliotheksroutinen vor. Es ist klar, dass das dynamisch gelinkte Programm i.d.R. weniger Platz auf der Festplatte beansprucht, als es ein statisch gelinktes Pedant tut, das dieselbe Funktionalität erfüllt.

Der Nutzen der dynamischen Realisierung wird erst recht deutlich, wenn man die Gemeinsamkeiten zahlreicher Programme betrachtet. So greift nahezu jedes Programm auf Routinen der Ein- und Ausgabe zu, die meisten Programme verwenden eine dynamische Speicherverwaltung usw. Kandidaten für Funktionen, die besser in Bibliotheken realisiert werden sollten, finden sich reichlich und tatsächlich existieren in einem Linuxsystem nur noch wenige statisch gelinkte Programme (der dynamische Linker ist eines davon).

Die dynamisch gelinkten Programme bedingen jedoch die Anwesenheit der benötigten Bibliotheken. Je nach Zeitpunkt, wann eine Bibliothek geladen wird, werden »dynamisch ladbare« und »geteilte« (engl.: shared) Bibliotheken unterschieden. Diese begriffliche Trennung ist etwas unglücklich, da sich beide Bibliothekenstypen nicht unterscheiden. Ob sie nun als dynamisch ladbare oder als shared Bibliothek fungieren, hängt einzig vom Funktionsaufruf des Programms ab. Lädt dieses eine Bibliotheksfunktion als »shared«, so wird die Bibliothek zum Zeitpunkt des Ladens des Programms in den Hauptspeicher geladen (falls ein anderes Programm diese Bibliothek bereits geladen hatte, entfällt dieser Schritt). Im Falle eines »dynamischen« Aufrufs erfolgt das Laden erst, wenn tatsächlich auf eine Funktion aus dieser Bibliothek zugegriffen wird.

Um noch einmal auf den Unterschied zwischen statisch und dynamisch gelinkten Programmen einzugehen, so lässt sich ein weiterer Vorteil der Bibliotheken nennen. Sie sind durch andere Versionen austauschbar, solange die Schnittstellen der Funktionen und die Datenstrukturen beibehalten werden. Somit ist es bspw. möglich, einer grafischen Anwendung unter X ein anderes Aussehen zu verleihen, indem einfach die zuständige Grafikbibliothek gewechselt wird.

Ein Übel verbirgt sich dennoch bei dynamisch gelinkten Programmen. Ohne die benötigten Bibliotheken sind sie nicht lauffähig. Der nächste Schritt, die Bibliotheken ins System einzuspielen, genügt meist nicht. Das Programm muss sie auch noch finden können...

Wie sucht ein Programm dynamische Bibliotheken?

Das Aufspüren der Bibliotheken obliegt einem dynamisch gelinkten Programm selbst. Hierzu beinhaltet es den Dateinamen des dynamischen Linkers ld-linux.so.2 (Glibc2) zu Beginn seines Programmkodes:

user@sonne> strings /lib/cat | head -1
/lib/ld-linux.so.2

ld-linux.so.2 selbst zeigt als symbolischer Link auf die aktuelle Version des Linkers.

Startet das Programm, lädt der Kernel diesen dynamischen Linker. Jede dynamische Bibliothek, die ein Programm verwendet, hinterlässt in diesem seinen so genannten »so-Namen«. Dabei handelt es sich um den Präfix lib, gefolgt vom Namen der Bibliothek, wiederum gefolgt von .so. und der Hauptversionsnummer (bspw.: libXt.so.6). Im Dateisystem selbst ist eine dynamische Bibliothek unter ihrem so-Namen, gefolgt von einer Nebenversionsnummer und einer optionalen Patchnummer gespeichert (bspw.: libXt.so.6.1.1).

Der dynamische Linker akzeptiert die Anforderung einer Bibliothek, wenn die so-Namen übereinstimmen. Denn eine abweichende Hauptversionsnummer bedeutet eine geänderte Schnittstelle, womit ein Programm mit einer solchen Bibliothek nicht zusammenarbeiten kann.

Der Linker lädt nun die geforderte Bibliothek - falls sie nicht schon dort ist - in den Hauptspeicher. Er kennt nun die relative Lage (Adresse) der Funktionen und Variablen, die das Programm aus der Bibliothek benötigt und manipuliert die Verweise in der »Global Offset Table« (globale Variablen) bzw. in der »Procedure Linkage Table« (Funktionen) des Programms.

Zur Suche einer Bibliothek bedient sich der Linker mehrfacher Mechanismen. Zunächst darf ein dynamisch gelinktes Programm auch die kompletten Pfade zu den Bibliotheken eingebunden haben, womit der Linker genau diese verwenden wird. Ein solches Vorgehen erschwert u.a. die Installation von Programmen einer Distribution auf einem »Fremdsystem«, sodass i.d.R. darauf verzichtet wird.

Als nächstes betrachtet der Linker den Inhalt verschiedener globaler Umgebungsvariablen. Selten von den Distributionen angewandt - aber möglich - ist die Belegung der Variablen LD_PRELOAD mit den vollständigen Pfadangaben zu Bibliotheken, die vor allen anderen zu laden sind. Somit ist es denkbar, konkrete Funktionen aus anderen Bibliotheken zu überschreiben, da der Linker eine einmal geladene Funktion nicht wieder überschreibt. Aus Sicherheitsgründen wird das Verfahren bei setuid/setgid-Programmen nur zugelassen, wenn Benutzer-ID (UID) und effektive Benutzer-ID (EUID) - bez. Gruppen-ID (GID) - und effektiver Gruppen-ID (EGID) übereinstimmen. In der Praxis setzen vor allem Speicherleck-Prüfprogramme auf den PRELOAD-Mechanismus auf, um (De)Allokationsroutinen durch eigene Versionen zu überladen.

Vor den Standardpfaden werden auf der Suche nach dynamischen Bibliotheken anschließend die in der Umgebungsvariablen LD_LIBRARY_PATH aufgeführten Pfade betrachtet.

Schließlich kommen die Bibliotheken aus den Standardpfaden zum Zuge. Aus Gründen der Effizienz erfolgt die Suche nicht in den Pfaden aus /etc/ld.so.conf sondern einzig in der Datei /etc/ld.so.cache, die eine Liste aller Bibliotheken aus diesen Pfaden enthält. Das Erzeugen dieser Cache-Datei obliegt dem Kommando ldconfig, das uns später noch interessieren soll.

Test mit ldd

Natürlich wird der Aufruf eines Programms, das eine Bibliothek vermisst, den Namen dieser ausgeben, womit fehlende Abhängigkeiten automatisch ans Licht gelangen. Darüber hinaus gestattet das Kommando ldd aber auch einen Einblick in die bereits erfüllten Voraussetzungen, indem es zu den auf der Kommandozeile angegebenen Programmen oder Bibliotheken die benötigten Bibliotheken ausgibt:

user@sonne> ldd /bin/ls
        libtermcap.so.2 => /lib/libtermcap.so.2 (0x40024000)
        librt.so.1 => /lib/librt.so.1 (0x40028000)
        libc.so.6 => /lib/libc.so.6 (0x4003a000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x40167000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
user@sonne> ldd -v /lib/libc.so.6
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

        Version information:
        /lib/libc.so.6:
                ld-linux.so.2 (GLIBC_2.1.1) => /lib/ld-linux.so.2
                ld-linux.so.2 (GLIBC_2.1) => /lib/ld-linux.so.2
                ld-linux.so.2 (GLIBC_2.2) => /lib/ld-linux.so.2
                ld-linux.so.2 (GLIBC_2.0) => /lib/ld-linux.so.2

Die im Beispiel angewandte Option -v gibt einige erweitere Informationen preis.

Konfiguration des dynamischen Linkers

Wie bereits erwähnt, wird die Arbeitsweise des Linkers sowohl durch Umgebungsvariablen als auch durch die Datei /etc/ld.so.cache beeinflusst. Letztere Datei wiederum wird durch das Programm ldconfig erzeugt, welches in /etc/ld.so.config konfiguriert wird.

Somit bieten sich bei der Installation neuer Bibliotheken zwei Wege an, damit der Linker diese in Zukunft finden kann:

  1. Erzeugen einer neuen Datei /etc/ld.so.cache (ldconfig)
  2. Setzen der Variablen LD_LIBRARY_PATH

Die Belegung von LD_LIBRARY_PATH ist die »schnelle Methode«, wenn ein Programm mal eben getestet werden soll und das Laden der Bibliothek durch den Linker scheitert, weil dieser sie nicht finden kann. Ansonsten hängt diesem Vorgehen derselbe Nachteil an, der zum Caching-Mechanismus führte: eine Suche im Dateisystem kostet Zeit. Ein Vorteil wäre dennoch zu nennen: mittels LD_LIBRARY_PATH darf jeder Benutzer Einfluss auf den Linker nehmen, während eine Manipulation von /etc/ld.so.cache einzig dem Administrator vorbehalten ist. Die Syntax zur Belegung der Variablen erfolgt analog zur Variablen PATH.

Um die Cache-Datei /etc/ld.so.cache des Linkers zu aktualisieren, genügt nach der Installation neuer Bibliotheken zumeist ein Aufruf von ldconfig:

root@sonne> ldconfig

ldconfig durchsucht zuerst die Verzeichnisse /usr/lib und /lib und nachfolgend alle in der Datei /etc/ld.so.conf aufgeführten Pfade. Zu jeder gefundenen neuen Bibliothek erzeugt ldconfig selbsttätig den symbolischen Link des so-Namens auf den Bibliotheksnamen (also bspw. libX.so.6 als Link auf libX.so.6.1) und aktualisiert die Datei /etc/ld.so.cache.

Das Erzeugen des Links kann mittels der Option -l verhindert werden. Mit -n Pfad[e] werden einzig die angegebenen Verzeichnisse betrachtet (also auch nicht /usr/lib und /lib).

Eine Konfiguration ist - wenn überhaupt - nur durch Hinzufügen weiterer Suchpfade in die Datei /etc/ld.so.conf erforderlich. Jeder Pfad erscheint in dieser auf einer eigenen Zeile:

root@sonne> vi /etc/ld.so.conf
/usr/X11R6/lib
/usr/local/lib

Vergessen Sie nicht, nach Manipulation dieser Datei die Änderung durch Aufruf von ldconfig auch zu aktivieren. Auch sollten Sie beachten, dass die Pfade in der aufgeführten Reihenfolge durchsucht und bei gleichnamigen Bibliotheken stets nur die »erste« gefunden wird.

Wohl eher für Entwickler interessant ist die Datei /etc/ld.so.preload, die eine Liste durch Leerzeichen getrennter Bibliotheken enthält, welche vor allen anderen Bibliotheken geladen werden. Analog zur Variablen LD_PRELOAD ist somit das Überschreiben einzelnen Funktionen möglich.

Korrekturen, Hinweise?
Startseite Nächste Seite Nächstes Kapitel Vorherige Seite Kapitelanfang