C言語でprefork型のデーモンを書く(4): init スクリプト
prefork して、シャットダウン可能なデーモン本体ができたので、次はinit スクリプトを用意する。
適当なサンプルは、/etc/init.d/ 以下にたくさんあるのでそれを参考に書いてみた。
デーモン起動時に /var/run/my_prefork_daemon.pid が自動作成されるのを利用して、起動中かどうかを判断し重複起動の防止を行い、シャットダウン時には .pid ファイルを削除している。
- initスクリプト作成
以下のスクリプトをchmod 755して、/etc/init.d/my_prefork_daemon として保存する。 Cで書いた my_prefork_daemon 本体へのパスは要変更。
75,25 という数字は、起動、停止 順序を決める数字(75が起動、25が停止)。前提となるサービスがある場合には、そのサービスの後に起動され、そのサービスの前に停止するように、数字を大きく/小さくする必要がある。
#! /bin/bash # # my_prefork_daemon # # chkconfig: 2345 75 25 # description: foo bar # # Source function library. . /etc/rc.d/init.d/functions prog="my_prefork_daemon" RETVAL=0 start() { echo -n $"Starting $prog: " if [ -e /var/run/$prog.pid ]; then failure echo echo "cannot start $prog: $prog is already running."; return 1 fi daemon /PATH/TO/YOUR/$prog && success RETVAL=$? echo return $RETVAL } stop() { echo -n $"Stopping $prog: " if [ ! -e /var/run/$prog.pid ]; then failure echo echo $"cannot stop $prog: $prog is not running." return 1; fi kill `cat /var/run/$prog.pid` && success RETVAL=$? echo [ $RETVAL -eq 0 ] && rm -f /var/run/$prog.pid return $RETVAL } restart() { stop start } case "$1" in start) start ;; stop) stop ;; restart) restart ;; status) if [ -f /var/run/$prog.pid ]; then echo $"$prog is running." RETVAL=0 else echo $"$prog is not running." RETVAL=3 fi ;; *) echo $"Usage: $0 {start|stop|restart|status}" exit 1 esac exit 0
- chkconfig
init スクリプトができたら、chkconfig で自動起動の設定。
サービスに追加 # chkconfig --add my_prefork_daemon 自動起動の設定 # chkconfig my_prefork_daemon on サービス一覧表示 # chkconfig --list 〜前略〜 crond 0:off 1:off 2:on 3:on 4:on 5:on 6:off my_prefork_daemon 0:off 1:off 2:on 3:on 4:on 5:on 6:off network 0:off 1:off 2:on 3:on 4:on 5:on 6:off postfix 0:off 1:off 2:on 3:on 4:on 5:on 6:off sshd 0:off 1:off 2:on 3:on 4:on 5:on 6:off 〜後略〜
- service コマンドで起動、停止のテスト
# service my_prefork_daemon start Starting my_prefork_daemon: [ OK ] # service my_prefork_daemon restart Stopping my_prefork_daemon: [ OK ] Starting my_prefork_daemon: [ OK ] # service my_prefork_daemon stop Stopping my_prefork_daemon: [ OK ] # service my_prefork_daemon status my_prefork_daemon is not running.
これでマシン起動時に自動で my_prefork_daemon が起動され、マシン停止時には自動でシャットダウンされる。
参考
C言語でprefork型のデーモンを書く(1): 非デーモン prefork サンプル - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(2): 非デーモン prefork シグナルハンドラ付き - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(3): デーモン化 - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(3): デーモン化
prefork して、シグナルで綺麗に終了できるようになったので次はデーモン化する。デーモンをkill しやすいように プロセスIDをファイルに書いておくwrite_pid()関数と、デーモン化関数daemonize()を追加している。
- my_prefork_daemon.c
デーモン化の処理が入ったバージョンのソース
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <apr_hash.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <sys/wait.h> #include <signal.h> #define MAX_CHILDREN 16 //子プロセスの数 #define PID_FILE "/var/run/my_prefork_daemon.pid" void kill_all_children(int); void signal_handler(int); void write_pid(); void daemonize(); // 子プロセスの管理にハッシュテーブルを使う static apr_pool_t* pool; static apr_hash_t* hChildren; int main(int argc, char **argv){ // デーモン化 daemonize(); //kill しやすいように pid ファイルの作成 write_pid(); // SIGTERM ですべての子プロセスを殺すようにシグナルハンドラを設定 signal(SIGTERM, kill_all_children); // ハッシュの初期化 apr_initialize(); apr_pool_create(&pool, NULL); hChildren = apr_hash_make(pool); //親プロセスのループ while(1){ while( apr_hash_count(hChildren) >= MAX_CHILDREN ){ int status; pid_t child_pid = wait( &status ); //子プロセスが死ぬまで待つ apr_hash_set(hChildren, &child_pid, sizeof(child_pid), NULL); //死んだ子プロセスをハッシュテーブルから削除 } pid_t *pid = apr_palloc(pool, sizeof(pid_t)); *pid = fork(); //フォーク if(*pid==0){ signal(SIGTERM, signal_handler); goto CHILDREN; //子プロセスだったら、ループから抜ける } apr_hash_set(hChildren, pid, sizeof(pid_t), 1); //子プロセスをハッシュテーブルに追加 usleep(100); } CHILDREN: while(1){ //子プロセスの処理をここに書く sleep(1); } } void kill_all_children(int sig){ apr_hash_index_t *hi; apr_ssize_t klen; pid_t *child_pid_ptr; int val; for( hi=apr_hash_first(pool, hChildren); hi; hi=apr_hash_next(hi) ){ apr_hash_this(hi, &child_pid_ptr, &klen, &val); int ret = kill(*child_pid_ptr, SIGTERM); } exit(0); } void signal_handler(int sig){ exit(0); } void write_pid(){ FILE *fp; fp = fopen(PID_FILE, "w"); fprintf(fp, "%d", getpid()); fclose(fp); } void daemonize(){ pid_t pid, sid; /* カレントディレクトリを / に変更 */ if( chdir("/") < 0 ) exit(EXIT_FAILURE); /* 子プロセスの開始 */ pid = fork(); if( pid < 0 ) exit(EXIT_FAILURE); /* 親プロセスを終了 */ if( pid > 0 ) exit(EXIT_SUCCESS); /* 新しいセッションを作成 */ sid = setsid(); if( sid < 0 ) exit(EXIT_FAILURE); /* ファイル作成マスクをリセット */ umask(0); /* 標準入力,標準出力,標準エラー出力を閉じる */ close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); return; }
細かいところはいい加減なので、警告がやたらでるので本番で使うときは -Wall を付けて警告がなくなるまで要修正。
# gcc -g -I /usr/include/apr-1 -L/usr/lib/apr-1 -lapr-1 my_prefork_daemon.c -o my_prefork_daemon
- 実行
デーモン化されたため実行するとすぐにプロンプトが返ってくる。また、psしてみると、親プロセスの親プロセスIDが、シェルのプロセスIDではなく、「1」(initのプロセスID)になっているはず。
# ./my_prefork_daemon # ps -ef|grep my_prefork_daemon root 8529 1 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8530 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8531 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8532 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8533 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8534 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8535 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8536 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8537 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8538 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8539 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8540 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8541 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8542 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8543 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8544 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon root 8545 8529 0 23:40 ? 00:00:00 ./my_prefork_daemon 503 8547 4614 0 23:41 pts/3 00:00:00 grep my_prefork_daemon
- プロセスの終了方法
親プロセスに対してシグナルを送信すれば終了できる。デーモンらしくプロセスIDを /var/run/my_prefork_daemon.pid というファイル名で残すようにしたので、シグナルの送信方法は、以下のようにすればOK。今後、/etc/init.d/ の下に起動/終了スクリプトを書くときにも使える。
# kill `cat /var/run/my_prefork_daemon.pid`
参考
C言語でprefork型のデーモンを書く(1): 非デーモン prefork サンプル - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(2): 非デーモン prefork シグナルハンドラ付き - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(2): 非デーモン prefork シグナルハンドラ付き
1つの親プロセスとたくさんの子プロセスという構成。親プロセスに SIGTERM を送ると、すべての子プロセスをきれいに終了させた後で終了するようにシグナルハンドラを追加したサンプル。
- my_prefork_signal.c
シグナルハンドラ付きのソース
#include <stdio.h> #include <string.h> #include <apr_hash.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #include <signal.h> #define MAX_CHILDREN 16 //子プロセスの数 void kill_all_children(int); void signal_handler(int); // 子プロセスの管理にハッシュテーブルを使う static apr_pool_t* pool; static apr_hash_t* hChildren; int main(void){ // SIGTERM ですべての子プロセスを殺すようにシグナルハンドラを設定 signal(SIGTERM, kill_all_children); // ハッシュの初期化 apr_initialize(); apr_pool_create(&pool, NULL); hChildren = apr_hash_make(pool); //親プロセスのループ while(1){ while( apr_hash_count(hChildren) >= MAX_CHILDREN ){ int status; pid_t child_pid = wait( &status ); //子プロセスが死ぬまで待つ apr_hash_set(hChildren, &child_pid, sizeof(child_pid), NULL); //死んだ子プロセスをハッシュテーブルから削除 } pid_t *pid = apr_palloc(pool, sizeof(pid_t)); *pid = fork(); //フォーク if(*pid==0){ signal(SIGTERM, signal_handler); goto CHILDREN; //子プロセスだったら、ループから抜ける } apr_hash_set(hChildren, pid, sizeof(pid_t), 1); //子プロセスをハッシュテーブルに追加 usleep(100); } CHILDREN: while(1){ //子プロセスの処理をここに書く sleep(1); } } void kill_all_children(int sig){ apr_hash_index_t *hi; apr_ssize_t klen; pid_t *child_pid_ptr; int val; for( hi=apr_hash_first(pool, hChildren); hi; hi=apr_hash_next(hi) ){ apr_hash_this(hi, &child_pid_ptr, &klen, &val); int ret = kill(*child_pid_ptr, SIGTERM); } exit(0); } void signal_handler(int sig){ exit(0); }
細かいところはいい加減なので、警告がやたらでるので本番で使うときは -Wall を付けて警告がなくなるまで要修正。
# gcc -g -I /usr/include/apr-1 -L/usr/lib/apr-1 -lapr-1 my_prefork_signal.c -o my_prefork_signal
- 実行
$ ./my_prefork_signal & [1] 8502 $ ps -ef|grep my_prefork_signal 503 8502 4614 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8503 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8504 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8505 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8506 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8507 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8508 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8509 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8510 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8511 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8512 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8513 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8514 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8515 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8516 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8517 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8518 8502 0 23:32 pts/3 00:00:00 ./my_prefork_signal 503 8521 4614 0 23:33 pts/3 00:00:00 grep my_prefork_signal
- プロセスの終了方法
親プロセスに対してシグナルを送信すれば終了できる。
シグナルの送信方法は、
親プロセスのプロセスIDを調べる # ps -ef そのプロセスIDにSIGTERMを送る。 # kill 8502
参考
C言語でprefork型のデーモンを書く(1): 非デーモン prefork サンプル - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(1): 非デーモン prefork サンプル
Cで書かれた prefork デーモン(daemon)のちょうど良いサンプルが見つからなかったので自分で書く。
ちょうど良いお手本がないので PerlのソースをCに移植した。
プラットフォームはCentOS5.2。
- my_prefork.c
ただの prefork のサンプル(デーモン化はしていない)
#include <stdio.h> #include <string.h> #include <apr_hash.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #define MAX_CHILDREN 16 //子プロセスの数 int main(void){ // 子プロセスの管理にハッシュテーブルを使う apr_pool_t* pool; apr_hash_t* hChildren; apr_initialize(); apr_pool_create(&pool, NULL); hChildren = apr_hash_make(pool); //親プロセスのループ while(1){ while( apr_hash_count(hChildren) >= MAX_CHILDREN ){ int status; pid_t child_pid = wait( &status ); //子プロセスが死ぬまで待つ apr_hash_set(hChildren, &child_pid, sizeof(child_pid), NULL); //死んだ子プロセスをハッシュテーブルから削除 } pid_t *pid = apr_palloc(pool, sizeof(pid_t)); *pid = fork(); //フォーク if(*pid==0) goto CHILDREN; //子プロセスだったら、ループから抜ける apr_hash_set(hChildren, pid, sizeof(pid_t), 1); //子プロセスをハッシュテーブルに追加 usleep(100); } CHILDREN: while(1){ //子プロセスの処理をここに書く sleep(1); } }
# gcc -g -I /usr/include/apr-1 -L/usr/lib/apr-1 -lapr-1 my_prefork.c -o my_prefork
Cでハッシュテーブルを使うときのメモ
Cでハッシュテーブルを使いたくなったので、調べてみたら APR(Apache Portability Runtime) のハッシュテーブルのパフォーマンスが良いらしい。名前的に移植性も良さそうな気がする。
簡単な使い方を兼ねたサンプルをメモがわりに残す。プラットフォームはCentOS5.2。
#include <stdio.h> #include <string.h> #include <apr_hash.h> #define MAX_KEY_LENGTH 512 #define MAX_VAL_LENGTH 512 static apr_pool_t* pool; static apr_hash_t* hash; main(){ //おまじない apr_initialize(); apr_pool_create(&pool, NULL); hash = apr_hash_make(pool); //ハッシュに値をセット char *key, *val; int i; for(i=0; i<20; i++){ key = apr_palloc(pool, MAX_KEY_LENGTH); val = apr_palloc(pool, MAX_VAL_LENGTH); sprintf(key, "KEY_%d", i); sprintf(val, "VAL_%d", i); apr_hash_set(hash, key, APR_HASH_KEY_STRING, val); } //キーを指定して値を取り出す val = apr_hash_get(hash, key, APR_HASH_KEY_STRING); printf("key=%s, val=%s\n\n", key,val); //ハッシュに入っているキーと値をすべて取り出す apr_hash_index_t *hi; apr_ssize_t klen; for( hi=apr_hash_first(pool, hash); hi; hi=apr_hash_next(hi) ){ apr_hash_this(hi, (const void **)&key, &klen,(void **)&val); printf("key=%s, val=%s, key_length=%d\n", key,val,(int)klen); } }
# gcc -g -I /usr/include/apr-1 -L/usr/lib/apr-1 -lapr-1 my_hash.c -o my_hash
- 実行
# ./my_hash key=KEY_19, val=VAL_19 key=KEY_8, val=VAL_8, key_length=5 key=KEY_9, val=VAL_9, key_length=5 key=KEY_10, val=VAL_10, key_length=6 key=KEY_11, val=VAL_11, key_length=6 key=KEY_12, val=VAL_12, key_length=6 key=KEY_13, val=VAL_13, key_length=6 key=KEY_14, val=VAL_14, key_length=6 key=KEY_15, val=VAL_15, key_length=6 key=KEY_16, val=VAL_16, key_length=6 key=KEY_17, val=VAL_17, key_length=6 key=KEY_18, val=VAL_18, key_length=6 key=KEY_19, val=VAL_19, key_length=6 key=KEY_0, val=VAL_0, key_length=5 key=KEY_1, val=VAL_1, key_length=5 key=KEY_2, val=VAL_2, key_length=5 key=KEY_3, val=VAL_3, key_length=5 key=KEY_4, val=VAL_4, key_length=5 key=KEY_5, val=VAL_5, key_length=5 key=KEY_6, val=VAL_6, key_length=5 key=KEY_7, val=VAL_7, key_length=5
意外と簡単。
- glibc バージョン
追加でglibc版も試した。どうやらiteration の機能が無いみたいなので注意が必要。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define __USE_GNU #include <search.h> #define MAX_KEY_LENGTH 512 #define MAX_VAL_LENGTH 512 main(void) { int ret; struct hsearch_data tab; /* おまじない */ memset(&tab, 0, sizeof(tab)); ret = hcreate_r(512, &tab); ENTRY e; ENTRY* search_result; /* ハッシュに値をセット */ int i; for(i=0; i < 20; i++) { e.key = (char*)malloc(MAX_KEY_LENGTH); e.data = (char*)malloc(MAX_VAL_LENGTH); sprintf(e.key, "KEY_%d", i); sprintf(e.data, "VAL_%d", i); ret = hsearch_r(e, ENTER, &search_result, &tab); } /* キーを指定して値を取り出す */ ret = hsearch_r(e, FIND, &search_result, &tab); printf("key=%s, val=%s\n", search_result->key, search_result->data); /* 全データを取り出す */ /* 全データをfree */ }
参考
C/C++ で使える Hashtable - BOOLEANLABEL
hcreate_rなどを使ってみた - Limitの日記
Windows Server {2000,2003,2008} でDSRを行う方法
「Keepalived + LVS + CentOS4 でロードバランサー(DSR) - Sleepless geek in Seattle」あたりで、Keepalived を使ったCentOS のロードバランスは簡単にできるようになったが、Windows Server の設定はどうやるんだろうと調べてみたら、こんな素敵なエントリ を見つけた。
Windows Server 2008 上にこの通りの設定をやってみたがうまく行かない。どうやら、上記のエントリでは、Windows Server 2003 でのみ動作するようだ。 Windows Server 2000, 2003, 2008 でやり方がそれぞれ違うことが分かったのでここにログを残そう。
- loopback インタフェースの追加(2000, 2003, 2008 共通)
概要を書くと、
新しいデバイスの追加 -> ネットワーク アダプタ -> 製造元からMicrosoftを選択 -> ネットワーク アダプタの中から Microsoft Loopback Adapter を選択
そのloopback インタフェースに、Virtual IP を振る。
詳細は、「Windows serverでDSRを行う方法: sanonosa システム管理コラム集」 を参照。
- Windows Server 2000 の場合
Metric を254などの大きい値にする。方法は、「@IT:Windows TIPS -- Tips:高速なネットワーク・インターフェイスを自動的に選択可能にする」を参照。これだけ。
ファイアウォールを無効にするか、そのポートのトラフィックを遮断しないように設定する。これだけ。
ファイアウォールを無効にするか、そのポートのトラフィックを遮断しないように設定するのに加えて、以下のコマンドを実行する。
"Local Area Connection" と "loopback" の部分は、各自読み替えること。(Windows Server日本語版だと、 「ローカルエリア接続」 みたいな名前になる。)
netsh interface ipv4 set interface "Local Area Connection" weakhostreceive=enabled netsh interface ipv4 set interface "loopback" weakhostreceive=enabled netsh interface ipv4 set interface "loopback" weakhostsend=enabled
参考にしたサイト
「Windows serverでDSRを行う方法: sanonosa システム管理コラム集」
「Loadbalancer.org Blog » Blog Archive » Direct Routing aka. Direct Server Return on Windows 2008 using loopback adpter」
Postfixのコンテンツフィルターを複数設定する方法
Postfixには、ウィルススキャン や アンチSPAM などをフィルターコンテンツフィルターという方法でプラグインできる。
その際に、amavisd-newや自分で作ったカスタムフィルターなどを組み合わせたいときには、複数のコンテンツフィルターを併せて設定すればよい。
カスタムのコンテンツフィルターを追加したいときには、Perlで書かれたトランスペアレントなコンテンツフィルター smtpprox を使うと便利。
今回はsmtpprox を2つ設定した場合の設定手順のログ。
- 最終的には以下のような感じ。
+-----------+ +----------------+ +-------------+ |Postfix:25 |=>|my_filter1:10024|=>|Postfix:10025|=> +-----------+ +----------------+ +-------------+ +----------------+ +-------------+ |my_filter2:20024|=>|Postfix:20025|====> +----------------+ +-------------+
- smtpprox のインストール & 起動
# wget http://bent.latency.net/smtpprox/smtpprox-1.2.tar.gz # tar zxvf smtpprox-1.2.tar.gz # cd smtpprox-1.2 ### my_filter1 ### # ./smtpprox localhost:10024 localhost:10025 ### my_filter2 ### # ./smtpprox localhost:20024 localhost:20025
- Postfix の設定
master.cf を以下のように修正
smtp inet n - n - - smtpd
を
smtp inet n - n - - smtpd -o content_filter=my_filter1:[127.0.0.1]:10024
のようにする。つまりコンテンツフィルタ my_filter1 を使う、という設定を追加する。
my_filter1 を設定(定義)する。
my_filter1 unix - - n - 30 smtp -o smtp_data_done_timeout=1200 -o smtp_send_xforward_command=yes -o disable_dns_lookups=yes
次に、my_filter1 から ポート10025に戻ってくる smtpd の設定を追加する。
その時に、2番目のコンテンツフィルタ my_filter2 を使う設定を行う。(2行目)
127.0.0.1:10025 inet n - n - - smtpd -o content_filter=my_filter2:[127.0.0.1]:20024 -o local_recipient_maps= -o relay_recipient_maps= -o smtpd_restriction_classes= -o smtpd_client_restrictions= -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o mynetworks=127.0.0.0/8 -o strict_rfc821_envelopes=yes -o smtpd_error_sleep_time=0 -o smtpd_soft_error_limit=1001 -o smtpd_hard_error_limit=1000 -o smtpd_client_connection_count_limit=0 -o smtpd_client_connection_rate_limit=0 -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks
先ほどと同様に、my_filter2 の定義を追加する。
my_filter2 unix - - n - 30 smtp -o smtp_data_done_timeout=1200 -o smtp_send_xforward_command=yes -o disable_dns_lookups=yes
次に、my_filter2 から ポート20025に戻ってくる smtpd の設定を追加する。
その時に、コンテンツフィルタを空にしておく。(コンテンツフィルタは使わないので。)間違って指定するとループするかも。
127.0.0.1:20025 inet n - n - - smtpd -o content_filter= -o local_recipient_maps= -o relay_recipient_maps= -o smtpd_restriction_classes= -o smtpd_client_restrictions= -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o mynetworks=127.0.0.0/8 -o strict_rfc821_envelopes=yes -o smtpd_error_sleep_time=0 -o smtpd_soft_error_limit=1001 -o smtpd_hard_error_limit=1000 -o smtpd_client_connection_count_limit=0 -o smtpd_client_connection_rate_limit=0 -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks
最後は、以下のようになる。
smtp inet n - n - - smtpd -o content_filter=my_filter1:[127.0.0.1]:10024 my_filter1 unix - - n - 30 smtp -o smtp_data_done_timeout=1200 -o smtp_send_xforward_command=yes -o disable_dns_lookups=yes 127.0.0.1:10025 inet n - n - - smtpd -o content_filter=my_filter2:[127.0.0.1]:20024 -o local_recipient_maps= -o relay_recipient_maps= -o smtpd_restriction_classes= -o smtpd_client_restrictions= -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o mynetworks=127.0.0.0/8 -o strict_rfc821_envelopes=yes -o smtpd_error_sleep_time=0 -o smtpd_soft_error_limit=1001 -o smtpd_hard_error_limit=1000 -o smtpd_client_connection_count_limit=0 -o smtpd_client_connection_rate_limit=0 -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks my_filter2 unix - - n - 30 smtp -o smtp_data_done_timeout=1200 -o smtp_send_xforward_command=yes -o disable_dns_lookups=yes 127.0.0.1:20025 inet n - n - - smtpd -o content_filter= -o local_recipient_maps= -o relay_recipient_maps= -o smtpd_restriction_classes= -o smtpd_client_restrictions= -o smtpd_client_restrictions= -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o mynetworks=127.0.0.0/8 -o strict_rfc821_envelopes=yes -o smtpd_error_sleep_time=0 -o smtpd_soft_error_limit=1001 -o smtpd_hard_error_limit=1000 -o smtpd_client_connection_count_limit=0 -o smtpd_client_connection_rate_limit=0 -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks 以下略
これで、コンテンツフィルタの中で、メールの中身を煮るなり焼くなりできる。