我们怎样才能找出对方的伪终端主机和从机呢?

我们怎样才能找出对方的伪终端主机和从机呢?

伪终端有一对主设备和从设备。

我们如何从从设备文件中找出主设备文件(例如/etc/pts/3)?我只找到/dev/ptmx/dev/pts/ptmx,但它们不能被多个从站共享。

给定主站和从站上运行的进程之一,我们如何才能找到另一个进程呢?例如,ps提供有关每个进程的控制 tty 的信息。有帮助吗?

谢谢。

答案1

这是一件比应有的事情更难的事情。

tty-index对于较新的 Linux 内核,可以从以下条目中收集与主设备配对的从设备 pty 的索引:/proc/PID/fdinfo/FD。看到这个犯罪

对于较旧的内核,实现这一点的唯一方法是通过将调试器附加到持有 master pty 的进程,并调用ptsname(3)(或直接ioctl(TIOCGPTN)调用)文件描述符。

[但这两种方法都会在使用多个 devpts 挂载的系统上遇到问题,请参见下文]

有了这些信息,您可以构建主从配对列表,这也将允许您从从属设备启动查找主设备。

这是一个应该做到这一点的愚蠢脚本;它会首先尝试该tty-index方法,如果不起作用,它将返回到gdb。对于后一种情况,需要一个在职的 gdb(不是gdb-minimal或另一个半破损的gdb大多数发行版)并且由于它的使用gdb,它将是非常慢

对于每个 pty 对,它将打印如下内容:

/dev/pts/1
    1227  3     t/ct_test
        1228  +*      t/ct_test
        1230  +      t/ct_test
/dev/pts/3
    975   9     'sshd: root [priv]' '' '' '' '' '' '' '' ''
    978   14,18,19   'sshd: root@pts/3' '' '' '' '' '' '' ''
        979   -*0,1,2,255   -bash
        1222  1     tiocsti
        1393  -0,1,2   sleep 3600
        1231  +0,2   perl ptys.pl
        1232  +1,2   cut -b1-60

两个sshd进程(pid 975 和 978)打开了主端的句柄(一个作为其 9 fd,另一个作为其 14、18 和 19 fd)。sleep-bash具有从属端的开放句柄作为其标准(0,1 和 2)fd。会话领导者 ( bash) 也用 a 标记*,前台进程 (perlcut) 也用 a 标记+,后台进程 (less-bash) 也用 a 标记-

这些t/ct_test进程使用 pty 作为其控制终端,而没有打开任何 fd。tiocsti有一个打开的句柄,但它不是其控制终端。

在 Debian 9 和 Fedora 28 上进行了测试。有关其使用的幻数的信息可以在procfs(5)Documentation/admin-guide/devices.txt在linux内核源码中。


这在任何使用 chroot 或命名空间容器的系统上都会失败;如果不对内核进行一些更改,这是无法修复的,因为没有可靠的方法来将字段匹配tty到pty,并且通过相应的安装/proc/PID/stat打开 fd 。看/dev/ptmx/dev/pts这里对此的咆哮。

这也不会链接到任何通过/dev/tty;打开的 fd这真实的tty 可以通过附加到进程并调用 来解决ioctl(fd, TIOCGDEV, &dev),但这将意味着 gdb 的另一次肮脏的大量使用,并且它将遇到与上面相同的问题,即伪 tty 从机的主要、次要数字不明确。

ptys.pl:

my (%pty, %ctty);
for(</proc/*[0-9]*/{fd/*,stat}>){
    if(my ($pid, $fd) = m{/proc/(\d+)/fd/(\d+)}){
        next unless -c $_;
        my $rdev = (stat)[6]; my $maj = $rdev >> 8 & 0xfff;
        if($rdev == 0x502){ # /dev/ptmx or /dev/pts/ptmx
            $pty{ptsname($pid, $fd, readlink $_)}{m}{$pid}{$fd} = 1;
        }elsif($maj >= 136 && $maj <= 143){ # /dev/pts/N
            $pty{readlink $_}{s}{$pid}{$fd} = 1;
        }
    }else{
        my @s = readfile($_) =~ /(?<=\().*(?=\))|[^\s()]+/gs;
        $ctty{$s[6]}{$s[0]} =       # ctty{tty}{pid} =
            ($s[4] == $s[7] ? '+' : '-').   # pgrp == tpgid
            ($s[0] == $s[5] ? '*' : '');    # pid == sid
    }
}
for(sort {length($a)<=>length($b) or $a cmp $b} keys %pty){
    print "$_\n";
    pproc(4, $pty{$_}{m}); pproc(8, $pty{$_}{s}, $ctty{(stat)[6]});
}

sub readfile { local $/; my $h; open $h, '<', shift and <$h> }
sub cmdline {
    join ' ', map { s/'/'\\''/g, $_ = "'$_'" if m{^$|[^\w./+=-]}; $_ }
        readfile("/proc/$_[0]/cmdline") =~ /([^\0]*)\0/g;
}
sub pproc {
    my ($px, $h, $sinfo) = @_;
    exists $$h{$_} or $$h{$_} = {''} for keys %$sinfo;
    return printf "%*s???\n", $px, "" unless $h;
    for my $pid (sort {$a<=>$b} keys %$h){
        printf "%*s%-5d %s%-3s   %s\n", $px, "", $pid, $$sinfo{$pid},
            join(',', sort {$a<=>$b} keys %{$$h{$pid}}),
            cmdline $pid;
    }
}
sub ptsname {
    my ($pid, $fd, $ptmx) = @_;
    return '???' unless defined(my $ptn = getptn($pid, $fd));
    $ptmx =~ m{(.*)(?:/pts)?/ptmx$} ? "$1/pts/$ptn" : "$ptmx ..?? pts/$ptn"
}
sub getptn {
    my ($pid, $fd) = @_;
    return $1 if
        readfile("/proc/$pid/fdinfo/$fd") =~ /^tty-index:\s*(\d+)$/m;
    return gdb_ioctl($pid, $fd, 0x80045430);    # TIOCGPTN
}
sub gdb_ioctl {
    my ($pid, $fd, $ioctl) = @_;
    my $cmd = qq{p (int)ioctl($fd, $ioctl, &errno) ? -1 : errno};
    qx{exec 3>&1; gdb -batch -p $pid -ex '$cmd' 2>&1 >&3 |
            grep -v '/sysdeps/.*No such file or directory' >&2}
        =~ /^\$1 *= *(\d+)$/m ? $1 : undef;
}

答案2

在Linux上,使用devpts,没有主设备文件。 master端的进程使用了​​一个文件描述符,它是通过open得到的ptmx,但是没有对应的设备节点。

联机ptmx帮助页了解详情。

(对于 Linux 上的 BSD 风格pty,主设备端和从设备端都有匹配的设备对,例如/dev/ptyp1/dev/ttyp1。)

答案3

看起来lsof +E给你了FD两端的信息,包括ptm/pts。

相关内容