
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) を読む。
comsat(8) ってのが怪しい。
% whatis comsat
comsat(8) - biff server
おお、まさにこれか。comsat(8) に
とあるので inetd 経由で起動されるのだろう。/etc/inetd.conf を見ると、
当然ながらコメントアウトしてある (いまどきの UNIX は、標準で talk
コマンドも write コマンドも使えないのでちょっと悲しい)。
この行を有効にして 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 のソース で定義してありました。
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 }}},
スポンサーサイト


