의사 단말 장치의 반대편에 누가 있는지 어떻게 알 수 있나요?

의사 단말 장치의 반대편에 누가 있는지 어떻게 알 수 있나요?

내가 다음을 수행하면:

echo foo > /dev/pts/12

일부 프로세스는 foo\n파일 설명자에서 마스터로 이를 읽습니다.

해당 프로세스가 무엇인지 알아낼 수 있는 방법이 있나요?

또는 다른 말로 하면, 어느 xterm/sshd/script/screen/tmux/expect/socat...가 반대편에 있는지 어떻게 알 수 있습니까 /dev/pts/12?

lsof /dev/ptmxpty의 마스터 측에 파일 설명자가 있는 프로세스를 알려줍니다. 프로세스 자체는 ptsname()( TIOCGPTNioctl )을 사용하여 자체 fd를 기반으로 마스터 장치를 찾을 수 있으므로 다음을 사용할 수 있습니다.

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

이 맵은 반환된 각 pid/fd에 대해 작성되었지만 lsof이 정보를 얻는 더 직접적이고 안정적이며 덜 침해적인 방법이 있습니까?

답변1

처음에는 찾은 정보를 바탕으로 pid를 추적해 보았지만 xterm느슨했습니다. 내 말은, 작동한다고 생각하지만 기껏해야 상황에 따른 것입니다. 해당 파일에서 제공하는 모든 정보를 완전히 이해하지 못하고 해당 내용과 알려진 터미널 프로세스 간에 일치하는 것으로 보이는 것만 일치합니다.xterm/proc/locks

그런 다음 pty 사이의 lsof/strace활성 프로세스를 관찰하려고 합니다. write/talk나는 이전에 두 프로그램을 실제로 사용한 적이 없지만 그것에 의존하는 것 같습니다 . 어떤 이유로 utmp내 대상 pty에 항목이 없으면 둘 다 그 존재를 인정하지 않습니다. utmp어쩌면 이 문제를 해결할 수 있는 방법이 있을지도 모르지만, 혼란스러워서 포기했습니다.

나는 udevadm각각 및 에 광고된 136개 및 128개의 마스터 장치 노드를 사용하여 몇 가지 발견을 pts시도 ptm했지만 /proc/tty/drivers해당 도구에 대한 매우 유용한 경험이 부족했으며 다시 한 번 실질적인 내용을 찾지 못했습니다. 그러나 흥미롭게도 :min두 가지 장치 유형 모두 놀라운 가격으로 나열되어 있다는 것을 알았습니다 0-1048575.

이걸 다시 보기 전까지는이 커널 문서그러나 나는 이 문제를 s의 관점에서 생각하기 시작했다 mount. 나는 이전에 이 내용을 여러 번 읽었지만 해당 분야에 대한 지속적인 연구를 통해 이것을 발견하게 되었습니다.이 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

각 pty에 $$num null 바이트를 인쇄 \0하고 이전 확인과 비교하여 각 기본 프로세스의 io를 확인합니다. 다른 경우 $$pid를 pty와 연결합니다. 이것최대일하다. 내 말은, 나에게는 다음과 같이 반환됩니다.

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

맞는 말이긴 한데, 분명히 조금은 부자연스럽습니다. 내 말은, 그들 중 하나가 그 당시에 많은 양의 데이터를 읽고 있다면 그것을 놓칠 수도 있다는 것입니다. stty문제를 해결할 수 있도록 정지 비트가 먼저 전송되도록 다른 pty에서 모드를 변경하는 방법을 알아내려고 노력 중입니다 .

답변2

2017년에 Linux에는 이 프로세스를 약간 더 쉽게 만드는 새로운 기능이 추가되었습니다(커밋d01c3289e7d, Linux 4.14 이상의 경우)

열린 프로세스 목록을 얻은 후 /dev/ptmx:

$ fuser dev/ptmx
/dev/ptmx:           1330334 1507443

pts다음과 같은 번호를 받을 수 있습니다.

for pid in $(fuser /dev/ptmx 2>/dev/null); do grep -r tty-index /proc/$pid/fdinfo; done
/proc/1330334/fdinfo/13:tty-index:  0
/proc/1330334/fdinfo/14:tty-index:  1
/proc/1330334/fdinfo/27:tty-index:  2
/proc/1330334/fdinfo/28:tty-index:  4
/proc/1507443/fdinfo/3:tty-index:   3

결과는 <pid>:<ptmx fd>a에서 해당 항목으로의 매핑 입니다./dev/pts/<index>

버전 4.90부터, lsof이 API를 사용하여 상대방에게 보고 /dev/ptmx하고 파일을 사용/ /dev/pts/x열 수 있습니다.-E+E

$ lsof -E -ad 0 -p $$
COMMAND   PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
zsh     14335     user    0u   CHR  136,8      0t0   11 /dev/pts/8 14333,xterm,5u
$ lsof +E -ad 0 -p $$
COMMAND   PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
xterm   14333     user    5u   CHR    5,2      0t0   87 /dev/ptmx ->/dev/pts/8 14335,zsh,0u 14335,zsh,1u 14335,zsh,2u 14335,zsh,10u 14391,lsof,0u 14391,lsof,1u 14391,lsof,2u
zsh     14335     user    0u   CHR  136,8      0t0   11 /dev/pts/8 14333,xterm,5u

답변3

누가 연결했는지, 어디서 연결했는지 알아낸다면,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
 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

PID를 얻으려면 현재 보고 있는 tty 세션으로 ps를 제한하십시오. 부팅이 전혀 눈에 띄지 않습니다.

$ 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

답변4

나는 qemu와 동일한 문제를 겪었고 마침내 매우 나쁜 해결책(그러나 여전히 해결책)을 찾았습니다: 프로세스 메모리를 구문 분석합니다.

이것은 qemu가 원격 pt를 특정 형식의 문자열로 저장하고 힙에 할당한다는 것을 알고 있기 때문에 여기서 작동합니다. 아마도 다른 경우에도 작동할 것입니다. 몇 가지 사항을 변경하고 퓨전 출력의 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()

관련 정보