我们怎么知道谁在伪终端设备的另一端?


26

如果我这样做:

echo foo > /dev/pts/12

某些进程foo\n将从其文件描述符读取该消息到主端。

有没有办法找出那个过程是什么?

换句话说,我如何找出哪个xterm / sshd / script / screen / tmux / expect / socat ...位于另一端/dev/pts/12

lsof /dev/ptmx会告诉我在任何pty的主端都有文件描述符的进程。进程本身可以使用ptsname()TIOCGPTNioctl)根据自身对主端的fd查找从属设备,因此我可以使用:

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

对于lsof建立该映射所返回的每个pid / fd ,但是是否有更直接,可靠和较不麻烦的方式来获取该信息?


这是你想要的吗?sudo find /proc/*/fd/0 -ls | grep '/dev/pts/4',将提供PID(/proc/PID)列表作为输出。
slm

@slm,不,换句话说,我想找出哪个xterm / sshd / script / screen / tmux / expect / socat ...位于的另一/dev/pts/4。通常,这将是那些/dev/pts/4开放但不一定的流程的共同祖先。
斯特凡Chazelas

1
使用套接字的情况甚至更糟-您需要一个内核调试器!
吉尔斯(Gillles)“所以-不要再邪恶了”

1
@Falsenames-我理解这个问题的意思-也许是错误的-不是将读取的数据传递给哪个进程(例如,在终端中调用的第一个shell),而是实际从主端读取它的进程。例如,如果我在中启动外壳程序screen,那就是screen在设备的生命周期内分配并积极管理pty从站,但是-正如我认为-该外壳程序是该tty的进程领导者,因此,输出显示,你bash或任何从ps没有screen。我追溯了一些基于xtermsxtermpid,/proc/locks但它很松散。
mikeserv 2014年

Answers:


3

最初,我尝试根据发现的信息xterm将s追溯到xtermpid,/proc/locks但这很松散。我的意思是,它起作用了,但我认为它只是出于环境的考虑-我不完全理解文件提供的所有信息,而只是匹配文件内容和已知终端进程之间似乎对应的内容。

然后,我尝试观察pty之间lsof/strace的活动write/talk进程。我以前从未实际使用过任何程序,但是它们似乎依赖于utmp。如果我的目标个人utmp出于任何原因没有条目,他们都会拒绝承认该条目的存在。也许有办法解决,但我很困惑,无法放弃。

我尝试分别在udevadm136和128个主要设备节点上进行发现ptsptm分别在中进行了发现/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

它向每个pty 打印$$num个\0空字节,并根据先前的检查来检查每个主进程的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

为了获得pid,请将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

谢谢,但这不是我想要的。例如,在上方,我想找到与终端应用程序(Xterm / gnome-terminal ...)对应的pid /dev/pts/4,您在其中运行了该w命令。
斯特凡Chazelas

抱歉,我第一次扫描时完全错过了pid部分。我以为您只是想知道结束进程的名称。
假名2014年

2

我在qemu上遇到了同样的问题,我终于找到了一个非常糟糕的解决方案(但仍然是一个解决方案):解析进程内存。

这在这里起作用,因为我知道qemu将远程pts存储在具有特定格式的字符串中,并在堆上分配。只需稍作更改,然后重新使用定影器输出中的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()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.