如果我做一个:
echo foo > /dev/pts/12
某些进程会将其foo\n
从其文件描述符读取到主端。
有没有办法找出那个(那些)进程是什么?
或者换句话说,我如何找出哪个 xterm/sshd/script/screen/tmux/expect/socat... 位于另一端/dev/pts/12
?
lsof /dev/ptmx
会告诉我在任何 pty 的主端有文件描述符的进程。进程本身可以使用ptsname()
(TIOCGPTN
ioctl )根据其自己的 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
我想我通常使用什么将进程与 a 关联起来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
2017 年,Linux 获得了一项新功能,可以稍微简化此过程(commitd01c3289e7d,适用于 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
结果是从 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
$ 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最后会显示这一点。
$ 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
答案4
我对qemu也有同样的问题,最后我找到了一个非常糟糕的解决方案(但仍然是一个解决方案):解析进程内存。
这是在这里起作用的,因为我知道 qemu 将远程 pts 存储在具有特定格式的字符串中并分配在堆上。也许它也可以在其他情况下工作,只需进行一些更改并重用来自 fusionr 输出的 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()