Comment obtenir le vrai nom du terminal de contrôle?


13

Comment obtenir le vrai nom du terminal de contrôle (s'il y en a un, sinon une erreur) comme nom de chemin?

Par "vrai nom", je veux dire non /dev/tty, qui ne peut pas être utilisé par d'autres processus arbitraires pour se référer au même terminal. Je préfère la réponse comme un simple code shell (comme l'exemple ci-dessous) si possible, sinon comme une fonction C.

Notez que cela doit fonctionner même si l'entrée standard est redirigée, de sorte que l' ttyutilitaire ne peut pas être utilisé: on obtiendrait une not a ttyerreur dans un tel cas, car il ttysuffit d'imprimer le nom de fichier du terminal connecté à l'entrée standard.

Sous Linux, on peut utiliser:

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

mais ce n'est pas portable, car selon POSIX, le format du nom du terminal n'est pas spécifié .

Concernant les fonctions C, ctermid (NULL)retourne /dev/tty, ce qui est inutile ici.

Remarque: selon la zshdocumentation, on devrait pouvoir faire

zsh -c 'echo $TTY'

mais cela actuellement (version 5.0.7) échoue lorsque l'entrée standard et la sortie standard sont redirigées:

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

@mikeserv Je pense que la pssolution couvre la plupart des systèmes (et whon'aide pas plus ps), peut-être avec un peu plus de code pour gérer l'identifiant seul (comme "04"). Je me demandais s'il y avait une solution encore plus portable.
vinc17

Cela pourrait avoir à voir avec des ensembles pré-appariés - les anciennes paires de pty de style bsd aussi, peut-être. Tous les ptys ne sont pas de type UNIX 98. Quoi qu'il en soit, à partir de man xterm: -Sccn Cette option permet xtermd'être utilisée comme canal d'E / S pour un programme existant ... La valeur de l'option est quelques lettres du nom d'un pty à utiliser en mode esclave, plus le numéro fd hérité. Si l'option contient un caractère «/», elle délimite le nom pty du fd.
mikeserv

@mikeserv Notez que la solution ne fonctionne pas avec psdepuis busybox (qui est utilisé par Android, BTW), même sous GNU / Linux. Que voulez-vous dire par « xtermpeut gérer ça 04»?
vinc17

busyboxn'est pas conforme à POSIX. toybox, cependant, fait très bien.
mikeserv

Réponses:


8

Le "terminal de contrôle" aka. CTTY, est distincted de « la borne processus d'une interagit avec ».

Le moyen standard d'obtenir le chemin de ctty est ctermid (3). En appelant cela, dans freebsd depuis la version 10, un chemin réel est recherché [1], tandis que les anciennes implémentations de freebsd et de glibc [2] renvoient inconditionnellement "/ dev / tty"].

ps (1) du paquet linux procps 3.2.8, lisez l'entrée numérique dans / proc / * / stat [3], puis déduisez partiellement le chemin en devinant [4, 5] en raison du manque de prise en charge du système [6] .

Cependant, si nous ne sommes pas strictement intéressés par le ctty mais par tout terminal associé à stdio, tty (1) affiche le chemin du terminal connecté à stdin, qui est identique à ttyname(fileno(stdin))c, et une alternative est readlink /proc/self/fd/0.


Pensée moins importante concernant le comportement inconditionnel "/ dev / tty": les spécifications indiquent simplement que la chaîne renvoyée par ctermid "lorsqu'elle est utilisée comme nom de chemin d'accès, se réfère au terminal de contrôle actuel", au lieu d'être simple "est le nom de chemin d'accès du courant terminal de contrôle ". Cela pourrait être interprété comme si "/ dev / tty" n'est pas le terminal de contrôle, mais ne fait référence au terminal de contrôle que si le même processus l'ouvre (3). Ainsi, ne pas enfreindre la règle «un terminal peut être coûteux pendant au plus une session» [7].

Une autre conséquence est que lorsque je suis sans aucun terminal de contrôle, ctermid n'échoue pas - une telle défaillance est autorisée par les spécifications [8] -, donc je ne peux prendre conscience de mon absence de contrôle que jusqu'à l'échec d'une ouverture ultérieure (3), ce qui est correct car les spécifications indiquent également qu'appeler open (3) dessus n'est pas garanti pour réussir.


Ce n'est pas plus portable que la pssolution que j'ai donnée dans ma question, car tous les systèmes d'exploitation n'ont pas de système de /procfichiers. Notez que pslui - même utilise un lien de lecture /proc/self/fd/2(qui fonctionne même si l'erreur standard est redirigée).
vinc17

1
édité. et ps readlink sur / proc / * / fd / 2 non pas pour trouver le ctty, mais pour rechercher des informations supplémentaires afin de mapper le terminal numérique au chemin, voir lien [4] [5].
把 友情 留 在 无 盐

1
Excellent montage. Concernant le ctty; Je ne peux pas parler pour vinc17, mais bien que vous puissiez probablement toujours écrire quelque part, il n'y a qu'un seul fichier qui doit rester ouvert pour garder votre groupe de processus en vie.
mikeserv

1
@ vinc17 - si vous avez des descripteurs de fichiers ouverts sur votre ctty, vous pouvez les lire avec tty. stderrest probablement le meilleur car il est prévu qu'il soit ouvert r / w. Alors tty <&2.
mikeserv

1
Le fait qu'un terminal donné puisse être ctty pendant au plus une session ne rend pas la glibc non conforme car elle ctermid()revient toujours "/dev/tty". Ce nom fait toujours référence au terminal de contrôle du processus qui y accède , qui varie selon la session. Le terminal est spécifique à la session, mais le nom par lequel il est accessible n'a pas besoin d'être.
PellMel du

5

La spécification POSIX couvre vraiment ses paris en ce qui concerne le Terminal de Contrôle et qu'elle définit ainsi:

  • Terminal de contrôle
    • La question de savoir si plusieurs fichiers spéciaux se référant au terminal sont concernés n'est pas traitée dans POSIX.1. Le chemin d'accès /dev/ttyest synonyme du terminal de contrôle associé à un processus.

C'est dans la liste des définitions - et c'est tout ce qu'il y a. Mais dans General Terminal Interface , on en dit plus:

  • Un terminal peut appartenir à un processus en tant que terminal de contrôle. Chaque processus d'une session qui a un terminal de contrôle a le même terminal de contrôle. Un terminal peut être le terminal de contrôle pour au plus une session. Le terminal de contrôle d'une session est alloué par le chef de session d'une manière définie par l'implémentation. Si un chef de session n'a pas de terminal de contrôle et ouvre un fichier de périphérique de terminal qui n'est pas déjà associé à une session sans utiliser l'option O_NOCTTY (voir open ()), il est défini par l'implémentation si le terminal devient le terminal de contrôle de la session chef.

  • Le terminal de contrôle est hérité par un processus enfant lors d'un appel de fonction fork (). Un processus abandonne son terminal de contrôle lorsqu'il crée une nouvelle session avec lesetsid()une fonction; d'autres processus restant dans l'ancienne session qui avaient ce terminal comme terminal de contrôle continuent de l'avoir. À la fermeture du dernier descripteur de fichier du système (qu'il se trouve ou non dans la session en cours) associé au terminal de contrôle, il n'est pas spécifié si tous les processus qui avaient ce terminal comme terminal de contrôle cessent d'avoir un terminal de contrôle. Il n'est pas précisé si et comment un chef de session peut réacquérir un terminal de contrôle après que le terminal de contrôle a été abandonné de cette manière. Un processus ne renonce pas à son terminal de contrôle simplement en fermant tous ses descripteurs de fichiers associés au terminal de contrôle si d'autres processus continuent de l'ouvrir.

Il reste beaucoup de choses non spécifiées - et honnêtement, je pense que cela a du sens. Bien que le terminal soit une interface utilisateur clé, il y a aussi toutes sortes d'autres choses dans certains cas - comme le matériel réel, ou même une sorte d'imprimante - mais dans beaucoup de cas, ce n'est pratiquement rien du tout - comme un xtermqui n'est qu'un émulateur . Il est difficile d'être précis là-bas - et je ne pense pas que ce serait dans l'intérêt de Unix de toute façon, car les terminaux font beaucoup plus que Unix.

Quoi qu'il en soit, POSIX est également assez sceptique sur la façon de psse comporter en ce qui concerne le ctty.

Il y a l' -ainterrupteur:

  • Écrivez des informations pour tous les processus associés aux terminaux. Les implémentations peuvent omettre les chefs de session de cette liste.

Génial. Les chefs de session peuvent être omis. Ce n'est pas très utile.

Et -t:

  • Écrivez les informations pour les processus associés aux terminaux donnés dans la liste de termes. La demande doit garantir que la liste terminologique est un argument unique sous la forme d'une <blank>liste séparée par des virgules. Les identifiants de terminal doivent être donnés dans un format défini par l'implémentation .

... ce qui est une autre déception. Mais cela continue à dire à propos des systèmes XSI:

  • Sur les systèmes conformes à XSI, ils doivent être donnés sous l'une des deux formes suivantes: le nom de fichier de l'appareil (par exemple, tty04) ou, si le nom de fichier de l'appareil commence par tty, juste l'identifiant suivant les caractères tty (par exemple, 04) .

C'est un peu mieux, mais ce n'est pas un chemin. Sur les systèmes XSI, il y a également le -dcommutateur:

  • Écrivez des informations pour tous les processus, à l'exception des chefs de session.

... ce qui est au moins clair. Vous pouvez également spécifier le -ocommutateur de sortie avec la ttychaîne de format, mais, comme vous l'avez noté, son format de sortie est défini par l'implémentation. Pourtant, je pense que c'est aussi bon que possible. Je pense que - avec beaucoup de travail - les commutateurs ci-dessus en combinaison avec d'autres utilitaires peuvent vous donner une assez bonne idée. Pour être tout à fait honnête, je ne sais pas quand / comment ça se casse pour vous - et je n'ai pas pu imaginer une situation dans laquelle cela se produirait. Mais, je pense que probablement si nous ajoutons fuseret findnous pouvons vérifier le chemin.

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"*}"

Le /dev/nulltruc était juste pour montrer que cela pouvait fonctionner quand aucun des sous-coquilles de recherche n'avait de 0,1,2 connecté au ctty. Quoi qu'il en soit, cela imprime:

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

Maintenant, ce qui précède obtient le chemin complet sur ma machine, et j'imagine que ce serait le cas pour la plupart des gens dans la plupart des cas. Je peux aussi imaginer que cela pourrait échouer. C'est juste une heuristique grossière.

Cela pourrait probablement échouer pour de nombreuses autres raisons, mais si vous êtes sur un système qui permet au chef de session de renoncer à tous les descripteurs du ctty et de rester le sid alors comme le permet la spécification, alors cela ne va certainement pas aider. Cela dit, je pense que cela peut obtenir une assez bonne estimation dans la plupart des cas.

Bien sûr, la chose la plus simple à faire si vous avez des descripteurs connectés à votre ctty est juste ...

tty <&2

...ou similaire.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.