Как получить настоящее имя управляющего терминала?


13

Как можно получить настоящее имя управляющего терминала (если оно есть, иначе ошибка) в качестве имени пути?

Под «настоящим именем» я подразумеваю /dev/tty, что другие произвольные процессы не могут ссылаться на один и тот же терминал. Я предпочитаю ответ как простой шелл-код (как в примере ниже), если это возможно, в противном случае как функцию C.

Обратите внимание, что это должно работать, даже если стандартный ввод перенаправлен, так что ttyутилита не может быть использована: not a ttyв таком случае возникнет ошибка, поскольку ttyпросто печатается имя файла терминала, подключенного к стандартному вводу.

Под Linux можно использовать:

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

но это не переносимо, так как в соответствии с POSIX формат имени терминала не указан .

Что касается функций C, ctermid (NULL)возвращает /dev/tty, что здесь бесполезно.

Примечание: согласно zshдокументации, уметь

zsh -c 'echo $TTY'

но в настоящее время (версия 5.0.7) происходит сбой, когда перенаправляются как стандартный ввод, так и стандартный вывод:

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

@mikeserv Я думаю, что psрешение охватывает большинство систем (и whoне помогает больше ps), возможно, с немного большим количеством кода для обработки только идентификатора (например, «04»). Мне было интересно, есть ли еще более портативное решение.
vinc17

Возможно, это связано с заранее спаренными наборами - возможно, со старыми парами bsd- стиля pty. Не все ptys являются типами UNIX 98. В любом случае, с man xterm: -Sccn Эта опция позволяет xtermиспользовать ее как канал ввода-вывода для существующей программы ... Значение опции - это несколько букв имени pty для использования в подчиненном режиме плюс унаследованный номер fd. Если опция содержит символ «/», она отделяет имя pty от fd.
mikeserv

@mikeserv Обратите внимание , что решение не работает с psот BusyBox (который используется Android, BTW), даже под GNU / Linux. Что вы подразумеваете под " xtermможет справиться с этим 04"?
vinc17

busyboxне POSIX-совместимый. toyboxОднако, очень хорошо.
mikeserv

Ответы:


8

«Управляющий терминал» ака. CTTY, является distincted от « процесс Терминал взаимодействует с».

Стандартный способ получения пути ctty - ctermid (3). После вызова этого в freebsd начиная с версии 10 ищется фактический путь [1], в то время как более старые реализации freebsd и glibc [2] безоговорочно возвращают "/ dev / tty"].

ps (1) из пакета linux procps 3.2.8, прочитайте числовую запись в / proc / * / stat [3], а затем частично выведите путь , угадав [4, 5] из-за отсутствия системной поддержки [6] ,

Однако, если мы не заинтересованы в ctty, но в любом терминале, связанном с stdio, tty (1) печатает путь терминала, связанный с stdin, который идентичен ttyname(fileno(stdin))c, и альтернативой является readlink /proc/self/fd/0.


Менее важная мысль относительно безусловного поведения "/ dev / tty": спецификации просто говорят, что строка, возвращаемая ctermid "при использовании в качестве имени пути, ссылается на текущий управляющий терминал", а не просто "- это имя пути текущего управляющий терминал ". Это может быть интерпретировано как то, что «/ dev / tty» не является управляющим терминалом, а относится к управляющему терминалу, только если тот же процесс открывает (3) его. Таким образом, не нарушая правило «терминал может быть не более одного сеанса» [7].

Другое следствие состоит в том, что, когда у меня нет какого-либо управляющего терминала, ctermid не выходит из строя - такой сбой допускается спецификациями [8] - так что только я могу узнать о своем ctty'lessness, пока не произойдет сбой последующего открытия (3), это нормально, так как спецификации также говорят, что вызов open (3) не гарантирует успеха.


Это не более портативно, чем psрешение, которое я дал в своем вопросе, поскольку не все ОС имеют /procфайловую систему. Обратите внимание, что psсам по себе используется ссылка для чтения /proc/self/fd/2(которая работает, даже если стандартная ошибка перенаправлена).
vinc17

1
изм. и ps readlink в / proc / * / fd / 2 не для поиска ctty, а для поиска дополнительной информации для отображения числового терминала на путь, см. ссылку [4] [5].
盐 友情 留 在 无 盐

1
Отличное редактирование. Что касается CTY; Я не могу говорить за vinc17, но, хотя вы, вероятно, всегда можете написать куда-то, есть только один файл, который должен оставаться открытым, чтобы поддерживать вашу группу процессов.
mikeserv

1
@ vinc17 - если у вас есть какие-либо файловые дескрипторы, открытые на вашем ctty, то вы можете прочитать их с помощью tty. stderrвероятно, лучший, потому что он должен быть открытым. Так tty <&2.
mikeserv

1
То, что данный терминал может быть не более одного сеанса, не делает glibc несоответствующим для его ctermid()постоянного возврата "/dev/tty". Это имя всегда относится к управляющему терминалу процесса , который обращается к нему , что зависит от сеанса. Терминал зависит от сеанса, но имя, к которому он обращается, не обязательно.
PellMel

