我们如何知道谁在伪终端设备的另一端?

Sté*_*las 29 terminal-emulator open-files pty

如果我做:

echo foo > /dev/pts/12
Run Code Online (Sandbox Code Playgroud)

某些进程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)"
Run Code Online (Sandbox Code Playgroud)

对于lsof构建该映射而返回的每个 pid/fd ,是否有更直接、可靠且侵入性更小的方法来获取该信息?

mik*_*erv 5

起初,我尝试根据我找到的信息追踪 pid xterm,但它是松散的。我的意思是,我认为它有效,但充其量只是环境 - 我不完全理解该文件提供的所有信息,并且仅匹配其内容和已知终端进程之间似乎对应的信息。xterm/proc/locks

然后我尝试观察pty 之间的lsof/strace活动write/talk进程。我以前从未真正使用过这两个程序,但它们似乎依赖于utmp. 如果我的目标 pty 出于某种原因没有utmp条目,他们都拒绝承认它的存在。也许有办法解决这个问题,但我很困惑而放弃了它。

我尝试udevadm使用 136 和 128 个主设备节点进行一些发现,分别在pts和中进行了广告,但我也缺乏使用该工具的任何非常有用的经验,并且再次没有发现任何实质性内容。但有趣的是,我注意到这两种设备类型的范围都以惊人的价格列出。ptm/proc/tty/drivers:min0-1048575

不过,直到我重新审视这个内核文档,我才开始从mounts 角度思考这个问题。我之前已经读过好几次了,但是当对该行的持续研究引导我找到这个 2012 年/dev/pts补丁集时,我有了一个想法:

sudo fuser -v /dev/ptmx
Run Code Online (Sandbox Code Playgroud)

我想我通常用什么来将进程与 关联起来mount果然:

                     USER        PID ACCESS COMMAND
/dev/ptmx:           root      410   F.... kmscon
                     mikeserv  710   F.... terminology
Run Code Online (Sandbox Code Playgroud)

因此,有了这些信息,我可以做的事情,例如terminology

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

正如您所看到的,通过一些显式测试,可以使这样的过程非常可靠地输出任意 pty 的主进程。关于套接字,我相当确定可以从那个方向接近它,也可以使用socat而不是调试器,但我还没有弄清楚如何实现。不过,我怀疑ss如果你比我更熟悉它可能会有所帮助:

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

因此,我实际上进行了一些更明确的测试来设置它:

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
Run Code Online (Sandbox Code Playgroud)

它向每个 pty 打印$$num\0个空字节,并根据之前的检查检查每个主进程的 io。如果不同,$$则它将 pid 与 pty 相关联。这大多有效。我的意思是,对我来说,它返回:

410 owns /dev/pts/0
410 owns /dev/pts/1
710 owns /dev/pts/2
Run Code Online (Sandbox Code Playgroud)

这是正确的,但是,显然,这有点活泼。我的意思是,如果其中一个当时正在读取一堆数据,它可能会错过。我正在尝试找出如何更改stty另一个 pty 上的模式,以便首先发送停止位或类似的内容,以便我可以修复该问题。


小智 2

2017 年,Linux 获得了一项新功能,可以稍微简化此过程(提交d01c3289e7d,在 Linux 4.14 及更高版本中可用)

获取打开的进程列表后/dev/ptmx

$ fuser dev/ptmx
/dev/ptmx:           1330334 1507443
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

结果是从 a<pid>:<ptmx fd>到相应的映射/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
Run Code Online (Sandbox Code Playgroud)
$ 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
Run Code Online (Sandbox Code Playgroud)