Como obter o nome real do terminal de controle?


13

Como obter o nome real do terminal de controle (se houver um, senão um erro) como um nome de caminho?

Por "nome real", quero dizer que não /dev/tty, que não pode ser usado por outros processos arbitrários para se referir ao mesmo terminal. Prefiro a resposta como um código shell simples (como o exemplo abaixo), se possível, caso contrário, como uma função C.

Observe que isso deve funcionar mesmo que a entrada padrão seja redirecionada, para que o ttyutilitário não possa ser usado: not a ttynesse caso, ocorreria um erro, pois ttyapenas imprime o nome do arquivo do terminal conectado à entrada padrão.

No Linux, pode-se usar:

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

mas isso não é portátil, pois de acordo com o POSIX, o formato do nome do terminal não é especificado .

Em relação às funções C, ctermid (NULL)retorna /dev/tty, que é inútil aqui.

Nota: de acordo com a zshdocumentação, deve-se poder fazer

zsh -c 'echo $TTY'

mas isso atualmente (versão 5.0.7) falha quando a entrada e a saída padrão são redirecionadas:

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

@mikeserv Acho que a pssolução cobre a maioria dos sistemas (e whonão ajuda mais do que ps), possivelmente com um pouco mais de código para lidar com o identificador sozinho (como "04"). Fiquei me perguntando se havia uma solução ainda mais portátil.
vinc17

Pode ter a ver com conjuntos pré-emparelhados - os antigos pares pty no estilo bsd também, talvez. Nem todos os ptys são do tipo UNIX 98. De qualquer forma, de man xterm: -Sccn Esta opção permite xtermser usada como um canal de E / S para um programa existente ... O valor da opção são algumas letras do nome de um pty para usar no modo escravo, mais o número fd herdado. Se a opção contiver um caractere "/", delimitará o nome do pty do fd.
mikeserv

@mikeserv Note-se que a solução não funciona com o psde busybox (que é usado pelo Android, BTW), mesmo sob GNU / Linux. O que você quer dizer com " xtermpode lidar com isso 04"?
vinc17

busyboxnão é compatível com POSIX. toybox, no entanto, faz muito bem.
mikeserv

Respostas:


8

O "terminal de controle" aka. ctty, distingue-se " do terminal com o qual o processo está interagindo".

A maneira padrão de obter o caminho do ctty é o ctermid (3). Ao chamar isso, no freebsd desde o release 10, um caminho real é pesquisado [1], enquanto as implementações mais antigas do freebsd e glibc [2] retornam incondicionalmente "/ dev / tty"].

ps (1) do pacote linux procps 3.2.8, leia a entrada numérica em / proc / * / stat [3] e deduza o nome do caminho parcialmente adivinhando [4, 5] devido à falta de suporte do sistema [6] .

No entanto, se não estivermos estritamente interessados ​​no ctty, mas em qualquer terminal associado ao stdio, o tty (1) imprime o caminho do terminal conectado ao stdin, que é idêntico ao ttyname(fileno(stdin))do c, e uma alternativa é readlink /proc/self/fd/0.


Pensamento menos importante em relação ao comportamento incondicional "/ dev / tty": as especificações apenas dizem que a string retornada pelo ctermid "quando usada como nome do caminho, refere-se ao terminal de controle atual", em vez de algo simples "é o nome do caminho do atual terminal de controle ". Pode ser interpretado como "/ dev / tty" não é o terminal de controle, mas apenas se refere ao terminal de controle se o mesmo processo o abrir (3). Portanto, não violar a regra "um terminal pode ser complicado para no máximo uma sessão" [7].

Outra conseqüência é que, quando estou sem um terminal de controle, o ctermid não falha - essa falha é permitida pelas especificações [8] -, de modo que só posso ficar ciente da minha ausência de ctty'less até falhar em uma abertura subsequente (3), o que é bom, já que as especificações também dizem que a abertura (3) não é garantida para ter sucesso.


Isso não é mais portátil que a pssolução que dei na minha pergunta, pois nem todos os sistemas operacionais têm um /procsistema de arquivos. Observe que psele mesmo usa um link de leitura ativado /proc/self/fd/2(que funciona mesmo, o erro padrão é redirecionado).
vinc17

1
editado. e ps readlink em / proc / * / fd / 2 não para encontrar o ctty, mas para buscar informações adicionais para mapear o terminal numérico para o caminho, consulte o link [4] [5].
28415

1
Excelente edição. Em relação ao ctty; Não posso falar por vinc17, mas, embora você possa sempre escrever em algum lugar, há apenas um arquivo que deve permanecer aberto para manter seu grupo de processos ativo.
mikeserv

1
@ vinc17 - se você tiver algum descritor de arquivo aberto no seu ctty, poderá lê-lo com tty. stderré provavelmente o melhor, porque deve ser aberto r / w. Então tty <&2.
mikeserv

1
O fato de um determinado terminal ser seguro por no máximo uma sessão não torna a glibc não conforme, pois ctermid()sempre retorna "/dev/tty". Esse nome sempre se refere ao terminal de controle do processo que o acessa , que varia de acordo com a sessão. O terminal é específico da sessão, mas o nome pelo qual é acessado não precisa ser.
PellMel

