無精で短気で傲慢なプログラマ

UNIX や web やプログラムの技術的なことを中心に。

biff はなぜ動く?

web の referrer を見てたら、slashdot.jp から
 UNIX の部屋: biff
へリンクが張られている。

そこでふと気づいたが、わたしは biff の動作原理を説明できない。

思い起こせば、わたしが biff 的なものに触れたのは twm 上での
xbiff がはじめてで、biff コマンドは一度試しにつかってみたことが
ある程度だったような気がする。

/var/mail/$USER などのメールボックスにアクセスしているのは誰か?
まさか biff がシェルの内部コマンドなわけでもないだろう (シェル
変数の $mail と役割がかぶってるし)。

ということは、biff y とすると常駐して、メールボックスを定期的に
ポーリングし、メールが届くと端末に出力するのか? しかし biff
コマンドが登場した 4.0BSD な時代に、そんなリソースの無駄遣いは
許されないような気がする。


とりあえず使ってみよう。
 % biff y
 % echo hoge | mail 68user@localhost

…何も起こりません。


仕方がないので FreeBSD 6.0-RELEASE の biff.c を読む。

つまるところ biff y とすると
 char *name = ttyname(STDIN_FILENO);
  if ( argv[1][0] == 'y' )
  chmod(name, (sb.st_mode & ~(S_IXUSR | S_IXGRP)) | S_IXUSR);

となるようだ。

ぱっと見、パーミッションをどう変えたいのかさっぱりわからない。
そういえば C で chmod(2) を使ったことがないような気もする。

 % tty
 /dev/ttyp0


となるので、name はおそらくこの値が入っているのだろう。chmod(2) によると、
S_IXUSR は 0000100、S_IXGRP は 0000010 なので (0 から始まる数字は 8進数)、

 chmod(name, (sb.st_mode & ~(S_IXUSR | S_IXGRP)) | S_IXUSR)



 chmod("/dev/ttyp0", /dev/ttyp0 の現在のモード & ~(0000100 | 0000010)) | 0000100)

となり、0000100 と 0000010 の論理 OR は 0000110 なので

 chmod("/dev/ttyp0", /dev/ttyp0 の現在のモード & ~(0000110)) | 0000100)

となり、~ は否定なので 0000110 のビットが逆になり 7777667 となって (int が 32bit なら
01777777777667 とかが正しいか?)

 chmod("/dev/ttyp0", /dev/ttyp0 の現在のモード & 0777667) | 0000100)

となって、ログイン直後の /dev/ttyp0 は 0620 (rw--w----) なので

 chmod("/dev/ttyp0", 0000620 & 0777667) | 0000100)

となり、0620 と 0777667 の論理 AND を取ると 0620 なので

 chmod("/dev/ttyp0", 0000620 | 0000100)

となって、0620 と 0100 の論理 OR は 0720 なので

 chmod("/dev/ttyp0", 0000720)

となると。つまり u-x して g-x して u+x しているわけか。


というわけで実行。

 % biff y
 % ls -l `tty`
 crwx-w---- 1 68user tty 5, 0 Nov 23 00:59 /dev/ttyp0


確かに rw--w---- が rwx-w---- になった。

で、これでなぜメールが届くと端末にメッセージが出力されるのか? biff コマンドが
端末のパーミッションを開けているなら、誰かそこに出力しているプロセスがあるはず。

しかたがないので biff(1) を読む。
 SEE ALSO
  csh(1), mail(1), sh(1), comsat(8)

comsat(8) ってのが怪しい。

 % whatis comsat
 comsat(8) - biff server


おお、まさにこれか。comsat(8)
 SEE ALSO
  biff(1), inetd(8)

とあるので inetd 経由で起動されるのだろう。/etc/inetd.conf を見ると、
当然ながらコメントアウトしてある (いまどきの UNIX は、標準で talk
コマンドも write コマンドも使えないのでちょっと悲しい)。
  # run comsat as root to be able to print partial mailbox contents w/ biff,
# or use the safer tty:tty to just print that new mail has been received.
#comsat dgram udp wait tty:tty /usr/libexec/comsat comsat

この行を有効にして inetd 再起動。
 # kill -HUP `cat /var/run/inetd.conf`

再度メールを送信。
 % echo hoge | mail 68user@localhost
 New mail for 68user@localhost has arrived:
 ----


これでやっと biff が機能するようになった。しかしまだ comsat をつついて
いるプロセスがわからない。inetd を有効にして biff が機能するようになった
からには、UDP な comsat ポートをつついているプロセスがいるはずである。

まーどうせローカル配信エージェント (MDA) の mail.local なんでしょ?
と思いつつソースを探したが、マニュアルしか引っかからない。
 % cd /usr/src; find contrib/sendmail/mail.local/ -type f | xargs grep comsat
 contrib/sendmail/mail.local/mail.local.8:comsat(8),


はてと思いつつ /etc/services を見る。
 % grep comsat /etc/services
 biff 512/udp comsat #used by mail system to notify users

正式名称は biff で、comsat は別名だった。

というわけで、comsat でなく biff で mail.local のソースを grep。
 % cd /usr/src; find contrib/sendmail/mail.local/ -type f | xargs grep comsat
 contrib/sendmail/mail.local/mail.local.c:notifybiff(msg)
 contrib/sendmail/mail.local/mail.local.c: /* Be silent if biff service not available. */
 contrib/sendmail/mail.local/mail.local.c: if ((sp = getservbyname("biff", "udp")) == NULL ||


うむ、満足した。


苦労して計算したが、truss(1) なら一発でパーミッションがわかる。
 % truss biff y
 ....
 stat("/dev/ttyp0",0xbfbfeb64) = 0 (0x0)
 chmod("/dev/ttyp0",020720) = 0 (0x0)


0720 じゃなくて 020720 なのは、端末がキャラクタデバイスなため。
 /usr/include/sys/stat.h より:
  #define S_IFCHR 0020000 /* character special */


ところで、なぜ truss は chmod(2) の第二引数を 8進数で表示するのか?
と思ったらちゃんと truss のソース で定義してありました。
   struct syscall syscalls[] = {
...
{ "chmod", 0, 2,
{ { String, 0 }, { Octal, 1 }}},

スポンサーサイト

PageTop

コメント


管理者にだけ表示を許可する
 

ぜひここの開発板を参照してもらいたいです
http://www.usamimi.info/~raisetu/cgi-bin/athena/r_board.cgi

LIK | URL | 2005-12-12(Mon)19:20 [編集]