Das Symptom
Ein Datenimport läuft über mehrere Tage. Das Skript legt seine Zwischenstände in
/tmp ab, wo sie nicht stören und wo jeder schreiben darf. Am Montag fehlt die Hälfte der
Dateien. Der Rechner lief durch, niemand hat rm aufgerufen, und die Dateien eines
Kollegen vom Freitag liegen noch da. Es sieht nach einem defekten Dateisystem aus
oder nach einem Kollegen mit schlechtem Gewissen.
Die zweite Variante trifft Entwickler: Ein Dienst schreibt seine Sperrdatei oder
seinen Unix-Socket nach /tmp und läuft wochenlang stabil. Dann ist der Socket
plötzlich weg, obwohl der Prozess noch lebt, und Verbindungen schlagen fehl.
Neustart hilft, und der Fehler wird als Laune des Systems abgelegt.
Die „Layer 8“-Wahrheit
Es gibt einen Aufräumdienst, und er tut, was in seiner Konfiguration steht.
tmpfiles.d(5) beschreibt den Zweck des Mechanismus als Anlegen von Dateien und
Verzeichnissen, Anpassen ihrer Rechte „and finally their time-based removal“. Die
Zeile, die /tmp regelt, lag auf dem Prüfsystem in /usr/lib/tmpfiles.d/tmp.conf und
lautete q /tmp 1777 root root 10d. Das letzte Feld ist das Alter: Was älter als zehn Tage
ist, wird entfernt. Die Handbuchseite formuliert die Regel ohne Ausnahme: „If a file or
directory is older than the current time minus the age field, it is deleted.“
Dass es ohne Neustart passiert, liegt am Timer. systemd-tmpfiles(8) nennt
systemd-tmpfiles-clean.service und systemd-tmpfiles-clean.timer als Systemeinheiten;
der Timer auf dem Prüfsystem startete den Dienst eine Viertelstunde nach dem Boot und
danach täglich. Ein Server mit 400 Tagen Laufzeit hat die Bereinigung also rund
400 Mal ausgeführt. Dass die Datei des Kollegen vom Freitag noch da ist, widerspricht
dem nicht: Sie war schlicht noch keine zehn Tage alt.
Was als Alter zählt, ist der Teil, an dem die meisten Annahmen scheitern. Die
Handbuchseite nennt drei Zeitstempel, mtime, atime und bei Dateien auch ctime, und
jeder davon hält die Datei am Leben, solange er jung genug ist. Der Zugriffsstempel ist
dabei der unzuverlässigste, weil er davon abhängt, wie das Dateisystem eingehängt ist.
Der Importlauf, der eine Datei vor zwölf Tagen geschrieben und seither nur gelesen hat,
kann deshalb genau in die Lücke fallen. Bei Verzeichnissen wird der ctime laut
Handbuchseite absichtlich nicht beachtet, weil das Löschen von Dateien darin den
ctime des Verzeichnisses ändert und die Bereinigung sich sonst selbst verjüngen würde.
Für laufende Programme gibt es eine Ausnahme, die zum Design gehört: Die Bereinigung
nimmt auf jedem Eintrag, den sie löschen will, eine BSD-Dateisperre, und was bereits
gesperrt ist, wird samt Inhalt übersprungen. Ein Dienst, der sein Verzeichnis unter
/tmp per flock hält, ist geschützt, solange er läuft. Der Socket aus dem zweiten
Symptom war nicht gesperrt und älter als zehn Tage, also weg, obwohl der Prozess noch
lief. Der Zeitstempel einer Socket-Datei ändert sich durch Verbindungen nicht
zwingend; die Datei liegt da und altert.
Der Ausweg ist einfacher als die Diagnose. Wer eine Ausnahme braucht, schreibt sie als
x-Zeile nach /etc/tmpfiles.d/, wo laut Handbuchseite die Dateien des lokalen
Administrators liegen und gleichnamige Paketdateien überschreiben. Wer ein anderes Alter
für /tmp will, legt dort eine eigene tmp.conf an. Und wer Daten länger als zehn Tage
braucht, legt sie nicht in ein Verzeichnis, dessen Hausordnung im Namen steht.
Die echte, verifizierte Lösung
1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:
1.Die geltende Regel für /tmp finden
Die Konfiguration liegt in Dateien unter /usr/lib/tmpfiles.d/, /run/tmpfiles.d/ und /etc/tmpfiles.d/. Laut tmpfiles.d(5) installieren Pakete ihre Dateien unter /usr/lib/tmpfiles.d, und Dateien in /etc/tmpfiles.d überschreiben gleichnamige Dateien in den beiden anderen Verzeichnissen. Die Suche nach /tmp zeigt die Zeile mit dem Altersfeld. Auf dem Prüfsystem lautete sie q /tmp 1777 root root 10d: Typ q, Pfad, Rechte 1777, Eigentümer root, Gruppe root, Alter zehn Tage. Das Altersfeld gilt laut Handbuchseite nur für die Typen d, D, e, v, q, Q, C, r, R, x und X; steht dort ein Strich, wird nicht aufgeräumt.
grep -rn '/tmp' /usr/lib/tmpfiles.d/ /run/tmpfiles.d/ /etc/tmpfiles.d/ 2>/dev/null
2.Den Zeitplan der Bereinigung lesen
systemd-tmpfiles(8) führt systemd-tmpfiles-clean.service und systemd-tmpfiles-clean.timer als Systemeinheiten auf. Der Timer bestimmt, wann der Dienst läuft; seine Einstellungen stehen in der Unit-Datei und nicht in der Handbuchseite. Auf dem Prüfsystem enthielt der Timer OnBootSec=15min und OnUnitActiveSec=1d, also ein Lauf eine Viertelstunde nach dem Start und danach einmal täglich. Bei einem Rechner, der nie neu gestartet wird, läuft die Bereinigung also trotzdem jeden Tag, was die Beobachtung erklärt, dass Dateien ohne Neustart verschwinden.
systemctl cat systemd-tmpfiles-clean.timer
systemctl list-timers systemd-tmpfiles-clean.timer
3.Vorab ansehen, was gelöscht würde
Bevor man die Konfiguration ändert, hilft ein Blick, wie das Werkzeug die aktuelle Lage beurteilt. systemd-tmpfiles(8) beschreibt den Befehl --clean so: „If this command is passed, all files and directories with an age parameter configured will be cleaned up.“ Zusammen mit --dry-run führt das Werkzeug nichts aus, sondern zeigt nur an, was es täte. Das Ergebnis zeigt, welche Dateien die Altersgrenze bereits überschritten haben. Ohne --dry-run wird tatsächlich gelöscht, deshalb den Schalter erst entfernen, wenn das gewollt ist.
sudo systemd-tmpfiles --dry-run --clean
4.Eigene Ausnahme oder eigenes Alter setzen
Wer einen Pfad in /tmp dauerhaft von der Bereinigung ausnehmen will, legt eine eigene Datei unter /etc/tmpfiles.d/ an. Eine Zeile vom Typ x schließt laut tmpfiles.d(5) einen Pfad samt Inhalt von der Bereinigung aus, Typ X nur das Verzeichnis selbst, nicht dessen Inhalt; beide akzeptieren Shell-Globs. Soll stattdessen das Alter für /tmp insgesamt anders sein, legt man eine Datei mit demselben Namen wie die Paketdatei an, also tmp.conf, und trägt die Zeile mit dem gewünschten Alter ein; laut Handbuchseite überschreibt die Datei in /etc/tmpfiles.d dann die gleichnamige in /usr/lib/tmpfiles.d. Nach der Änderung reicht laut systemd-tmpfiles(8) ein Neustart von systemd-tmpfiles-clean.service, um die Einstellungen neu anzuwenden.
# /etc/tmpfiles.d/mein-import.conf
x /tmp/mein-import
5.Den Ablageort ehrlich wählen
Die eigentliche Korrektur ist meist keine Konfigurationsänderung, sondern eine Entscheidung: Was länger als die konfigurierte Frist gebraucht wird, gehört nicht nach /tmp. tmpfiles.d(5) beschreibt den Mechanismus ausdrücklich als für flüchtige und temporäre Dateien gedacht, mit /run/, /tmp/ und /var/tmp/ als typischen Orten. Für Dienste empfiehlt die Handbuchseite RuntimeDirectory=, StateDirectory= und CacheDirectory= in der Unit-Datei, womit die Lebensdauer eines Verzeichnisses an den Dienst gebunden ist. Ein Import, der eine Woche braucht, und ein Zwischenstand, den man nächsten Monat noch braucht, gehören in ein Verzeichnis, das man selbst aufräumt.