1 Punkte von GN⁺ 2024-07-30 | 1 Kommentare | Auf WhatsApp teilen
  • izabera/ps ist eine Bash-Implementierung, die eine Ausgabe ähnlich zu ps aux innerhalb von Bash nachahmen soll, selbst in Situationen, in denen keine neuen Prozesse erstellt werden können
  • Die Kernbedingung ist, dass auf einer per ssh verbundenen Maschine zwar eine vertrauenswürdige bash shell vorhanden ist, aber alle anderen PIDs bereits belegt sind, sodass kein neuer Prozess erzeugt werden kann
  • Das README stellt diese Situation als Beispiel für eine Interviewfrage für Positionen vor, die Bash-/Linux-Kenntnisse erfordern
  • Das Tool wird so beschrieben, dass es einem erlaubt, so zu tun, „als hätte man Zugriff auf ein funktionierendes ps aux“, ohne eine vollständige Ersatzimplementierung zu garantieren
  • Die Aussage „funktioniert auf allen Maschinen und in allen Situationen zu 100 %“ ist offensichtlich als scherzhafte Garantie gemeint

Was macht das Projekt?

  • Es handelt sich um ein Projekt, das ps aux nur mit Bash implementiert
  • Der README-Titel lautet „ps aux written entirely in bash without ever forking“
  • Das zentrale Merkmal des Projekts ist, dass es während der Ausführung überhaupt keinen Fork verwendet

Das angenommene Szenario

  • Das Beispielszenario sieht so aus
    • Man ist per ssh auf einer Maschine eingeloggt
    • Der Nutzer befindet sich in einer vertrauten bash shell
    • Aber alle anderen PIDs sind bereits belegt, sodass sich überhaupt kein neuer Prozess erzeugen lässt
  • Das README erklärt, dass unter solchen Bedingungen eine ps aux-ähnliche Funktion nützlich sein kann

Erwartbarer Umfang und Caveat

  • Das Tool wird als etwas vorgestellt, mit dem man „kinda sorta pretend“ kann, man hätte ein funktionierendes ps aux
  • Die README-Formulierung „funktioniert perfekt in allen Situationen auf 100 % der Maschinen, garantiert“ ist als überzogener Humor gemeint
  • Der Kern der Beschreibung ist daher weniger vollständige Kompatibilität als vielmehr die Nachahmung von ps aux allein mit Bash in einer extremen Umgebung, in der keine neuen Prozesse erstellt werden können

