Come ottenere il vero nome del terminale di controllo?


13

Come si può ottenere il vero nome del terminale di controllo (se ce n'è uno, altrimenti un errore) come percorso?

Con "nome reale", intendo di no /dev/tty, che non può essere utilizzato da altri processi arbitrari per fare riferimento allo stesso terminale. Preferisco la risposta come un semplice codice shell (come nell'esempio sotto) se possibile, altrimenti come una funzione C.

Si noti che questo deve funzionare anche se viene reindirizzato l'input standard, in modo che l' ttyutilità non possa essere utilizzata: si otterrebbe un not a ttyerrore in tal caso, poiché ttystampa semplicemente il nome del file del terminale collegato all'input standard.

Sotto Linux, si può usare:

echo "/dev/`ps -p $$ -o tty | tail -n 1`"

ma questo non è portatile, come secondo POSIX, il formato del nome del terminale non è specificato .

Per quanto riguarda le funzioni C, i ctermid (NULL)ritorni /dev/tty, che è inutile qui.

Nota: secondo la zshdocumentazione, si dovrebbe essere in grado di fare

zsh -c 'echo $TTY'

ma questo attualmente (versione 5.0.7) non riesce quando vengono reindirizzati sia lo standard input che lo standard output:

$ zsh -c 'echo $TTY > /dev/tty' < /dev/null
/dev/pts/9
$ zsh -c 'echo $TTY > /dev/tty' < /dev/null > /dev/null
/dev/tty

@mikeserv Penso che la pssoluzione copra la maggior parte dei sistemi (e whonon aiuta più di ps), possibilmente con un po 'più di codice per gestire l'identificatore da solo (come "04"). Mi chiedevo se esistesse una soluzione ancora più portatile.
vinc17,

Potrebbe avere a che fare con set pre-abbinati - forse anche le vecchie coppie pty in stile bsd . Non tutti i pty sono tipi UNIX 98. Ad ogni modo, da man xterm: -Sccn Questa opzione consente xtermdi essere utilizzata come canale I / O per un programma esistente ... Il valore dell'opzione è costituito da alcune lettere del nome di un pty da utilizzare in modalità slave, oltre al numero fd ereditato. Se l'opzione contiene un carattere "/", delimita il nome pty da fd.
Mikeserv,

@mikeserv Si noti che la soluzione non funziona con psda busybox (che viene utilizzato da Android, BTW), anche sotto GNU / Linux. Cosa intendi con "in xtermgrado di gestirlo 04"?
vinc17,

busyboxnon è conforme a POSIX. toybox, tuttavia, fa molto bene.
Mikeserv,

Risposte:


8

Il "terminale di controllo" aka. CTTY, è contraddistinta da " il processo di un terminale interagisce con".

Il modo standard per ottenere il percorso di ctty è ctermid (3). Chiamandolo, In freebsd dalla versione 10, viene cercato un percorso effettivo [1], mentre le implementazioni più vecchie di freebsd e glibc [2] restituiscono incondizionatamente "/ dev / tty"].

ps (1) dal pacchetto linux procps 3.2.8, leggi la voce numerica in / proc / * / stat [3], quindi deduci parzialmente il nome del percorso indovinando [4, 5] a causa della mancanza di supporto del sistema [6] .

Tuttavia, se non siamo strettamente interessati a ctty ma a qualsiasi terminale associato a stdio, tty (1) stampa il percorso terminale collegato a stdin, che è identico a ttyname(fileno(stdin))in c, e un'alternativa è readlink /proc/self/fd/0.


Pensiero meno importante riguardo al comportamento incondizionato "/ dev / tty": Le specifiche dicono semplicemente che la stringa restituita da ctermid "quando usata come nome di percorso, fare riferimento al terminale di controllo corrente", invece di un semplice "è il nome del percorso della corrente terminale di controllo ". Potrebbe essere interpretato come "/ dev / tty" non è il terminale di controllo, ma si riferisce al terminale di controllo solo se lo stesso processo lo apre (3). Pertanto, non violare la regola "un terminale può essere ctty al massimo per una sessione" [7].

Un'altra conseguenza è che quando sono senza alcun terminale di controllo, ctermid non fallisce - tale fallimento è consentito dalle specifiche [8] -, quindi solo posso prendere coscienza della mia mancanza di ctty fino a quando fallisco una successiva apertura (3), il che va bene poiché le specifiche dicono anche che chiamare open (3) non è garantito per avere successo.


Questo non è più portatile della pssoluzione che ho dato alla mia domanda, poiché non tutti i sistemi operativi hanno un /procfile system. Si noti che esso psstesso utilizza un readlink su /proc/self/fd/2(che funziona anche se l'errore standard viene reindirizzato).
vinc17,

1
modificato. e ps readlink su / proc / * / fd / 2 non per trovare il ctty, ma per cercare informazioni supplementari per mappare il terminale numerico sul percorso, vedi link [4] [5].
把 友情 留 在 无 盐

1
Modifica eccellente. Per quanto riguarda il ctty; Non posso parlare per vinc17, ma mentre probabilmente puoi sempre scrivere da qualche parte, c'è solo un file che deve rimanere aperto per mantenere in vita il tuo gruppo di processi.
Mikeserv,

1
@ vinc17 - se si dispone di qualsiasi file di descrittori aprono sul CTTY poi si può leggere con tty. stderrè probabilmente il migliore perché dovrebbe essere aperto r / w. Così tty <&2.
Mikeserv,

1
Che un determinato terminale possa essere ctty al massimo per una sessione non rende glibc non conforme per il suo ctermid()ritorno sempre "/dev/tty". Tale nome si riferisce sempre al terminale di controllo del processo che accede ad esso , che varia in base alla sessione. Il terminale è specifico della sessione, ma non è necessario che il nome con cui si accede.
PellMel,

5

Le specifiche POSIX coprono davvero le sue scommesse per quanto riguarda il Terminale di controllo , e che definisce così:

  • Terminale di controllo
    • La questione di quale dei diversi file speciali che si riferiscono al terminale potrebbe non essere affrontata in POSIX.1. Il nome percorso /dev/ttyè sinonimo del terminale di controllo associato a un processo.

È nell'elenco delle definizioni e non c'è altro. Ma in General Terminal Interface , si dice ancora:

  • Un terminale può appartenere a un processo come suo terminale di controllo. Ogni processo di una sessione che ha un terminale di controllo ha lo stesso terminale di controllo. Un terminale può essere il terminale di controllo per al massimo una sessione. Il terminale di controllo per una sessione è assegnato dal leader della sessione in un modo definito dall'implementazione. Se un leader di sessione non ha un terminale di controllo e apre un file del dispositivo terminale che non è già associato a una sessione senza utilizzare l'opzione O_NOCTTY (vedere open ()), viene definito dall'implementazione se il terminale diventa il terminale di controllo della sessione capo.

  • Il terminale di controllo viene ereditato da un processo figlio durante una chiamata della funzione fork (). Un processo abbandona il suo terminale di controllo quando crea una nuova sessione con ilsetsid()funzione; altri processi rimanenti nella vecchia sessione che aveva questo terminale poiché il suo terminale di controllo continua ad averlo. Alla chiusura dell'ultimo descrittore di file nel sistema (indipendentemente dal fatto che sia o meno nella sessione corrente) associato al terminale di controllo, non è specificato se tutti i processi che avevano quel terminale come terminale di controllo cessano di avere un terminale di controllo. Non è specificato se e come un leader di sessione possa riacquistare un terminale di controllo dopo che il terminale di controllo è stato abbandonato in questo modo. Un processo non abbandona il suo terminale di controllo semplicemente chiudendo tutti i suoi descrittori di file associati al terminale di controllo se altri processi continuano ad averlo aperto.

Ne rimangono molte cose non specificate - e onestamente penso che abbia senso. Mentre il terminale è un'interfaccia utente chiave, in alcuni casi è anche un sacco di altre cose - come l'hardware reale o persino un tipo di stampante - ma in molti casi non è praticamente nulla - come un xtermche è solo un emulatore . È difficile essere specifici lì - e non penso che sarebbe comunque nell'interesse di Unix, perché i terminali fanno molto di più di Unix.

Comunque, POSIX è anche piuttosto incerto su come psdovrebbe comportarsi per quanto riguarda il ctty.

C'è l' -ainterruttore:

  • Scrivi informazioni per tutti i processi associati ai terminali. Le implementazioni possono omettere i leader di sessione da questo elenco.

Grande. I leader della sessione possono essere omessi. Non è molto utile.

E -t:

  • Scrivi informazioni per i processi associati ai terminali indicati nella termlist. La domanda deve garantire che l'elenco di termini sia un singolo argomento sotto forma di un <blank>elenco separato da virgole. Gli identificativi dei terminali devono essere forniti in un formato definito dall'implementazione .

... che è un'altra delusione. Ma continua dicendo questo sui sistemi XSI:

  • Sui sistemi conformi a XSI, devono essere indicati in una delle due forme: il nome del file del dispositivo (ad esempio, tty04) o, se il nome del dispositivo inizia con tty, solo l'identificatore che segue i caratteri tty (ad esempio 04) .

È un po 'meglio, ma non è un percorso. Anche sui sistemi XSI c'è lo -dswitch:

  • Scrivi informazioni per tutti i processi, tranne i leader della sessione.

... che è almeno chiaro. Puoi specificare anche l' -output switch con la ttystringa di formato, ma, come hai notato, il suo formato di output è definito dall'implementazione. Tuttavia, penso che sia bello come si arriva. Penso che - con un sacco di lavoro - gli interruttori di cui sopra in combinazione con alcune altre utility possano offrirti un campo da baseball abbastanza buono. Ad essere sincero, però, non so quando / come si rompe per te - e non sono stato in grado di immaginare una situazione in cui sarebbe. Ma probabilmente penso che aggiungiamo fusere findpossiamo verificare il percorso.

exec 2<>/dev/null
ctty=$(sh -c 'ps -p "$$" -o tty=' <&2)
sid=$(sh -c 'ps -Ao pid= -o tty=|
      grep '"$ctty$"' | 
      grep -Fv "$(ps -do pid=)"'  <&2)
find / -type c -name "*${ctty##*/}*" \
       -exec fuser -uv {} \; 2>&1  |
grep ".*$ctty.*${sid%%"$ctty"*}"

Il /dev/nullmateriale era solo per dimostrare che poteva funzionare quando nessuno dei sottotitoli di ricerca aveva uno di 0,1,2 collegato al ctty. Ad ogni modo, ciò stampa:

/dev/pts/3:          mikeserv   3342 F.... (mikeserv)zsh

Ora quanto sopra ottiene il percorso completo sulla mia macchina e immagino che sarebbe per la maggior parte delle persone nella maggior parte dei casi. Posso anche immaginare che potrebbe fallire. È solo un'euristica approssimativa.

Questo potrebbe non riuscire per molte altre ragioni probabilmente, ma se sei su un sistema che consente al leader della sessione di rinunciare a tutti i descrittori al ctty e tuttavia rimane il sid allora come le specifiche lo consentono, allora sicuramente non sarà di aiuto. Detto questo, penso che questo possa ottenere una stima abbastanza buona nella maggior parte dei casi.

Naturalmente la più semplice cosa da fare se si dispone di eventuali descrittori collegati al CTTY è solo ...

tty <&2

...o simili.

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.