Wiederherstellen einer geöffneten Datei

Wiederherstellen einer geöffneten Datei

Ich habe ein interessantes Problem, für das es vielleicht eine Lösung gibt, vielleicht aber auch nicht, aber wenn möglich hätte ich gern eine:

Unter Solaris wurde eine offene Protokolldatei entfernt, die während der Ausführung des Prozesses weiterhin gefüllt wird, jetzt aber für alle anderen Tools wie cat, tailusw. nicht mehr zugänglich ist.

Gibt es eine Möglichkeit, den Eintrag im Verzeichnis dieser Datei wiederherzustellen, während alles weiterläuft?

Antwort1

Dies ist mit einigen Hacking-Techniken und Einschränkungen machbar (Sie benötigen Root-Rechte).

Finden Sie zunächst heraus, welchen Dateideskriptor die Anwendung zum Schreiben der Protokolldatei verwendet. Erstellen Sie dann einen symbolischen Link am vorherigen Speicherort der Protokolldatei, der auf den Eintrag /proc der Datei verweist, z. B.:

ln -s /var/tmp/file.log /proc/12345/fd/3

Die erste Einschränkung besteht darin, dass, wenn die Datei nur zum Schreiben durch den Prozess geöffnet wurde, seine Berechtigung es einem nicht privilegierten Benutzer nicht erlaubt, ihren Inhalt zu lesen. Root und Benutzer mit dem Privileg file_dac_read sind davon jedoch nicht betroffen. Alternativ können Sie einen Prozess verwenden, um den Dateiinhalt zu kopieren, tailwie Gilles in seinem Kommentar vorschlägt. Beispiel:

tail -c +1 -f /proc/12345/fd/5 > /var/tmp/file.log

Das zweite Problem besteht darin, dass der gesamte Dateiinhalt ( ln -s) oder ein Teil davon ( tail -c 1 -f) verloren geht, wenn der Prozess die Datei schließt oder beendet wird.

Eine Problemumgehung besteht darin, ein Programm zu verwenden, das dieses Ereignis überwacht und die Datei sichert, bevor „Close“ tatsächlich aufgerufen wird.

Geeignete Tools für diese Aufgabe sind wahrscheinlich dtrace, truss, mdb oder dbx.

Hier ist ein Proof of Concept mit dtrace unter Solaris 10.

#!/bin/ksh
#
# This dtrace script is monitoring a file descriptor for a given process
# and copy its content to the given path when the file is closed.
#

pid=${1:?"$0: Usage: pid fd path"}
fd=${2:?}
path=${3:?}
[[ -f $path ]] && { echo "$path exists"; exit 1; }
dtrace -w -n '
syscall::close:entry
/pid=='$pid' && arg0=='$fd'/
{
        stop();
        system("cp /proc/%d/fd/%d %s",pid,arg0,"'"$path"'");
        system("prun %d",pid);
        exit(0);
}'

verwandte Informationen