web-dev-qa-db-ja.com

どうすればこのptyを手に入れることができ、どうすればよいですか?

_/dev/ptmx_からの単純なシェルリダイレクトにより、新しい疑似端末が得られるようです。

_$ ls /dev/pts; ls /dev/pts </dev/ptmx
0  1  2  ptmx
0  1  2  3  ptmx
_

_/dev/ptmx_のfdクレームを所有するプロセスが終了するとすぐに消えますが、それを保持するのに十分簡単です。

_$ PS1='<$(ls -1 "$@" /dev/pts/*|uniq -u)> $ ' \
  exec 3<>/dev/ptmx "$0" -si -- /dev/pts/*
</dev/pts/3> $ ls /dev/pts; exec 3>&-
0  1  2  3  ptmx
<> $ ls /dev/pts; exec 3<>/dev/ptmx
0  1  2  ptmx
</dev/pts/3> $ exit
_

したがって、疑似端末を取得するには、_/dev/ptmx_の単純なopen()で十分であるように見えます。私はそれがシェルを作るだろうと思います-(またはそれがリダイレクトを行うプロセス)-ptyのマスター側の所有者。

しかし、どのようにスレーブ側を割り当てるのですか?またはcanスレーブ側を割り当てますか?そして、私ができるなら-私はそれで何ができますか? sttyを使用して、読み取り/書き込み/フラッシュのタイミングに影響を与えることはできますか? _/dev/tty_を参照するときに、スレーブ側のプロセスは新しいptyにリンクしますか?

5
mikeserv

したがって、@ muruがコメントで指摘しているように、作成されたptyをシェルだけでインターフェースする簡単な方法はないようです。 unlockpt()部分以外はすべて管理しました。私が読んだものによると ここ 新しく作成されたptyロックを無効にするためのコンパイル時オプションがカーネルにあるかもしれませんが、私はそれをしたくありませんでした。それで、私は何か他のことをしました。

実際にはgrantpt()は必要ありませんでした。見つかった説明によると ここ _/dev/pts/[num]_デバイスファイルのUID/GIDを変更するだけです。しかし、_man mount_によると、それを処理する簡単な方法があります。 devptsマウントオプションは次のとおりです。

  • _uid=value_および_gid=value_
    • これにより、所有者または新しく作成されたPTYのグループが指定された値に設定されます。何も指定されていない場合は、作成プロセスのUIDとGIDに設定されます。たとえば、GID 5のttyグループがある場合、_gid=5_により、新しく作成されたPTYはttyグループ。

それは私のシステムではすでにデフォルトで当てはまりました。しかし、それを読んだ後、私は結局変更を加えたいと思うかもしれないことに気づきました。次のセクションは次のとおりです。

  • _ptmxmode=value_
    • devptsファイルシステムで新しいptmxデバイスノードのモードを設定します。
    • devptsの複数のインスタンスのサポート(上記のnewinstanceオプションを参照)により、各インスタンスには、devptsファイルシステムのルートにプライベートptmxノードがあります(通常は_/dev/pts/ptmx_)
    • 古いバージョンのカーネルとの互換性のために、新しいptmxノードのデフォルトモードは_0000_です。 _ptmxmode=value_は、ptmxノードのより便利なモードを指定し、newinstanceオプションが指定されている場合に強くお勧めします。

そうしなくても機能するはずでしたが、私はそのアイデアが気に入り、 カーネルドキュメント で推奨されているように_0640_に設定しました。ちなみに、カーネルドキュメントリンクでは、newinstanceマウントオプションについて詳しく説明しています。これは非常に優れており、基本的に、_/dev/ptmx_マウントごとに名前を空けたptyのグループを個別に取得できます。

とにかく、何か他のものは主に次のようになりました:

_mount -o remount,newinstance,gid=5,ptmxmode=0640 /dev/pts
mount --bind /dev/pts/ptmx /dev/ptmx
_

...カーネルドキュメントが推奨するように-理由についてのリンクを参照してください。また、_/etc/fstab_に数行追加することで、上記のコマンドの効果を永続的にしました。

そして...

_<<\C cc -xc - -o pts
#include <stdio.h>
int main(int argc, char *argv[]) {
        if(unlockpt(0)) return 2;
        char *ptsname(int fd);
        printf("%s\n",ptsname(0));
        return argc - 1;
}
C
_

これは、stdinでunlockpt()を呼び出そうとする小さなCプログラムをコンパイルし、成功すると、新しく作成されロック解除されたptyの名前をstdoutに出力するか、サイレントに2を返します。

それが完了すると、次のような独自のscreenedプロセスを作成できます。

_exec 3<>/dev/ptmx
_

...現在のシェルでマスター側のfdを取得するには...

_(setsid -c "$0" -i 2>&1|tee log) <>"$(./pts <&3)" 3>&- >&0 &
_

これにより、バックグラウンドで疑似端末のもう一方の端でインタラクティブシェルが実行されます。 _>&3_に出力されたものはすべてユーザー入力として解釈されます。

_mikeserv@localhost$ echo echo hey >&3
mikeserv@localhost$ cat log
$ hey
$
mikeserv@localhost$ echo echo hey >&3
mikeserv@localhost$ cat log
$ hey
$ hey
$ 
_

これは基本的に、バックグラウンドでログに記録されたインタラクティブなインタプリタ(またはこれらで実行したい他の何か)ala screenを、それほどオーバーヘッドなしで、ファイル上でネットします。私が選んだ記述子。

現在のシェルが所有するマスター側のfdは、スレーブ側の入力を提供する唯一の手段であり、現在のシェルプロセス(およびその子)によってのみ書き込み可能です。 _>&3_に書き込むことで相手と通信でき、必要に応じて同じファイルまたはログファイルから読み取ることができます。

そして、sttyは結局ターミナルで機能します:

_mikeserv@localhost$ echo stty -a ">$(tty)" >&3
speed 38400 baud; rows 0; columns 0; line = 0;                                      
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>;
swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc
-ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl
echoke
_
4
mikeserv