Subscribed unsubscribe Subscribe Subscribe

gnome-pty-helperを読んでみた

sendmsg FDPassing GNOME

GNOME端末を使っていると、gnome-pty-helperという謎のプロセスが立ち上がっているのに気づく。このプロセスは一体何をやっているのだろうか。
f:id:moriyoshi:20070419192745p:image
gnome-pty-helper は次の処理を行うヘルパープログラムだ。

  1. 擬似端末を確保する。
  2. 擬似端末の権限を適切に設定する。
  3. utmp エントリを更新し、wtmp エントリを追加する。 (ログイン)
  4. セッションをキープする。
  5. utmp エントリを更新し、wtmp エントリを追加する。 (ログアウト)
  6. (擬似端末の権限を元に戻す) ← どこ?

2. 6. の操作に pty デバイスノードの所有者の権限が必要なのと、3. 5. の操作に utmp / wtmp を更新できる権限が必要だが、GNOME端末を起動したユーザ権限でそれらが行えるとは限らないのでヘルパーが必要になるというわけ。

gnome-pty-helper は 2. の操作が行えるように suid されていることがあるけど、これは devpts の導入により不要になる。devpts が適切な権限の設定されたノードをアトミックに生成できるからだ。また同様に 3. と 5. の操作が行えるよう wtmp グループなどに sgid されている。

ここで gnome-pty-helper が端末のディスクリプタを確保するのはいいとして、一体どうやって呼出元のGNOME端末にそれを渡すのかという疑問が生じるが、なんと、というか、やはりというか、 sendmsg() を使ってディスクリプタを渡しているのだ。

Author: Miguel de Icaza (miguel@ぐにゅー.org)

Parent application talks to us via a couple of sockets that are strategically placed on file descriptors 0 and 1 (STDIN_FILENO and STDOUT_FILENO).

We use the STDIN_FILENO to read and write the protocol information and we use the STDOUT_FILENO to pass the file descriptors (we need two different file descriptors as using a socket for both data transfers and file descriptor passing crashes some BSD kernels according to Theo de Raadt)

A sample protocol is used:


OPEN_PTY => 1
=> 0

CLOSE_PTY => void


<tag> is a pointer. If tag is NULL, then the ptys were not allocated.
ptys are passed using file descriptor passing on the stdin file descriptor

We use as little as possible external libraries.

gnome-pty-helper.c上部のコメントより

ところで気になるのは 6. の操作。OpenSSH では pty_release (sshpty.c) で元に戻しているよう見受けられるけど、gnome-pty-helper の場合はどこなんだろう?

追記: Konsole の場合はどうしているか調べてみたところ

  1. grantpt() が動くならそれを利用する。
  2. 素の openpty() が自動的に権限を適切に設定できるなら (grantpt() / revoke() を使って) それを利用する。
  3. 以上だめなら konsole_grantpt というヘルパーに頼る。

のようになっているようだ。ちなみに utmp / wtmp の書き出しは端末エミュレータ本体がやっている模様。

なお、grantpt() の実装は NetBSD の場合次のようになっていた。ioctl() を使った権限管理って結構見掛けるけど、ちょっとこわい。
pty.c*1:

int
grantpt(int fildes)
{

	return ioctl(fildes, TIOCGRANTPT, 0);
}

追記2: pseudo terminal は擬似端末と書くのが正しいのについつい「仮想端末」と書いてしまったので置き換えました。混乱した方すみませんでした。

*1:全くの余談、File Descriptor => fildes の略が結構好き。「モテるオヤジは全員FILDESを装着」とか書いても違和感がないところが特に。