Come possiamo sapere chi c'è all'altra estremità di un dispositivo pseudo-terminale?


26

Se faccio un:

echo foo > /dev/pts/12

Qualche processo lo leggerà foo\ndal suo descrittore di file al lato master.

C'è un modo per scoprire cosa sono (sono) quel (quei) processo (i)?

O in altre parole, come potrei scoprire quale xterm / sshd / script / screen / tmux / prevedono / socat ... è all'altro capo di /dev/pts/12?

lsof /dev/ptmxmi dirà i processi che hanno descrittori di file sul lato master di qualsiasi pty. Un processo stesso può usare ptsname()( TIOCGPTNioctl) per scoprire il dispositivo slave basato sul proprio fd sul lato master, in modo da poter usare:

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

per ciascuno dei pid / fd restituiti lsofper creare quella mappatura, ma esiste un modo più diretto, affidabile e meno invadente per ottenere tali informazioni?


È questo che vuoi? sudo find /proc/*/fd/0 -ls | grep '/dev/pts/4', fornirebbe l'elenco di PID ( /proc/PID) come output.
slm

@slm, no, in altre parole, voglio scoprire quale xterm / sshd / script / screen / tmux / prevedono / socat ... è all'altro capo di /dev/pts/4. Di solito, sarà un antenato comune di quei processi che hanno /dev/pts/4aperto, ma non necessariamente.
Stéphane Chazelas,

1
È ancora peggio con i socket : è necessario un debugger del kernel!
Gilles 'SO- smetti di essere malvagio' il

1
@Falsenames: ho capito che la domanda significa - forse in modo errato - non quale processo viene passato ai dati letti - come la prima shell invocata nel terminale - ma quale processo legge effettivamente dal lato master. Ad esempio, se lancio una shell screen, è screenche alloca e gestisce attivamente lo slave pty per la vita del dispositivo, ma - come, penso - la shell diventa il leader del processo per quella tty e così, come il tuo l'output mostra, ottieni basho qualsiasi altra cosa da psno screen. Ho tracciato un paio xtermsdi nuovo al xtermPID sulla base /proc/locksma è stato sciolto.
Mikeserv,

Risposte:


3

All'inizio ho provato a risalire qualche xtermsecondo al xtermpid in base alle informazioni che ho trovato /proc/locksma era sciolto. Voglio dire, ha funzionato, penso, ma nella migliore delle ipotesi era circostanziale - non capisco completamente tutte le informazioni fornite dal file e corrispondevo solo a ciò che sembrava corrispondere tra il suo contenuto e i processi terminali noti.

Poi ho provato a guardare lsof/straceun write/talkprocesso attivo tra i pty. Non avevo mai usato nessuno dei due programmi prima, ma sembra che facciano affidamento utmp. Se il mio pty mirato non aveva una utmpvoce per qualsiasi motivo, entrambi si sono rifiutati di ammettere che esisteva. Forse c'è un modo per aggirare questo, ma ero abbastanza confuso da abbandonarlo.

Ho provato alcune udevadmscoperte con i nodi dei dispositivi con il numero maggiore di 136 e 128 come pubblicizzato ptse ptmrispettivamente in /proc/tty/drivers, ma mi manca anche qualche esperienza molto utile con quello strumento e ancora una volta non ho riscontrato nulla di sostanziale. È interessante notare, tuttavia, che ho notato che l' :minintervallo per entrambi i tipi di dispositivo era elencato in modo impressionante 0-1048575.

Non è stato fino a quando non ho rivisitato questo documento del kernel che ho iniziato a pensare al problema in termini di mounts, però. L'avevo letto diverse volte prima, ma quando la continua ricerca in quella linea mi ha portato a questo patchset del 2012,/dev/pts ho avuto un'idea:

sudo fuser -v /dev/ptmx

Ho pensato cosa dovrei usare di solito per associare i processi a un mount? E abbastanza sicuro:

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

Quindi con queste informazioni posso fare, ad esempio da terminology:

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

Come puoi vedere, con un test un po 'esplicito un simile processo potrebbe essere realizzato per produrre in modo abbastanza affidabile il processo principale di un pty arbitrario. Per quanto riguarda i socket, sono abbastanza sicuro che si possa avvicinarlo da quella direzione oltre a utilizzare socatun debugger, ma devo ancora chiarire come. Tuttavia, sospetto che sspotrebbe esserti d'aiuto se ne hai più familiarità di me:

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

Quindi l'ho impostato con un test un po 'più esplicito, in realtà:

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

Esso stampa $$num \0nulla byte per ciascun PTY e controlli io di ogni processo padrone contro un precedente controllo. Se la differenza è $$allora associa il pid al pty. Questo funziona principalmente . Voglio dire, per me, ritorna:

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

È corretto, ma, ovviamente, è un po 'audace. Voglio dire, se uno di quegli altri stesse leggendo un mucchio di dati in quel momento probabilmente non ci sarebbe. Sto cercando di capire come cambiare le sttymodalità su un altro pty per inviare prima il bit di stop o qualcosa del genere in modo da poterlo risolvere.


2

Se stai solo cercando chi possiede la connessione e da dove sono connessi, il comando who funzionerà bene.

$ 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)

Se anche voi volete sapere che cosa è in ascolto su quella connessione, w mostrerà che alla fine.

$ 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

E per ottenere i pids, limita un ps alla sessione tty che stai guardando. Completamente discreto per l'avvio.

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

Nota, questo può portare a aringhe rosse, a seconda dei tempi. Ma è un buon punto di partenza.

$ 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

Grazie, ma non è quello che sto cercando. Sopra, ad esempio, vorrei trovare il pid dell'applicazione terminale (Xterm / gnome-terminal ...) che corrisponde a /dev/pts/4dove hai eseguito quel wcomando.
Stéphane Chazelas,

Siamo spiacenti, ho perso completamente la parte pid quando ho scansionato la prima volta. Pensavo volessi solo sapere il nome del processo finale.
Falsenames,

2

Ho avuto lo stesso problema con qemu e alla fine ho trovato una pessima soluzione (ma comunque una soluzione): analizzare la memoria di processo.

Questo funziona qui perché so che qemu sta memorizzando i punti remoti in una stringa con un formato specifico e allocato sull'heap. Può essere che possa funzionare anche in altre situazioni con alcune modifiche e riutilizzando il pid dall'uscita del fusore (controlla altre risposte).

Il codice è adattato da qui .

#! /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()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.