1 Kommentare

 
GN⁺ 2024-07-30
Meinungen auf Hacker News
  • Der Witz, dass das schwierigste Problem der Informatik am Ende doch Ausrichtung ist, trifft einen Nerv.
    Ich habe unzählige Male Funktionen zur Spaltenausrichtung in verschiedenen Sprachen geschrieben, und jedes Mal war es schmerzhaft. Im Kopf wirkt es so simpel wie: „Bestimme die maximale Länge jeder Spalte und füge Leerzeichen bis zum nächsten Vielfachen der Tabulatorgröße ein.“
    Selbst mit Pythons f-Strings und Padding-Funktionen wird der Code schnell kompliziert und schwer lesbar; während ich ein Beispiel für einen Kommentar neu schrieb, habe ich sogar mehrere Bugs behoben — so furchtbar ist das.

    • Ich habe einem Projekt schon einmal Pandas hinzugefügt, nur weil ich solchen Code für hübsche Tabellen nicht selbst schreiben wollte.
      Für einen so häufigen Anwendungsfall sollte es doch selbstverständlich eine Library geben; ehrlich gesagt überrascht es mich, dass das nicht in der Standardbibliothek enthalten ist.
    • https://perldoc.perl.org/perlform
    • Vor einiger Zeit habe ich auf Stack Overflow einmal mit einer O(n)-Lösung geantwortet: https://stackoverflow.com/questions/10865483/print-results-i...
      Dabei werden aus description eines Datenbank-Cursors Spaltenbreiten und Namen entnommen, daraus Trennlinien und Formatstrings erzeugt und anschließend die Zeilen ausgegeben. Ich weiß nicht, ob ich dabei einen gravierenden Bug übersehen habe, aber es wirkt nicht wie ein besonders schwieriges Problem.
    • Noch einfacher geht es, indem man mit zip(*table) die Tabelle nach Spalten dreht, die maximale Länge jeder Spalte bestimmt und dann mit f"{r:<{w}}" ausgerichtet ausgibt.
      Das Beispielergebnis wird dann eine Tabelle mit passenden Spaltenbreiten, etwa agony | kick | pump.
    • Umgekehrt ist das auch nicht einfach, wenn man häufig spaltenausgerichtete Daten parsen muss.
      Werte enthalten Leerzeichen, sind mit Leerzeichen gepaddet, manchmal ist die Ausrichtung verschoben und Spalten laufen über.
      Es wäre wohl für alle besser, wenn man sich einfach darauf einigt, keine spaltenausgerichteten Daten zu verwenden, sondern ein einfacheres, menschenlesbares Format.
  • Wenn ich per SSH auf einer Maschine bin, die Bash-Shell noch lebt, aber alle PIDs aufgebraucht sind und keine neuen Prozesse mehr erzeugt werden können, würde ich das Dateisystem /proc/[pid]/ durchsuchen, um herauszufinden, welcher Prozess den PID-Raum erschöpft.
    kill in Bash ist ein Shell-Builtin und muss daher im Gegensatz zu /bin/kill keinen neuen Prozess forken.
    Wenn man den Elternprozess findet, der die Kinder erzeugt, die die PIDs aufbrauchen, kann man ihn stoppen und die Kontrolle über das System zurückgewinnen.
    Dieses Skript parst ebenfalls /proc und ist ziemlich sauber, weil es weder Pipes noch $(...)-Substitutionen verwendet, die eine neue Bash-Subshell erzeugen könnten.

    • In einem Vorstellungsgespräch habe ich einmal mit „exec Python“ geantwortet.
      Damit kann man die nötigen POSIX-Funktionen aufrufen, ohne einen separaten Befehl auszuführen, und das kam gut an.
    • Ehrlich gesagt würde ich wohl einfach neu starten.
      Das System nach einem Reboot wiederherzustellen kann schneller sein, als in einer eingeschränkten Umgebung den Elternprozess zu finden und zu töten; wenn die PIDs ausgegangen sind, ist wahrscheinlich auch anderes bereits in einem schlechten Zustand.
    • Wenn man nur PID und Befehlsnamen sehen will, geht es in nahezu minimaler Form auch so: ps(){ (cd /proc;for i in [0-9]*;do echo $i: $(tr '\0' ' ' < $i/cmdline);done); }
    • Der Gedanke, in /proc/[pid]/ nachzusehen, welcher Prozess den PID-Raum erschöpft, ist richtig; den Kommentaren im Quellcode zufolge hatte man anfangs aber gehofft, dass allein /proc/*/status ausreichen würde, doch Werte wie die CPU-Auslastung lassen sich dort nicht bekommen.
    • Im Zusammenhang mit Subprozessen bin ich wirklich neugierig, wie [[ $cmdline ]] && exec {cmdline}>&- und exec {cmdline}< "$dir"/cmdline || continue funktionieren.
  • 2011 hatte ich ein Vorstellungsgespräch für eine SRE-Rolle bei einem recht großen US-Tech-Unternehmen; damals hörte ich den Begriff SRE zum ersten Mal.
    Das Unternehmen arbeitete an einer browserbasierten Alternative zu MS Office, und nach dem Telefonscreening musste ich während eines Anrufs mit dem Interviewer live in deren Dokumenteneditor programmieren.
    Weil ich mich in der Selbsteinschätzung bei shell scripting und Linux hoch eingestuft hatte, bekam ich die Aufgabe, in Bash einen Ersatz für netstat zu bauen. Damals wusste ich aber nicht, wo und wie Socket-Informationen unter /proc/ liegen, und entschied schnell, dass ich das nicht hinbekommen würde.
    Stattdessen schlug ich vor, abgespeckte Versionen von ps und fuser zu bauen, und meine Lösung in diesem furchtbaren browserbasierten Textverarbeitungsprogramm wurde akzeptiert, sodass ich es bis zum Onsite-Interview schaffte.
    Rückblickend könnte das hypothetische Szenario, das diese Übung motivierte, stärker in der Realität verwurzelt gewesen sein, als ich dachte.

    • Einige dieser System-Utilities schauen intern wahrscheinlich ohnehin in procfs/sysfs nach.
      Ich würde auch dort anfangen.
  • Ich habe früher zum Spaß eine interaktive Website gebaut, auf der man eine Situation erkunden kann, in der man per SSH eingeloggt ist, aber keine neuen Prozesse erstellen kann: https://oops.cmdchallenge.com

    • "echo *" listet nicht alle Dateien in einem Verzeichnis auf.
      Man muss "echo .* *" verwenden.
    • Es ist cool, aber nachdem ich es geschafft hatte, wurde ich wieder zur ersten Stufe zurückgeworfen und konnte die anderen Stufen nicht sehen, was frustrierend war.
      Ich wollte mir die Listen mit "View Solutions" für andere Stufen ansehen, um zu sehen, welche anderen Ansätze möglich gewesen wären.
  • Izabera ist einer der Könner in #bash@libera.
    Von solchen Leuten habe ich seit den alten freenode-Zeiten in den letzten zehn Jahren wirklich viel gelernt.

  • Das ist ziemlich sauberes Bash.
    Meiner Erfahrung nach ist Bash-Code oft schlecht geschrieben und ineffizient; dieser Code wirkt wie ein gutes Gegenbeispiel.

    • Sauberes Bash sollte auch portabel sein, aber dieses Skript ist Linux-only und wird anderswo massiv kaputtgehen.
  • Was macht man, wenn man in einer verlässlichen POSIX-Shell ohne Bash-Unterstützung landet?
    Dieses Bash-Skript ist nicht POSIX-kompatibel.

  • Dieses Skript läuft nicht unter Bash 3.2, funktioniert aber mit Bash 4.2.
    Unter Bash 3.2 erscheint der Fehler printf: '(': invalid format character; die Beispielumgebung ist bash-3.2-33.el5_11.4.0.1.

    • Es ist völlig nachvollziehbar, eine 18 Jahre alte Bash-Release-Linie und eine 17 Jahre alte Betriebssystem-Release-Linie nicht zu unterstützen.
  • Ein besserer Einsatzzweck wäre wohl, auf Systemen ohne installiertes procps eine Prozessliste anzuzeigen.
    Passt schon.

  • Mit Bash kann man auch Listener und Clients bauen.
    Für die Praxis würde ich es nicht empfehlen.