Как мы можем узнать, кто находится на другом конце псевдо-терминального устройства?


26

Если я сделаю:

echo foo > /dev/pts/12

Какой-то процесс прочитает это foo\nиз своего файлового дескриптора в сторону мастера.

Есть ли способ узнать, что это (эти) процесс (ы) является (есть)?

Или, другими словами, как я могу узнать, какой xterm / sshd / script / screen / tmux / receive / socat ... находится на другом конце /dev/pts/12?

lsof /dev/ptmxскажет мне процессы, которые имеют файловые дескрипторы на главной стороне любого pty. Сам процесс может использовать ptsname()( TIOCGPTNioctl) для обнаружения подчиненного устройства на основе его собственного fd на стороне мастера, поэтому я мог бы использовать:

gdb --batch --pid "$the_pid" -ex "print ptsname($the_fd)"

для каждого из pid / fd, возвращаемых lsofдля создания этого отображения, но есть ли более прямой, надежный и менее навязчивый способ получения этой информации?


Это то, что вы хотите? sudo find /proc/*/fd/0 -ls | grep '/dev/pts/4', предоставил бы список PID ( /proc/PID) в качестве вывода.
SLM

@slm, нет, другими словами, я хочу выяснить, какой xterm / sshd / script / screen / tmux / receive / socat ... находится на другом конце /dev/pts/4. Обычно это будет общим предком тех процессов, которые /dev/pts/4открыты, но не обязательно.
Стефан Шазелас

1
Еще хуже с сокетами - вам нужен отладчик ядра!
Жиль "ТАК - перестать быть злым"

1
@Falsenames - я понял, что вопрос означает - возможно, неправильно - не то, какой процесс передает прочитанные данные - например, первая оболочка, вызванная в терминале, - но какой процесс фактически читает его со стороны мастера. Например, если я запускаю оболочку screen, она screenвыделяет и активно управляет pty-slave в течение всего срока службы устройства, но, как я думаю, оболочка становится лидером процесса для этого tty и так, как ваш выход показывает, вы получаете bashили что-то из psнет screen. Я проследил несколько xtermsназад к xtermpid на основе, /proc/locksно это было свободно.
mikeserv

Ответы:


3

Сначала я попытался проследить несколько xtermсекунд назад до xtermpid, основываясь на информации, которую я нашел, /proc/locksно это было свободно. Я имею в виду, что это работало, я думаю, но в лучшем случае это было финансово - я не до конца понимаю всю информацию, которую предоставляет файл, и соответствовал только тому, что, казалось, соответствовало его содержанию и известным терминальным процессам.

Затем я попытался посмотреть lsof/straceна активный write/talkпроцесс между ptys. Я никогда раньше не использовал ни одну из этих программ, но, похоже, они полагаются utmp. Если у моего целевого pty utmpпо какой-либо причине не было записи, они оба отказывались признать, что она существует. Может быть, есть способ обойти это, но я был достаточно смущен, чтобы отказаться от него.

Я попробовал некоторые udevadmоткрытия с 136 и 128 старшими узлами устройств, как рекламировалось ptsи ptmсоответственно в нем /proc/tty/drivers, но у меня также не было очень полезного опыта работы с этим инструментом, и я снова не нашел ничего существенного. Интересно, что я заметил, что :minдиапазон для обоих типов устройств был ошеломляющим 0-1048575.

До тех пор, пока я не вернулся к этому документу по ядру , я начал думать о проблеме с точки зрения mounts. Я прочитал , что несколько раз до этого, но когда продолжение исследований в этой линии привело меня к этому это 2012 /dev/ptsнабор патчей у меня была идея:

sudo fuser -v /dev/ptmx

Я думал, что я обычно использую, чтобы связать процессы с mount? И конечно же:

                     USER        PID ACCESS COMMAND
/dev/ptmx:           root      410   F.... kmscon
                     mikeserv  710   F.... terminology

Так что с этой информацией я могу сделать, например, из terminology:

sudo sh -c '${cmd:=grep rchar /proc/410/io} && printf 1 >/dev/pts/0 && $cmd'
###OUTPUT###
rchar: 667991010
rchar: 667991011

Как вы можете видеть, при небольшом явном тестировании такой процесс может быть выполнен для довольно надежного вывода основного процесса произвольного pty. Что касается сокетов, я вполне уверен, что к этому можно подходить и в этом направлении, socatв отличие от отладчика, но мне еще предстоит выяснить, как это сделать. Тем не менее, я подозреваю, что ssможет помочь, если вы более знакомы с этим, чем я:

sudo sh -c 'ss -oep | grep "$(printf "pid=%s\n" $(fuser /dev/ptmx))"'

Итак, я настроил это на более подробное тестирование:

sudo sh <<\CMD
    chkio() {
        read io io <$1
        dd bs=1 count=$$ </dev/zero >$2 2>/dev/null
        return $((($(read io io <$1; echo $io)-io)!=$$))
    }
    for pts in /dev/pts/[0-9]* ; do
        for ptm in $(fuser /dev/ptmx 2>/dev/null)
            do chkio /proc/$ptm/io $pts && break
        done && set -- "$@" "$ptm owns $pts"
    done
    printf %s\\n "$@"
 CMD

Он печатает $$num \0null-байтов для каждого pty и проверяет io каждого главного процесса на соответствие предыдущей проверке. Если разница, $$то он связывает pid с pty. Это в основном работает. Я имею в виду, для меня это возвращает:

410 owns /dev/pts/0
410 owns /dev/pts/1
710 owns /dev/pts/2

Что правильно, но, очевидно, это немного круто. Я имею в виду, если бы один из тех других читал в кучу данных в то время, он, вероятно, пропустил бы. Я пытаюсь выяснить, как изменить sttyрежимы на другом pty, чтобы сначала отправить стоп-бит или что-то в этом роде, чтобы я мог это исправить.


2

Если вы просто ищете, кому принадлежит соединение и откуда оно подключено, команда who будет работать хорошо.

$ who
falsenames   tty8         Jun 13 16:54 (:0)
falsenames   pts/0        Jun 16 11:18 (:0)
falsenames   pts/1        Jun 16 12:59 (:0)
falsenames   pts/2        Jun 16 13:46 (:0)
falsenames   pts/3        Jun 16 14:10 (:0)
falsenames   pts/4        Jun 16 16:41 (:0)

Если вы также хотите узнать, что прослушивает это соединение, w покажет это в конце.

$ w
 16:44:09 up 2 days, 23:51,  6 users,  load average: 0.26, 0.98, 1.25
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
falsenames   tty8     :0               Fri16    2days 53:36   0.59s x-session-manager
falsenames   pts/0    :0               11:18    5:25m  1:10   1:10  synergys -a 10.23.8.245 -c .synergy.conf -f -d DEBUG
falsenames   pts/1    :0               12:59    3:44m  0.05s  0.05s bash
falsenames   pts/2    :0               13:46    2:52m  0.11s  0.11s bash
falsenames   pts/3    :0               14:10    2:17   0.07s  0.07s bash
falsenames   pts/4    :0               16:41    1.00s  0.04s  0.00s w

И чтобы получить pids, ограничьте ps сеансом tty, на который вы смотрите. Полностью ненавязчивый для загрузки.

$ ps -t pts/0 --forest 
  PID TTY          TIME CMD
23808 pts/0    00:00:00 bash
23902 pts/0    00:03:27  \_ synergys

Обратите внимание, что это может привести к красной сельди, в зависимости от времени. Но это хорошее место для начала.

$ tty
/dev/pts/4
$ ps -t pts/4 --forest
  PID TTY          TIME CMD
27479 pts/4    00:00:00 bash
 3232 pts/4    00:00:00  \_ ps
27634 pts/4    00:00:00 dbus-launch

Спасибо, но это не то, что я ищу. Выше, например, я хотел бы найти pid терминального приложения (Xterm / gnome-терминал ...), который соответствует тому /dev/pts/4, где вы выполняли эту wкоманду.
Стефан Шазелас

Извините, полностью пропустил часть pid при первом сканировании. Я думал, вы просто хотели узнать имя конечного процесса.
Falsenames

2

У меня была та же проблема с qemu, и я, наконец, нашел очень плохое решение (но все же решение): анализ памяти процесса.

Это работает здесь, потому что я знаю, что qemu хранит удаленные точки в строке с определенным форматом и размещается в куче. Может быть, он может работать и в других ситуациях с некоторыми изменениями и повторным использованием pid с выхода фьюзера (см. Другой ответ).

Код адаптирован отсюда .

#! /usr/bin/env python

import sys
pid = sys.argv[1]

import re
maps_file = open("/proc/" + pid + "/maps", 'r')
mem_file = open("/proc/" + pid + "/mem", 'r', 0)
for line in maps_file.readlines():
    # You may want to remove the 'heap' part to search all RAM
    m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r]).*\[heap\]', line)
    if m and m.group(3) == 'r':
        start = int(m.group(1), 16)
        end = int(m.group(2), 16)
        mem_file.seek(start)
        chunk = mem_file.read(end - start)
        # You may want to adapt this one to reduce false matches
        idx = chunk.find("/dev/pts/")
        if idx != -1:
            end = chunk.find("\0", idx)
            print chunk[idx:end]
maps_file.close()
mem_file.close()
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.