¿Cómo obtener el nombre real de la terminal de control?


13

¿Cómo se puede obtener el nombre real del terminal de control (si hay uno, sino un error) como nombre de ruta?

Por "nombre real", quiero decir que no /dev/tty, que no puede ser utilizado por otros procesos arbitrarios para referirse al mismo terminal. Prefiero la respuesta como un simple código de shell (como el ejemplo a continuación) si es posible, de lo contrario como una función C.

Tenga en cuenta que esto debe funcionar incluso si la entrada estándar se redirige, de modo que la ttyutilidad no se puede usar: not a ttyen ese caso, se obtendría un error, ya que ttysolo imprime el nombre del archivo del terminal conectado a la entrada estándar.

Bajo Linux, uno puede usar:

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

pero esto no es portátil, ya que según POSIX, el formato del nombre del terminal no está especificado .

En cuanto a las funciones de C, ctermid (NULL)devuelve /dev/tty, que aquí es inútil.

Nota: de acuerdo con la zshdocumentación, uno debería poder hacer

zsh -c 'echo $TTY'

pero esto actualmente (versión 5.0.7) falla cuando se redirigen tanto la entrada estándar como la salida estándar:

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

@mikeserv Creo que la pssolución cubre la mayoría de los sistemas (y whono ayuda más que ps), posiblemente con un poco más de código para manejar solo el identificador (como "04"). Me preguntaba si habría una solución aún más portátil.
vinc17

Es posible que tenga que ver con conjuntos pre-emparejados, los viejos pares de pty de estilo bsd también, tal vez. No todos los ptys son tipos UNIX 98. De todos modos, desde man xterm: -Sccn Esta opción permite xtermser utilizada como un canal de E / S para un programa existente ... El valor de la opción es unas pocas letras del nombre de una pty para usar en modo esclavo, más el número fd heredado. Si la opción contiene un carácter "/", delimita el nombre pty del fd.
mikeserv

@mikeserv Tenga en cuenta que la solución no funciona psdesde busybox (que es utilizada por Android, por cierto), incluso bajo GNU / Linux. ¿Qué quiere decir con " xtermpuede manejar eso 04"?
vinc17

busyboxno es compatible con POSIX. toybox, sin embargo, lo hace muy bien.
mikeserv

Respuestas:


8

El "terminal de control" alias. ctty, se distingue de " la terminal con la que interactúa un proceso".

La forma estándar de obtener el camino de ctty es ctermid (3). Al llamar a esto, en freebsd desde la versión 10, se busca una ruta real [1], mientras que las implementaciones anteriores de freebsd y glibc [2] devuelven incondicionalmente "/ dev / tty"].

ps (1) del paquete linux procps 3.2.8, lea la entrada numérica en / proc / * / stat [3], y luego deduzca el nombre de ruta parcialmente adivinando [4, 5] debido a la falta de soporte del sistema [6] .

Sin embargo, si no estamos estrictamente interesados ​​en el ctty pero cualquier terminal asociada con stdio, tty (1) imprime la ruta del terminal conectada a stdin, que es idéntica a ttyname(fileno(stdin))in c, y una alternativa es readlink /proc/self/fd/0.


Pensamiento menos importante con respecto al comportamiento incondicional "/ dev / tty": las especificaciones simplemente dicen que la cadena devuelta por ctermid "cuando se usa como nombre de ruta, se refiere al terminal de control actual", en lugar de algo sencillo "es el nombre de ruta de la corriente terminal de control ". Podría interpretarse como que "/ dev / tty" no es el terminal de control, sino que solo se refiere al terminal de control si el mismo proceso lo abre (3). Por lo tanto, no violar la regla "un terminal puede ser insignificante para, como máximo, una sesión" [7].

Otra consecuencia es que cuando no tengo ningún terminal de control, ctermid no falla, tal falla está permitida por las especificaciones [8], así que solo puedo darme cuenta de mi falta de confianza hasta que falle una apertura posterior (3), lo cual está bien ya que las especificaciones también dicen que llamar a open (3) no está garantizado para tener éxito.


Esto no es más portátil que la pssolución que di en mi pregunta, ya que no todos los sistemas operativos tienen un /procsistema de archivos. Tenga en cuenta que psusa un enlace de lectura /proc/self/fd/2(que funciona incluso si el error estándar se redirige).
vinc17

1
editado y ps readlink en / proc / * / fd / 2 no para encontrar el ctty, sino para buscar información complementaria para mapear el terminal numérico a la ruta, ver enlace [4] [5].
把 友情 留 在 无 盐

1
Excelente edición. En cuanto a la ctty; No puedo hablar por vinc17, pero aunque probablemente siempre puedas escribir en alguna parte, solo hay un archivo que debe permanecer abierto para mantener vivo tu grupo de procesos.
mikeserv

1
@ vinc17 - si tiene cualquier archivo de descriptores abiertos en su CTTY continuación se puede leer con ellos tty. stderres probablemente el mejor porque se especifica que debe estar abierto r / w. Por lo tanto tty <&2.
mikeserv