5

Спецификация POSIX действительно хеджирует свои ставки в отношении управляющего терминала и определяет его таким образом:

  • Терминал управления
    • Вопрос о том, какой из нескольких специальных файлов, относящихся к терминалу, подразумевается, не рассматривается в POSIX.1. Путь /dev/ttyявляется синонимом управляющего терминала, связанного с процессом.

Это в списке определений - и это все, что есть. Но в General Terminal Interface сказано еще кое-что:

  • Терминал может принадлежать процессу в качестве управляющего терминала. Каждый процесс сеанса, который имеет управляющий терминал, имеет один и тот же управляющий терминал. Терминал может быть управляющим терминалом максимум для одного сеанса. Управляющий терминал для сеанса назначается руководителем сеанса в зависимости от реализации. Если руководитель сеанса не имеет управляющего терминала и открывает файл терминального устройства, который еще не связан с сеансом, без использования опции O_NOCTTY (см. Open ()), то определяется, будет ли реализация становиться управляющим терминалом сеанса. лидер.

  • Управляющий терминал наследуется дочерним процессом во время вызова функции fork (). Процесс освобождает свой управляющий терминал при создании нового сеанса сsetsid()функция; другие процессы, оставшиеся в старом сеансе, которые имели этот терминал в качестве управляющего терминала, продолжают иметь его. После закрытия последнего файлового дескриптора в системе (независимо от того, находится ли он в текущем сеансе), связанного с управляющим терминалом, не определено, все ли процессы, которые имели этот терминал в качестве своего управляющего терминала, перестают иметь какой-либо управляющий терминал. Вопрос о том, может ли и как руководитель сеанса повторно получить управляющий терминал после того, как управляющий терминал был освобожден таким образом, не определено. Процесс не отказывается от своего управляющего терминала, просто закрывая все свои файловые дескрипторы, связанные с управляющим терминалом, если другие процессы продолжают открывать его.

Там многое осталось неуказанным - и, честно говоря, я думаю, что это имеет смысл. В то время как терминал является ключевым пользовательским интерфейсом, в некоторых случаях это также все виды других вещей - например, аппаратное обеспечение или даже принтер - но во многих случаях это практически вообще ничего - например, xtermэмулятор, который просто эмулятор , Здесь сложно конкретизировать - и я не думаю, что это все равно будет в интересах Unix, потому что терминалы делают намного больше, чем Unix.

Во всяком случае, POSIX также довольно сомнительно, как psвести себя, когда дело касается ctty.

Там -aпереключатель:

  • Запишите информацию для всех процессов, связанных с терминалами. Реализации могут исключить лидеров сеансов из этого списка.

Отлично. Руководители сессий могут быть опущены. Это не очень полезно.

И -t:

  • Запишите информацию для процессов, связанных с терминалами, приведенными в списке терминов. Приложение должно убедиться, что список терминов является единственным аргументом в форме <blank>списка, разделенного запятыми. Идентификаторы терминала должны быть заданы в формате, определяемом реализацией .

... что является еще одним разочарованием. Но это говорит о системах XSI:

  • В XSI-совместимых системах они должны быть заданы в одной из двух форм: имя файла устройства (например, tty04) или, если имя файла устройства начинается с tty, только идентификатор, следующий за символами tty (например, 04) .

Это немного лучше, но это не путь. Также в системах XSI есть -dпереключатель:

  • Запишите информацию для всех процессов, кроме руководителей сеансов.

... что по крайней мере понятно. Вы также можете указать -oпереключатель utput со ttyстрокой формата, но, как вы заметили, его выходной формат определяется реализацией. Тем не менее, я думаю, что это так же хорошо, как и получается. Я думаю, что - с большой работой - вышеупомянутые переключатели в сочетании с некоторыми другими утилитами могут дать вам довольно хороший пример. Честно говоря, я не знаю, когда / как это сломается для вас - и я не смог представить ситуацию, в которой это произойдет. Но, думаю, возможно, если мы добавим fuserи findсможем проверить путь.

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

Материал /dev/nullбыл просто для того, чтобы показать, что он может работать, когда ни один из поисковых субоболочек не имеет 0,1,2, связанных с ctty. Во всяком случае, это печатает:

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

Теперь вышесказанное дает полный путь к моей машине, и я думаю, что это будет для большинства людей в большинстве случаев. Я также могу представить, что это может потерпеть неудачу. Это просто грубая эвристика.

Возможно, это может произойти по многим другим причинам, но если вы работаете в системе, которая позволяет руководителю сеанса передавать все дескрипторы на ctty и в то же время оставаться sid, как позволяет спецификация, то это определенно не поможет. Тем не менее, я думаю, что это может получить довольно хорошую оценку в большинстве случаев.

Конечно, проще всего, если у вас есть какие-либо дескрипторы, связанные с вашим ctty, это просто ...

tty <&2

...или похожие.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.