疑似端末には、マスターとスレーブのペアがあります。
スレーブデバイスファイル(例:/etc/pts/3
)からマスターデバイスファイルを見つけるにはどうすればよいですか? /dev/ptmx
と/dev/pts/ptmx
しか見つかりませんが、複数のスレーブで共有することはできません。
マスターとスレーブで動作しているプロセスの1つを考えると、他のプロセスをどのように見つけることができますか?たとえば、ps
は、各プロセスの制御ttyに関する情報を提供します。それは役に立ちますか?
ありがとう。
それは、あるべきよりも難しいことの1つです。
新しいLinuxカーネルでは、マスターとペアになっているスレーブptyのインデックスは、_tty-index
_の_/proc/PID/fdinfo/FD
_エントリから収集できます。これを参照してください commit 。
古いカーネルでは、それを取得する唯一の方法は、マスターptyを保持するプロセスにデバッガーでアタッチし、ファイル記述子でptsname(3)
(または直接ioctl(TIOCGPTN)
)を呼び出すことです。
[ただし、両方の方法で、複数のdevptsマウントを使用するシステムで問題が発生します。以下を参照してください]
その情報を使用して、マスターとスレーブのペアのリストを作成できます。これにより、スレーブから起動するマスターを検索することもできます。
これはそれを行うべきばかげたスクリプトです。最初に_tty-index
_の方法を試し、それが機能しない場合はgdb
にフォールバックします。後者の場合、workinggdb
(_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
_
2つのsshd
プロセス(pid 975および978)には、マスター側へのオープンハンドルがあります(1つは9 fdとして、もう1つは14、18、および19 fdsとして)。 sleep
および_-bash
_には、標準(0、1、および2)fdsとしてスレーブ側へのオープンハンドルがあります。セッションリーダー(bash
)には_*
_、フォアグラウンドのプロセス(Perl
およびcut
)には_+
_、バックグラウンドのプロセス(less
および_-bash
_)にはマークが付けられます。 _-
_。
_t/ct_test
_プロセスは、fdを開かずに、制御端末としてptyを使用しています。 tiocsti
は、制御端末ではなく、ハンドルが開いています。
Debian9およびFedora28でテスト済み。使用している魔法数に関する情報は、Linuxカーネルの procfs(5)
および _Documentation/admin-guide/devices.txt
_ にあります。ソース。
これは、chrootまたは名前空間コンテナを使用するシステムでは失敗します。 _/proc/PID/stat
_からptyへのtty
フィールドを一致させる信頼できる方法がなく、fdが_/dev/ptmx
_を介して対応する_/dev/pts
_マウントに開かれるため、カーネルにいくつかの変更を加えることなく修正することはできません。それについての暴言については ここ を参照してください。
これは、_/dev/tty
_を介して開かれたfdにもリンクされません。 realttyは、プロセスにアタッチして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;
}
_
Linuxでは、devpts
を使用すると、マスターデバイスファイルはありません。マスター側のプロセスは、ptmx
を開くことで取得するファイル記述子を使用しますが、対応するデバイスノードはありません。
詳細については、 ptmx
マンページ を参照してください。
(Linux上のBSDスタイルのpty
sでは、マスター側とスレーブ側にそれぞれ/dev/ptyp1
と/dev/ttyp1
などの一致するデバイスペアがあります。)