sendmailのQueueファイルロック処理
外部プログラムからQueueファイルを直接処理する必要があったので
調べた結果をメモ。
同様の処理をしているプログラムとして contrib/qtool.pl 等があるが
これは、一部の環境では動かない。(理由は後述)
contrib以下だし2002年から更新されていないのでそんな物かもしれない。
Queue処理にて使用されるファイルは基本的に以下の3種類。
プレフィックスでファイルの種類が付きその後にユニークなIDが付加される。
dfファイルが更新されるのはQueueに入ったときのみなので、このファイル
のmtimeを調べることで滞留時間も測定可能。
qfファイル ヘッダ+制御情報 dfファイル メールBODY tfファイル qfファイル更新時に使用される、renameにてatomicにqfを置き換える
sendmailのQueue処理は並列で行う事を想定しているため
以下のような手順を踏んで排他処理をしている。
- sendmailのQueueファイル処理ロック取得
1. qfファイルをopen 2. qfファイルをlock、失敗したらlock失敗 3. qfファイルをstat 4. 1 にて取得したファイルディスクリプタに対してfstat 5. 3と4にて取得したstat結果を比較し、違いがあった場合はlock失敗 6. ロック成功
また更新時は以下のような手順でqfファイルの置き換えが行われる。
- sendmailのqfファイル更新時の処理(配送結果反映等)
0. 処理中なので既にqfファイルに対してlockは保持している 1. tfファイルを作成しlockを取得する 2. tfファイルに対して更新後のqfデータを書き込む 3. tfファイルをqfファイルにrenameしてatomicに上書きする 4. qfファイル(renameで潰された方)のファイルディスクリプタをclose
この様になっているので、外部プログラムからQueueファイルを弄る場合にも
同様の手順でロックを取得すれば問題ないことになる。が、一つ落とし穴があり
sendmailは環境によって、flockとfcntlのどちらかを使用してロックを取得している。
該当箇所は sendmail/conf.c:lockfile 関数。
処理としてはどちらもアドバイザリロックで、かつflockとfcntlで別に取得すると
排他が保証されない(fcntlで取得してもflockでは見えない)ので、qtool.pl の様に
flockのみを使用したコードでは一部の環境では動かない事になる。
(少なくともlinuxでは動かない)
sendmailがどちらを使用しているかはコンパイル時に決まる為、バイナリに HASFLOCK が
含まれていればflock、それ以外ならfcntlとなる。
strings sendmail | grep HASFLOCK 等で判別可能。
また、実装時に注意する点としてはqfファイルは処理する度に上記手順で置き換えられるので
ロック取得時に待つのは意味がない。(取得出来た時点で既に破棄されたqfファイルなので)
そのため、ノンブロッキング(flockのLOCK_NB、fcntlのF_SETLK)でビジーループをまわす
必要がある。