5

A especificação POSIX realmente protege suas apostas no que diz respeito ao Terminal de Controle e define assim:

  • Terminal de controle
    • A questão sobre qual dos possíveis arquivos especiais referentes ao terminal é abordada no POSIX.1. O nome do caminho /dev/ttyé um sinônimo para o terminal de controle associado a um processo.

Está na lista de definições - e é tudo o que existe. Mas na Interface Geral do Terminal , mais um pouco é dito:

  • Um terminal pode pertencer a um processo como seu terminal de controle. Cada processo de uma sessão que possui um terminal de controle possui o mesmo terminal de controle. Um terminal pode ser o terminal de controle por no máximo uma sessão. O terminal de controle de uma sessão é alocado pelo líder da sessão de uma maneira definida pela implementação. Se um líder de sessão não tiver um terminal de controle e abrir um arquivo de dispositivo de terminal que ainda não esteja associado a uma sessão sem usar a opção O_NOCTTY (consulte open ()), será definido como implementação se o terminal se tornará o terminal de controle da sessão líder.

  • O terminal de controle é herdado por um processo filho durante uma chamada de função fork (). Um processo renuncia ao seu terminal de controle quando cria uma nova sessão com osetsid()função; outros processos restantes na sessão antiga que possuíam esse terminal como seu terminal de controle continuam a ter. Após o fechamento do último descritor de arquivo no sistema (se está ou não na sessão atual) associado ao terminal de controle, não é especificado se todos os processos que tinham esse terminal como terminal de controle deixam de ter qualquer terminal de controle. Não é especificado se e como um líder de sessão pode recuperar um terminal de controle depois que o terminal de controle foi abandonado dessa maneira. Um processo não renuncia ao seu terminal de controle simplesmente fechando todos os descritores de arquivo associados ao terminal de controle se outros processos continuarem a abri-lo.

Ainda há muita coisa não especificada - e honestamente acho que faz sentido. Embora o terminal seja uma interface principal do usuário, em alguns casos também existem todos os tipos de outras coisas - como hardware real ou até mesmo um tipo de impressora - mas em muitos casos praticamente não é praticamente nada - como um xtermque é apenas um emulador . É difícil ser específico lá - e eu não acho que seria muito do interesse do Unix, porque os terminais fazem muito mais do que o Unix.

De qualquer forma, o POSIX também é bastante duvidoso sobre como psdeve se comportar no que diz respeito ao ctty.

Aqui está o -ainterruptor:

  • Escreva informações para todos os processos associados aos terminais. As implementações podem omitir os líderes de sessão desta lista.

Ótimo. Os líderes da sessão podem ser omitidos. Isso não é muito útil.

E -t:

  • Escreva informações para processos associados aos terminais fornecidos na lista de termos. O aplicativo deve garantir que a lista de termos seja um argumento único na forma de uma lista <blank>ou separada por vírgula. Os identificadores de terminal devem ser fornecidos em um formato definido pela implementação .

... que é outra decepção. Mas continua dizendo isso sobre os sistemas XSI:

  • Nos sistemas compatíveis com XSI, eles devem ser fornecidos em uma de duas formas: o nome do arquivo do dispositivo (por exemplo tty04) ou, se o nome do arquivo do dispositivo começar tty, apenas o identificador após os caracteres tty (por exemplo 04) .

Isso é um pouco melhor, mas não é um caminho. Também em sistemas XSI, há o -dswitch:

  • Escreva informações para todos os processos, exceto os líderes da sessão.

... o que é pelo menos claro. Você pode especificar a -oopção utput também com a ttystring format, mas, como você observou, o formato de saída é definido pela implementação. Ainda assim, acho que é o melhor possível. Eu acho que - com muito trabalho - as opções acima, combinadas com outros utilitários, podem lhe proporcionar uma estimativa muito boa. Para ser sincero, porém, não sei quando / como isso acontece para você - e não pude imaginar uma situação em que isso aconteceria. Mas acho que provavelmente se adicionarmos fusere findpodemos verificar o caminho.

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

O /dev/nullmaterial era apenas para mostrar que poderia funcionar quando nenhum dos subconjuntos de pesquisa tinha 0,1,2 conectado ao ctty. Enfim, isso imprime:

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

Agora, o texto acima mostra o caminho completo na minha máquina, e imagino que seria para a maioria das pessoas na maioria dos casos. Eu também posso imaginar que poderia falhar. São apenas heurísticas grosseiras.

Provavelmente, isso pode falhar por muitas outras razões, mas se você estiver em um sistema que permita ao líder da sessão renunciar a todos os descritores ao ctty e ainda assim permanecer como sid, conforme a especificação permitir, isso definitivamente não ajudará. Dito isto, acho que isso pode obter uma estimativa muito boa na maioria dos casos.

Claro que a coisa mais fácil de fazer se você tiver algum descritor conectado ao seu ctty é apenas ...

tty <&2

...ou similar.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.