Woher wissen wir, wer sich am anderen Ende eines Pseudo-Endgeräts befindet?

26

Wenn ich ein mache:

echo foo > /dev/pts/12

Einige Prozesse lesen das foo\nvon ihrem Dateideskriptor zur Masterseite.

Gibt es eine Möglichkeit herauszufinden, was diese Prozesse sind?

Oder mit anderen Worten, wie kann ich herausfinden, welches xterm / sshd / script / screen / tmux / expect / socat ... am anderen Ende von ist /dev/pts/12?

lsof /dev/ptmxIch erfahre, welche Prozesse auf der Masterseite eines Pty über Dateideskriptoren verfügen. Ein Prozess selbst kann ptsname()( TIOCGPTNioctl) verwenden, um das Slave-Gerät basierend auf seiner eigenen FD für die Masterseite zu ermitteln. Ich könnte also Folgendes verwenden:

gdb --batch --pid "$the_pid" -ex "print ptsname($the_fd)"

Gibt lsofes eine direktere, zuverlässigere und weniger aufdringliche Möglichkeit, an diese Informationen zu gelangen, wenn jedes PID / FD von zurückgegeben wird , um diese Zuordnung zu erstellen?

Stéphane Chazelas
quelle
Ist das was du willst? sudo find /proc/*/fd/0 -ls | grep '/dev/pts/4', würde die Liste der PIDs ( /proc/PID) als Ausgabe bereitstellen .
slm
@slm, nein, mit anderen Worten, ich möchte herausfinden, welches xterm / sshd / script / screen / tmux / expect / socat ... sich am anderen Ende von befindet /dev/pts/4. Normalerweise ist dies ein gemeinsamer Vorfahr jener Prozesse, die /dev/pts/4offen sind, aber nicht unbedingt.
Stéphane Chazelas
1
Bei Sockets ist es noch schlimmer - Sie benötigen einen Kernel-Debugger!
Gilles 'SO- hör auf böse zu sein'
1
@Falsenames - Ich habe verstanden, dass die Frage - vielleicht falsch - nicht bedeutet, welcher Prozess den gelesenen Daten übergeben wird - wie die erste im Terminal aufgerufene Shell -, sondern welcher Prozess sie tatsächlich von der Masterseite liest. Zum Beispiel, wenn ich eine Schale in starten screen, ist es , screendass zuordnet und verwaltet aktiv die pty Slave für die Lebensdauer des Gerätes, sondern - wie ich glaube - die Schale gemacht wird , den Prozess-Führer für das tty und so, wie Ihre ausgangsshows bekommst du bashoder was auch immer psnicht ab screen. Ich habe ein paar davon xtermsauf die xtermPID zurückverfolgt, /proc/locksaber sie war locker.
mikeserv

Antworten:

3

Zuerst habe ich versucht, ein paar Sekunden xtermzu der xtermPID zurückzuverfolgen, basierend auf den Informationen, die ich in gefunden habe, /proc/locksaber sie war locker. Ich meine, es hat funktioniert, denke ich, aber es war im besten Fall ein finanzieller Umstand - ich verstehe nicht alle Informationen, die die Datei bietet, und stimme nur mit den scheinbaren Übereinstimmungen zwischen ihrem Inhalt und bekannten Terminalprozessen überein.

Dann habe ich versucht, lsof/straceeinen aktiven write/talkProzess zwischen ptys zu beobachten. Ich hatte noch nie ein Programm benutzt, aber sie scheinen sich darauf zu verlassen utmp. Wenn meine Zielgruppe utmpaus irgendeinem Grund keinen Eintrag hatte, wollten sie beide nicht zugeben, dass er existiert. Vielleicht gibt es einen Ausweg, aber ich war verwirrt genug, es aufzugeben.

Ich habe versucht , eine udevadmEntdeckung mit 136 und 128 Hauptknoten Nummer Gerät wie in der Werbung für ptsund ptmjeweils in /proc/tty/drivers, aber mir fehlt auch jede sehr nützliche Erfahrungen mit diesem Werkzeug und wieder nichts Wesentliches aufgedreht. Interessanterweise bemerkte ich jedoch, dass der :minBereich für beide Gerätetypen Staffelungen aufwies 0-1048575.

Erst als ich dieses Kernel-Dokument erneut besuchte , begann ich, über das Problem in Bezug auf mounts nachzudenken . Ich hatte das schon mehrmals gelesen, aber als mich die weitere Forschung in dieser Zeile zu diesem Patchset von 2012 führte, hatte/dev/pts ich eine Idee:

sudo fuser -v /dev/ptmx

Ich dachte, was verwende ich normalerweise, um Prozesse mit einem zu verknüpfen mount? Und sicher genug:

                     USER        PID ACCESS COMMAND
/dev/ptmx:           root      410   F.... kmscon
                     mikeserv  710   F.... terminology

Mit diesen Informationen kann ich zum Beispiel Folgendes tun terminology:

sudo sh -c '${cmd:=grep rchar /proc/410/io} && printf 1 >/dev/pts/0 && $cmd'
###OUTPUT###
rchar: 667991010
rchar: 667991011

Wie Sie sehen, könnte mit einem kleinen expliziten Test ein solcher Prozess dazu gebracht werden, den Master-Prozess eines beliebigen Pty ziemlich zuverlässig auszugeben. In Bezug auf die Sockets bin ich mir ziemlich sicher, dass man es auch aus dieser Richtung angehen kann, socatim Gegensatz zu einem Debugger, aber ich muss noch klären, wie. Ich vermute jedoch, dass sses hilfreich sein könnte, wenn Sie damit besser vertraut sind als ich:

sudo sh -c 'ss -oep | grep "$(printf "pid=%s\n" $(fuser /dev/ptmx))"'

Also habe ich es mit ein wenig expliziterem Testen eingerichtet:

sudo sh <<\CMD
    chkio() {
        read io io <$1
        dd bs=1 count=$$ </dev/zero >$2 2>/dev/null
        return $((($(read io io <$1; echo $io)-io)!=$$))
    }
    for pts in /dev/pts/[0-9]* ; do
        for ptm in $(fuser /dev/ptmx 2>/dev/null)
            do chkio /proc/$ptm/io $pts && break
        done && set -- "$@" "$ptm owns $pts"
    done
    printf %s\\n "$@"
 CMD

Er druckt $$num \0Null - Bytes zu jedem pty und prüft jeden IO - Master - Prozess gegen eine vorherige Überprüfung. Wenn der Unterschied $$dann ist, ordnet es das pid dem pty zu. Das funktioniert meistens . Ich meine, für mich kehrt es zurück:

410 owns /dev/pts/0
410 owns /dev/pts/1
710 owns /dev/pts/2

Was richtig ist, aber offensichtlich ist es ein bisschen rassig. Ich meine, wenn einer dieser anderen zu der Zeit ein paar Daten einliest, würde es wahrscheinlich fehlen. Ich versuche herauszufinden, wie man die sttyModi auf einem anderen Pty ändert, um zuerst das Stoppbit oder so etwas zu senden, damit ich das beheben kann.

mikeserv
quelle
2

Wenn Sie nur suchen, wem die Verbindung gehört und woher sie stammt, funktioniert der Befehl who gut.

$ who
falsenames   tty8         Jun 13 16:54 (:0)
falsenames   pts/0        Jun 16 11:18 (:0)
falsenames   pts/1        Jun 16 12:59 (:0)
falsenames   pts/2        Jun 16 13:46 (:0)
falsenames   pts/3        Jun 16 14:10 (:0)
falsenames   pts/4        Jun 16 16:41 (:0)

Wenn Sie auch wissen möchten, was diese Verbindung abhört, wird dies am Ende angezeigt.

$ w
 16:44:09 up 2 days, 23:51,  6 users,  load average: 0.26, 0.98, 1.25
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
falsenames   tty8     :0               Fri16    2days 53:36   0.59s x-session-manager
falsenames   pts/0    :0               11:18    5:25m  1:10   1:10  synergys -a 10.23.8.245 -c .synergy.conf -f -d DEBUG
falsenames   pts/1    :0               12:59    3:44m  0.05s  0.05s bash
falsenames   pts/2    :0               13:46    2:52m  0.11s  0.11s bash
falsenames   pts/3    :0               14:10    2:17   0.07s  0.07s bash
falsenames   pts/4    :0               16:41    1.00s  0.04s  0.00s w

Und um die Pids zu erhalten, beschränken Sie einen Ps auf die Tty-Sitzung, die Sie sich ansehen. Ganz unauffällig zu booten.

$ ps -t pts/0 --forest 
  PID TTY          TIME CMD
23808 pts/0    00:00:00 bash
23902 pts/0    00:03:27  \_ synergys

Beachten Sie, dass dies je nach Zeitpunkt zu roten Heringen führen kann. Aber es ist ein guter Anfang.

$ tty
/dev/pts/4
$ ps -t pts/4 --forest
  PID TTY          TIME CMD
27479 pts/4    00:00:00 bash
 3232 pts/4    00:00:00  \_ ps
27634 pts/4    00:00:00 dbus-launch
Falsche Namen
quelle
Danke, aber das ist nicht das, wonach ich suche. Oben möchte ich zum Beispiel die pid der Terminal-Anwendung (Xterm / gnome-terminal ...) finden, zu der /dev/pts/4Sie diesen wBefehl ausgeführt haben.
Stéphane Chazelas
Entschuldigung, ich habe den PID-Teil beim ersten Scannen komplett verpasst. Ich dachte, Sie wollten nur den Namen des Endprozesses wissen.
Falsenames
2

Ich hatte das gleiche Problem mit qemu und fand schließlich eine sehr schlechte Lösung (aber immer noch eine Lösung): das Parsen des Prozessspeichers.

Dies funktioniert hier, weil ich weiß, dass qemu die Remote-PTS in einer Zeichenfolge mit einem bestimmten Format speichert und auf dem Heap reserviert. Möglicherweise funktioniert es auch in anderen Situationen mit ein paar Änderungen und durch Wiederverwenden der PID aus der Fixierausgabe (andere Antwort überprüfen).

Der Code wird von hier aus angepasst .

#! /usr/bin/env python

import sys
pid = sys.argv[1]

import re
maps_file = open("/proc/" + pid + "/maps", 'r')
mem_file = open("/proc/" + pid + "/mem", 'r', 0)
for line in maps_file.readlines():
    # You may want to remove the 'heap' part to search all RAM
    m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r]).*\[heap\]', line)
    if m and m.group(3) == 'r':
        start = int(m.group(1), 16)
        end = int(m.group(2), 16)
        mem_file.seek(start)
        chunk = mem_file.read(end - start)
        # You may want to adapt this one to reduce false matches
        idx = chunk.find("/dev/pts/")
        if idx != -1:
            end = chunk.find("\0", idx)
            print chunk[idx:end]
maps_file.close()
mem_file.close()
calandoa
quelle