ini
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Die geschrieben Logs sollen nicht mit geschrieben werden
|
||||
*.log
|
||||
49
README.md
Normal file
49
README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# TTY Analyser
|
||||
|
||||
Dieses Projekt enthält zwei einfache Python-Skripte, um Terminal-Sitzungen aufzuzeichnen und live anzuzeigen.
|
||||
|
||||
## Komponenten
|
||||
|
||||
1. **`pty_monitor.py`**: Startet eine neue Shell-Sitzung und zeichnet alle Ein- und Ausgaben in einer Log-Datei auf.
|
||||
2. **`log_viewer.py`**: Zeigt den Inhalt einer Log-Datei an und aktualisiert die Ansicht automatisch, wenn neue Inhalte hinzukommen (ähnlich wie `tail -f`).
|
||||
|
||||
## Anwendung
|
||||
|
||||
Die Anwendung ist in zwei Schritte aufgeteilt: das Aufzeichnen der Sitzung und das Anzeigen der Aufzeichnung.
|
||||
|
||||
### 1. Sitzung aufzeichnen
|
||||
|
||||
Um eine Terminal-Sitzung aufzuzeichnen, führen Sie `pty_monitor.py` aus.
|
||||
|
||||
```bash
|
||||
python3 pty_monitor.py
|
||||
```
|
||||
|
||||
- Das Skript startet eine neue `zsh`-Shell.
|
||||
- Alle Befehle, die Sie eingeben, und deren Ausgaben werden in einer Log-Datei gespeichert.
|
||||
- Der Name der Log-Datei wird automatisch generiert, z.B. `username-ttyX.log`.
|
||||
- Um die Aufzeichnung zu beenden, geben Sie `exit` in der aufgezeichneten Shell ein.
|
||||
|
||||
### 2. Aufzeichnung ansehen
|
||||
|
||||
Öffnen Sie ein **zweites Terminal**, um die Log-Datei live zu verfolgen.
|
||||
|
||||
```bash
|
||||
python3 log_viewer.py <name-der-log-datei>.log
|
||||
```
|
||||
|
||||
Ersetzen Sie `<name-der-log-datei>.log` mit dem Dateinamen, der beim Start von `pty_monitor.py` angezeigt wurde (z.B. `jonnybravo-tty7.log`).
|
||||
|
||||
- Das Skript zeigt den aktuellen Inhalt der Log-Datei an.
|
||||
- Alle neuen Eingaben aus der aufgezeichneten Sitzung erscheinen sofort im Viewer.
|
||||
- Sie können den Viewer jederzeit mit `Strg+C` beenden.
|
||||
|
||||
## Details zu den Skripten
|
||||
|
||||
### `pty_monitor.py`
|
||||
|
||||
Dieses Skript verwendet das `pty`-Modul von Python, um eine Pseudo-Terminal-Sitzung zu erstellen. Es agiert als Vermittler zwischen Ihrer Tastatur, der gestarteten Shell (`/bin/zsh`) und der Log-Datei. Steuerzeichen und Escape-Sequenzen werden aus den Ausgaben entfernt, um die Log-Dateien sauber und lesbar zu halten.
|
||||
|
||||
### `log_viewer.py`
|
||||
|
||||
Ein einfaches "Follow"-Skript, das eine Datei öffnet und am Ende der Datei auf neue Zeilen wartet. Sobald eine neue Zeile in die Datei geschrieben wird, liest das Skript sie und gibt sie auf der Konsole aus.
|
||||
40
log_viewer.py
Normal file
40
log_viewer.py
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
|
||||
def follow_file(filename):
|
||||
"""Liest eine Datei und gibt neue Zeilen aus, sobald sie hinzugefügt werden."""
|
||||
print(f"Beobachte Datei: '{filename}'. Beenden mit Strg+C.")
|
||||
|
||||
# Warte, bis die Datei existiert
|
||||
while not os.path.exists(filename):
|
||||
print(f"Warte auf die Erstellung von '{filename}'...", end='\r')
|
||||
time.sleep(1)
|
||||
|
||||
try:
|
||||
with open(filename, 'r', encoding='utf-8', errors='ignore') as file_:
|
||||
# Gehe zum Ende der Datei, falls sie schon Inhalt hat
|
||||
# file_.seek(0, 2)
|
||||
|
||||
while True:
|
||||
line = file_.readline()
|
||||
if not line:
|
||||
# Wenn keine neue Zeile da ist, kurz warten
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
# Neue Zeile gefunden, ausgeben
|
||||
print(line, end='')
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"Fehler: Datei '{filename}' wurde nicht gefunden.")
|
||||
except KeyboardInterrupt:
|
||||
print("\nViewer beendet.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Benutzung: python3 {sys.argv[0]} <dateipfad>")
|
||||
sys.exit(1)
|
||||
|
||||
log_file_path = sys.argv[1]
|
||||
follow_file(log_file_path)
|
||||
96
pty_monitor.py
Normal file
96
pty_monitor.py
Normal file
@@ -0,0 +1,96 @@
|
||||
import sys
|
||||
import os
|
||||
import pty
|
||||
import tty
|
||||
import termios
|
||||
import select
|
||||
import re
|
||||
import getpass
|
||||
import datetime
|
||||
import itertools
|
||||
|
||||
|
||||
def get_log_filename():
|
||||
"""Ermittelt einen Dateinamen basierend auf Username und TTY-Nummer."""
|
||||
try:
|
||||
username = getpass.getuser()
|
||||
except Exception:
|
||||
username = "user"
|
||||
|
||||
try:
|
||||
tty_path = os.ttyname(sys.stdout.fileno())
|
||||
tty_number = os.path.basename(tty_path)
|
||||
filename = f"{username}-tty{tty_number}.log"
|
||||
except OSError:
|
||||
for i in itertools.count():
|
||||
filename = f"{username}-tty{i}.log"
|
||||
if not os.path.exists(filename):
|
||||
break
|
||||
return filename
|
||||
|
||||
|
||||
def sanitize_output(data: bytes) -> bytes:
|
||||
"""Entfernt Steuerzeichen und -sequenzen, um die Log-Datei lesbarer zu machen."""
|
||||
data = re.sub(
|
||||
rb'\x1b(\[[0-?]*[ -/]*[@-~]|\][^]*(\x07|\x1b\\))', b'', data)
|
||||
data = data.replace(b'\r', b'')
|
||||
data = data.replace(b'\b', b'')
|
||||
data = data.replace(b'\a', b'')
|
||||
return data
|
||||
|
||||
|
||||
def run_interactive_shell():
|
||||
log_filename = get_log_filename()
|
||||
print(f"Sitzung wird in '{log_filename}' aufgezeichnet.")
|
||||
|
||||
file_existed = os.path.exists(log_filename)
|
||||
original_termios = termios.tcgetattr(sys.stdin.fileno())
|
||||
|
||||
try:
|
||||
# Öffne die Datei im 'append binary'-Modus ('ab')
|
||||
with open(log_filename, 'ab') as log_file:
|
||||
if file_existed:
|
||||
# Füge einen Trenner für die neue Sitzung hinzu
|
||||
timestamp = datetime.datetime.now().isoformat()
|
||||
separator = f"\n\n--- NEUE SITZUNG GESTARTET AM {
|
||||
timestamp} ---\n\n".encode('utf-8')
|
||||
log_file.write(separator)
|
||||
|
||||
pid, master_fd = pty.fork()
|
||||
|
||||
if pid == 0:
|
||||
os.execv("/bin/zsh", ["/bin/zsh"])
|
||||
else:
|
||||
tty.setraw(sys.stdin.fileno())
|
||||
|
||||
while True:
|
||||
try:
|
||||
readable, _, _ = select.select(
|
||||
[sys.stdin, master_fd], [], [])
|
||||
|
||||
if sys.stdin in readable:
|
||||
user_input = os.read(sys.stdin.fileno(), 1024)
|
||||
if not user_input:
|
||||
break
|
||||
log_file.write(sanitize_output(user_input))
|
||||
os.write(master_fd, user_input)
|
||||
|
||||
if master_fd in readable:
|
||||
shell_output = os.read(master_fd, 1024)
|
||||
if not shell_output:
|
||||
break
|
||||
log_file.write(sanitize_output(shell_output))
|
||||
os.write(sys.stdout.fileno(), shell_output)
|
||||
sys.stdout.flush()
|
||||
|
||||
except OSError:
|
||||
break
|
||||
finally:
|
||||
termios.tcsetattr(sys.stdin.fileno(),
|
||||
termios.TCSADRAIN, original_termios)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Starte PTY-Monitor. Gib 'exit' ein, um die Sitzung zu beenden.")
|
||||
run_interactive_shell()
|
||||
print(f"\nPTY-Monitor beendet.")
|
||||
Reference in New Issue
Block a user