1
El hecho de que un terminal determinado pueda ser molesto durante a lo sumo una sesión no hace que glibc no sea conforme para que ctermid()siempre regrese "/dev/tty". Ese nombre siempre se refiere al terminal de control del proceso que accede a él , que varía según la sesión. El terminal es específico de la sesión, pero el nombre por el que se accede no necesita serlo.
PellMel

5

La especificación POSIX realmente cubre sus apuestas en lo que respecta al Terminal de Control , y que define así:

  • Terminal de control
    • La cuestión de cuál de los posibles archivos especiales que se refieren al terminal no se aborda en POSIX.1. El nombre de ruta /dev/ttyes sinónimo del terminal de control asociado con un proceso.

Eso está en la lista de Definiciones, y eso es todo lo que hay. Pero en General Terminal Interface , se dice algo más:

  • Un terminal puede pertenecer a un proceso como su terminal de control. Cada proceso de una sesión que tiene un terminal de control tiene el mismo terminal de control. Un terminal puede ser el terminal de control para, como máximo, una sesión. El líder de sesión asigna el terminal de control para una sesión de una manera definida por la implementación. Si un líder de sesión no tiene terminal de control y abre un archivo de dispositivo de terminal que no está asociado con una sesión sin usar la opción O_NOCTTY (ver open ()), se define la implementación si el terminal se convierte en el terminal de control de la sesión líder.

  • El terminal de control es heredado por un proceso hijo durante una llamada a la función fork (). Un proceso renuncia a su terminal de control cuando crea una nueva sesión con elsetsid()función; otros procesos restantes en la sesión anterior que tenía este terminal como su terminal de control continúan teniendo. Al cerrar el último descriptor de archivo en el sistema (esté o no en la sesión actual) asociado con el terminal de control, no se especifica si todos los procesos que tenían ese terminal como terminal de control dejan de tener algún terminal de control. No se especifica si y cómo un líder de sesión puede volver a adquirir un terminal de control después de que el terminal de control haya sido cedido de esta manera. Un proceso no renuncia a su terminal de control simplemente cerrando todos sus descriptores de archivos asociados con el terminal de control si otros procesos continúan abriéndolo.

Quedan muchas cosas sin especificar , y sinceramente, creo que tiene sentido. Si bien el terminal es una interfaz de usuario clave, en algunos casos también es todo tipo de otras cosas, como hardware real o incluso una especie de impresora, pero en muchos casos prácticamente no es nada, como xtermun emulador . Es difícil ser específico allí, y no creo que sea de gran interés para Unix de todos modos, porque los terminales hacen mucho más que Unix.

De todos modos, POSIX también es bastante dudoso sobre cómo psdebe comportarse en lo que respecta a la comunidad.

Ahí está el -ainterruptor:

  • Escriba información para todos los procesos asociados con terminales. Las implementaciones pueden omitir los líderes de sesión de esta lista.

Excelente. Los líderes de sesión pueden ser omitidos. Eso no es muy útil.

Y -t:

  • Escriba información para los procesos asociados con los terminales que figuran en la lista de términos. La aplicación se asegurará de que la lista de términos sea un argumento único en forma de una <blank>lista separada por comas. Los identificadores de terminal se darán en un formato definido por la implementación .

... que es otra decepción. Pero continúa diciendo esto sobre los sistemas XSI:

  • En los sistemas compatibles con XSI, se darán en una de dos formas: el nombre de archivo del dispositivo (por ejemplo, tty04) o, si el nombre de archivo del dispositivo comienza con tty, solo el identificador que sigue a los caracteres tty (por ejemplo 04) .

Eso es un poco mejor, pero no es un camino. También en los sistemas XSI existe el -dinterruptor:

  • Escriba información para todos los procesos, excepto los líderes de sesión.

... que al menos está claro. Puede especificar el -omodificador de rendimiento también con la ttycadena de formato, pero, como ha notado, su formato de salida está definido por la implementación. Aún así, creo que es tan bueno como se pone. Creo que, con mucho trabajo, los interruptores anteriores en combinación con algunas otras utilidades pueden darle un buen estadio. Sin embargo, para ser sincero, no sé cuándo / cómo se rompe para usted, y no he podido imaginar una situación en la que lo haría. Pero, creo que probablemente si agregamos fusery findpodemos verificar la ruta.

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

El /dev/nullmaterial era solo para mostrar que podía funcionar cuando ninguna de las subcapas de búsqueda tenía ninguna conexión de 0,1,2 conectada a la comunidad. De todos modos, eso imprime:

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

Ahora lo anterior obtiene el camino completo en mi máquina, e imagino que lo haría para la mayoría de las personas en la mayoría de los casos. También me imagino que podría fallar. Es solo una heurística aproximada.

Probablemente, esto podría fallar por muchas otras razones, pero si está en un sistema que permite que el líder de la sesión renuncie a todos los descriptores y, sin embargo, siga siendo el sid, según lo permitan las especificaciones, entonces esto definitivamente no va a ayudar. Dicho esto, creo que esto puede obtener una estimación bastante buena en la mayoría de los casos.

Por supuesto, el más fácil de lo que debe hacer si tiene algún descriptores conectados a su CTTY es sólo ...

tty <&2

...o similar.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.