/dev/tty

先日 のエントリで「tty デバイスに...」と書いたのだが*1、tty とは何? たまに聞くことはあっても、よくわかっていないのだ。
tty について調べていると、次のようなことができるとわかった。

% tty                             # 自分の制御端末を調べる
/dev/pts/0
% echo "hello world" > /dev/pts/0 # 自分の制御端末にデータを書き込む
hello world                       # 書き込んだデータが表示される (echo の出力が表示されたわけではない)

ひとつの端末でやるとよくわからないんだけど、2 つ使うとわかりやすいと思う。

そういえば、マシンを shutdown させるとき次のようなメッセージが端末に表示される。

hogehoge@fugafuga からのブロードキャスト・メッセージ
	(/dev/pts/1) at 21:22 ...

The system is going down for reboot NOW!

今までどうやって表示させているのかわからなかったけど、何となくわかった気がする。ので、shutdown コマンドがどのようにメッセージを各端末に送っているのかを調べてみた。ソースは http://launchpad.net/upstart/0.6/0.6.3/+download/upstart-0.6.3.tar.gz
shutdown.c を追ってみると wall() という関数がどうもそれくさい。以下は、shutdown.c の 732 行目から 767 行目を抜き出してみた。

/* Iterate entries in the utmp file */
setutxent ();
while ((ent = getutxent ()) != NULL) {
  char dev[PATH_MAX + 1];
  int  fd;

  /* Ignore entries without a name, or not a user process */
  if ((ent->ut_type != USER_PROCESS)
      || (! strlen (ent->ut_user)))
    continue;

  /* Construct the device path */
  if (strncmp (ent->ut_line, DEV "/", 5)) {
    snprintf (dev, sizeof (dev),
        "%s/%s", DEV, ent->ut_line);
  } else {
    snprintf (dev, sizeof (dev), "%s", ent->ut_line);
  }

  alarm (2);
  fd = open (dev, O_WRONLY | O_NDELAY | O_NOCTTY);
  if ((fd >= 0) && isatty (fd)) {
    FILE *term;

    term = fdopen (fd, "w");
    if (term) {
      fprintf (term, "\007\r\n%s\r\n\t%s\r\n\r\n",
         banner1, banner2);
      fputs (message, term);
      fflush (term);
      fclose (term);
    }
  }
  alarm (0);
}
endutxent ();

getutxent() で現在ログイン中のユーザの情報を得る。ログイン中のユーザの情報は /var/run/utmp にある。これでユーザがどの端末を使っているのかがわかるので、その端末に向けてメッセージを送っている。

大体予想通りの仕組みになっていた。ただ、ほんの 35 行程抜き出しただけなのに、知らない関数が多くあった。普通のアプリを作るだけじゃなく OS 寄りのアプリ (?) を書いて(読んで)みるのもいい刺激になるかも。


で、結局 tty ってなんだっけ?制御端末?

*1:引用部だけど