Ubuntu Weekly Recipe

第497回 トランプ大統領のツイートをbiffで通知する

この記事を読むのに必要な時間:およそ 6 分

先週アメリカ合衆国のトランプ大統領が来日しましたね。そこで今回はトランプ大統領のツイートを「biff」で通知してみましょう。

そもそも「biff」とは

「biff」とはBSD由来のメール通知システムです。通知を受け取るかどうかを設定するbiffコマンドと実際に通知を発行するcomsatデーモンが協調して,以下のような流れでユーザーにメールが届いたことを通知します。

  1. comsatデーモンは定期的に/var/run/utmpからログインユーザーのリストを取得しておく
  2. MTAがメールを受け取る
  3. MTAがcomsatデーモンに「受信者」⁠メールボックスファイル名」⁠受信したメールのファイル上のオフセット」をUDPで通知する
  4. comsatデーモンは「受信者」がログインしているか確認する
  5. comsatデーモンは受信者が使用している端末ファイル/dev/ttyX/dev/pts/Xのbiffビットが立っているか確認する
  6. biffビットが立っている端末ファイルに対して,メールの「From:」「Subject:」⁠さらに本文の一部を書き込む
  7. 先頭7行もしくは560文字の小さいサイズに到達した時点で書き込みを終了する

要するに「エディターを開いていようが何していようが,問答無用で(仮想)端末デバイス上にメール着信通知を行うシステムです。つまり古いUnix環境であれば便利だったかもしれないツールではあるものの,現在の環境ではほぼ役に立たないというかむしろ邪魔になるツールです。そのため現在ではまず使われていませんし,512番ポートが開いていることも稀でしょう※1)⁠

※1
512/UDPはcomsatデーモン用にwell-knownポートとして登録されているため,他のサービスに使われることも基本的にはありません。ちなみに512/TCPはリモートマシンでコマンドを実行するrexec(Remote Process Execution)で使われるポートのようです。こちらもセキュリティな観点から,現在では使われなくなっています。

しかしながら「メールを受信したらユーザーに通知する」というアイデアそのものは今でも有用です。xbiffやgnubiffといった「ユーザーの邪魔をしないような」単体の通知システムが実装されましたし,今ではメールクライアントそのものにも通知システムが搭載されていることが一般的です。

ところでこのオリジナルのbiffコマンドとcomsatデーモンそのものはパッケージリポジトリに残っています。そこで今回はあえてこのbiffコマンドを使うことで,昔の環境ではどのように「You've Got Mail」していたのか体験してみることにしましょう。

正直に言うと「トランプ大統領とbiff」という話をやりたかっただけです※2)⁠

※2
トランプ大統領とbiffの関係性がわからないよいこのみんなはビフ・タネン「ググって」ください。

biffのインストール

biffとcomsatデーモンはともにbiffパッケージに含まれています。よってインストールは簡単です。

$ sudo apt install biff
(中略)
biff (1:0.17.pre20000412-5) を設定しています ...
grep: /etc/inetd.conf: そのようなファイルやディレクトリはありません
grep: /etc/inetd.conf: そのようなファイルやディレクトリはありません

最後にエラーが出ていますが,comsatはinetd経由で起動するサービスであるにも関わらず最近のシステムだとinetdがインストールされていないことが原因です。/etc/inetd.confの中身を確認した上でupdate-inetdコマンドを実行しようとするものの,/etc/inetd.confが存在しないために上記のようなgrepコマンドのエラーが出ています。本来はbiffパッケージがxinetdなどに依存しておくべきですが,今回はinetdを使わないためそのままにしておきます。

inetdとsystemd

そう,inetdです。comstatは/etc/inetd.confに以下の項目を記述することでサービスを有効化します。

biff    dgram   udp wait    root.tty    usr/sbin/in.comsat  comsat

/etc/servicesファイルを確認するとbiffサービスについて記載されていることがわかります。

$ grep biff /etc/services
biff            512/udp         comsat

inetd経由で起動するサービスの特徴のひとつが,⁠起動したサービスの標準入出力がクライアント側のソケットの入出力に繋がっている」ことです。つまりサービスプロセスが標準入力から(0番のファイルディスクリプターから)読み込むと,それはクライアントから送られてきたデータを読み込むことになりますし,標準出力に書き出したデータはネットワーク経由でクライアントに送られることになります。実際にcomsatのソースコードには,以下のようなコードがあります。

cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
if (cc <= 0) {
    if (errno != EINTR) sleep(1);
    continue;
}

このためinetd用のサービスをそのまま実行してもうまく動きません。comsatデーモンを動かすためにはinetdサービスが必要になります。ここで素直にxinetdをインストールしてもいいのですが,実はsystemdはinetdのようなsocketドリブンのサービスを作ることも可能なのです。今回はみんな大好きsystemdを使って実現してみましょう。

まずsocket unitを作成します。/etc/systemd/system/biff.sockを次の内容で作成します。

[Unit]
Description=Biff Comsat

[Socket]
ListenDatagram=[::1]:512

[Install]
WantedBy=sockets.target

内容はごくごくシンプルで,⁠localhostの512ポートで待ち受けるsocket unit」です。ListenDatagramでデータグラムのsocketにしています。ストリームにしたい場合は,ListenStreamにしてください。またその場合は,Acceptも設定したほうがいいでしょう。

ListenDatagramのアドレス部分には[::1]:512を指定しています。ここは次のようなルールになっています※3)⁠

※3
詳細はman systemd.socketを参照してください。
  • 「/」で始まる場合はUnixドメインソケットのパスとして使われます
  • 「@」で始まる場合はLinuxの抽象ソケットアドレスとして扱います
  • 単一の数字の場合はポート番号として扱い,システム上のすべてのインターフェースをlistenします
  • 「A.B.C.D:PORT」の場合はIPv4アドレスとポート番号として指定したアドレスでlistenします
  • IPv6表記の場合はIPv6でlistenしますが,設定によってはIPv4アドレスも同時にlistenします

上記の例ではIPv6表記のループバックアドレスを指定しているため,ローカルからIPv6と設定次第ではIPv4でもアクセスできることになります。

次にこのsocket unitにパケットが届いたときにアクティベートする本体のサービスファイルを/etc/systemd/system/biff.serviceとして作成します。

[Unit]
Description=Biff Service
Requires=biff.socket

[Service]
Type=simple
ExecStart=/usr/sbin/in.comsat
StandardInput=socket
StandardError=journal
TimeoutStopSec=5

[Install]
WantedBy=multi-user.target

見た目はごくごく普通のサービスファイルですね。comsatデーモン自身はforkしないのでサービスタイプはType=simpleです。またRequiresには先程作成したbiff.socketを指定しています。

inetdライクなサービスにするためにStandardInput=socketを設定しています。これによりinetdと同じく標準入力がsocketにつながることになります。ちなみにcomsatデーモンは何もデータを返しません(標準出力に対してsend()write()を呼びません)⁠よってStandardOutputは設定していません。一般的なinetdサービスであれば,StandardOutputも設定する必要があるでしょう。

biff.serviceはbiff.socketによってアクティベートされるので,biff.socketのみ起動しておきましょう。

$ sudo systemctl daemon-reload
$ sudo systemctl start biff.socket

これで512番ポートにパケットが届くとcomsatが起動するようになりました。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。

コメント

コメントの記入