[mail] CentOS6でmailxをmailutilsに切り替える

コマンドラインでさくっとメールを送る際や、バッチでもたまに使っている「mail」コマンドですが、CentOS5系から6系に乗り換えたところ問題が・・・。

インストーラーを使用して入れたVM環境ではmailコマンドが標準で入っていたのですが、これまで使ってたテストコマンドがなんだかうまくいきません。

echo 'テストです!' | mail -s "テストメール"  "hoge@hoge.com" -- -f "huga@huga.com"

 

コマンドの実態を確認すると、

ls -la /bin/mail
lrwxrwxrwx 1 root root 19  5月 1 21:57 2012 /bin/mail -> mailx

そもそも「mailx」というコマンドに変わってしまった模様。

 

さらに、以下の状況になっていることがわかりました。

  • 件名がutf-8で送られて一部メーラーでは文字化け。お馴染みのnkf -jで渡してもだめ
  • 本文が数行に渡るとバイナリとして送信され、メーラー側ではファイル添付扱いされる

 

マニュアル含めいろいろ調べてみたものの、オプションで文字コードやContent-typeの指定ができるやり方は見つからず。

どうやら慣れ親しんだ以前のmailコマンドの挙動とはだいぶ違うことがわかったので、以前のコマンドに近いものを探してみたところ、「mailunits」を発見。

mailxコマンドをアンインストールした上で、インストールしてみます。

cd /usr/local/src/
wget http://ftp.gnu.org/gnu/mailutils/mailutils-latest.tar.gz
tar xvzf mailutils-latest.tar.gz
cd mailutils-*
./configure
make
make install

/usr/local/bin/mailとしてインストールされました。

 

一部オプションは違うようで、

echo "テストです!" | mail -s "テストメール"  "hoge@hoge.com" -- -f "huga@huga.com"
↓↓
echo "テストです!" | mail -s "テストメール" -a "From:huga@huga.com" "hoge@hoge.com"

とするとうまく動きました。

本文が長文でもちゃんと送ってくれてます。

cat honbun.txt | nkf -j | mail -s "テストメール" -a "From:huga@huga.com" "hoge@hoge.com"

 

mailコマンドはディストリビューションによって挙動が違う認識はありましたが、OSバージョンアップでコマンド自体が変わってしまっているのは予想外でした。

とりあえずはこの「mailutils」で様子を見ようと思います。

※初稿時、「mailunits」となっていた箇所を「mailutils」に修正しました(DQNEOさんありがとうございます。2012/11/7)。


[Apache] mod_rpafをApache2.4系で使う

Apache2.4系では2.2系からオブジェクト構造などに一部変更があったらしく、メンテされてないモジュールなどで、コンパイルが通らないことがあります。

今回は、バランサを前においたApacheで、X-Forwarded-Forをアクセス元ホストとして使うためのモジュール「mod_rpaf」のコンパイルをする際に問題がでました。

 

調べてみたところ、対象モジュールは違いますが、以下で紹介されている「remote_ip」「remote_addr」の名称変更がビンゴっぽい。

apache2.4.1リリース

 

mod_rpaf-2.0.cファイルの対象箇所を修正します(gistはこちら)。

--- mod_rpaf-2.0.c.org  2012-05-17 12:05:34.082130109 +0900
+++ mod_rpaf-2.0.c      2012-05-17 12:16:41.648138252 +0900
@@ -147,8 +147,8 @@

 static apr_status_t rpaf_cleanup(void *data) {
     rpaf_cleanup_rec *rcr = (rpaf_cleanup_rec *)data;
-    rcr->r->connection->remote_ip   = apr_pstrdup(rcr->r->connection->pool, rcr->old_ip);
-    rcr->r->connection->remote_addr->sa.sin.sin_addr.s_addr = apr_inet_addr(rcr->r->connection->remote_ip);
+    rcr->r->connection->client_ip   = apr_pstrdup(rcr->r->connection->pool, rcr->old_ip);
+    rcr->r->connection->client_addr->sa.sin.sin_addr.s_addr = apr_inet_addr(rcr->r->connection->client_ip);
     return APR_SUCCESS;
 }

@@ -161,7 +161,7 @@
     if (!cfg->enable)
         return DECLINED;

-    if (is_in_array(r->connection->remote_ip, cfg->proxy_ips) == 1) {
+    if (is_in_array(r->connection->client_ip, cfg->proxy_ips) == 1) {
         /* check if cfg->headername is set and if it is use
            that instead of X-Forwarded-For by default */
         if (cfg->headername && (fwdvalue = apr_table_get(r->headers_in, cfg->headername))) {
@@ -180,11 +180,11 @@
                 if (*fwdvalue != '\0')
                     ++fwdvalue;
             }
-            rcr->old_ip = apr_pstrdup(r->connection->pool, r->connection->remote_ip);
+            rcr->old_ip = apr_pstrdup(r->connection->pool, r->connection->client_ip);
             rcr->r = r;
             apr_pool_cleanup_register(r->pool, (void *)rcr, rpaf_cleanup, apr_pool_cleanup_null);
-            r->connection->remote_ip = apr_pstrdup(r->connection->pool, ((char **)arr->elts)[((arr->nelts)-1)]);
-            r->connection->remote_addr->sa.sin.sin_addr.s_addr = apr_inet_addr(r->connection->remote_ip);
+            r->connection->client_ip = apr_pstrdup(r->connection->pool, ((char **)arr->elts)[((arr->nelts)-1)]);
+            r->connection->client_addr->sa.sin.sin_addr.s_addr = apr_inet_addr(r->connection->client_ip);
             if (cfg->sethostname) {
                 const char *hostvalue;
                 if (hostvalue = apr_table_get(r->headers_in, "X-Forwarded-Host")) {

 

パッチ当て&インストール手順をまとめるとこんな感じです。
(自前環境ではapxsにパスを通してなかったので、Makefileも一部書き換えてます)

wget http://stderr.net/apache/rpaf/download/mod_rpaf-0.6.tar.gz
tar xvzf mod_rpaf-0.6.tar.gz
cd mod_rpaf-0.6
vi Makefile
> APXS2=$(shell which apxs2)
>  ↓ ↓
> #APXS2=$(shell which apxs2)
> APXS2=/usr/local/apache/bin/apxs

git clone git://gist.github.com/2716030.git
patch < 2716030/mod_rpaf-2.0.c.patch

make rpaf-2.0
make install-2.0

 

mod_rpafはバランサのIPアドレスを決め打ちでしか指定できず、ELBのようにIPが固定されないケースなどではかなりつらい仕様なので、以下のような事例を参考にコンパイル前に少し動きをいじってあげると幸せです。

mod_rpaf の RPAFproxy_ips に 192.0.2. とか書きたい

 

毎回同じローカルIPがログに出力されてもうれしくないので、バランサを使う際は、これ系のモジュールを入れるかログ出力内容の書き換えは必須っぽいですね。


[Apache] Apache2.4系でSSIを使う

拡張子.htmlのファイルでSSI(Server Side Include)を使いたい、というニーズはまだまだ根強いと思ってます(手軽なので)。

今回、Apache2.2系→2.4系にした際に、既存のSSIファイルが動かなくなっていたので対処をメモ。

基本セットとして、以下をhttpd.confまたは設定の上書きが許可されている状態で.htaccessに記述します。
 

AddType application/x-httpd-php .html
AddType text/html .html
AddHandler server-parsed .html
AddOutputFilter INCLUDES .html
Options All

 

その上でApache再起動してもうまくいかない!という場合は、Includeモジュールが読みこまれてない可能性があります。
httpd.confで以下の記述を確認。
 

LoadModule include_module modules/mod_include.so

 

読み込まれてなければ、コメントアウトを外してからApache再起動。
これで解決するはずです。

2.2系のときを覚えていないのですが、少なくとも2.4系からはinclude_moduleのデフォルト設定はOFFになってるようです。

バージョンアップ時はいろいろでてきますね。。


[PHP] PHP5.4でpukiwikiを動かす

日本製wikiとしてはおなじみのpukiwiki

2006年以降公式なバージョンアップもなく、開発フリーズな状態っぽいですが、海外含めたwikiの中でも非常に使いやすいので、いまだに重宝させてもらってます。

今回は、PHP5.4を導入した環境で画面真っ白になってしまったので、そのときの対応をまとめてみます。

とりあえず動くようにする

とりあえず真っ白画面から抜けだそうということで、Fatalエラーをつぶしていきます。
index.phpの10行目あたりにある以下のコメントアウトを外します。

error_reporting(E_ALL); // Debug purpose

その状態ででてきたFatalエラーについて対応します。
Fatalが出た時点で処理は止まってしまうので、Fatalを解決→ページを確認→Fatalを解決→・・・の流れになると思います。
おそらく、Warning,Notice,Deprecatedあたりもいっぱい出ると思いますが、ここでは一旦無視します。

 

自前環境では以下の2つを対応しました。

hex2bin関数をコメントアウト

lib/func.php にある「hex2bin」関数が、PHP5.4からネイティブ関数化されているためコメントアウト。

// Inversion of bin2hex()
/*
function hex2bin($hex_string)
{
        // preg_match : Avoid warning : pack(): Type H: illegal hex digit ...
        // (string)   : Always treat as string (not int etc). See BugTrack2/31
        return preg_match('/^[0-9a-f]+$/i', $hex_string) ?
                pack('H*', (string)$hex_string) : $hex_string;
}
*/

ls2プラグインの関数呼び出し時の参照渡しを修正

plugin/ls2.inc.php で、関数呼び出し時の参照渡しがされているため、修正。

array_walk($args, 'plugin_ls2_check_arg', & $params);
↓
array_walk($args, 'plugin_ls2_check_arg', $params);

 

これでさきほどコメントアウトを外した index.php のエラー出力をもとに戻すと、とりあえずは使える状態になりました。
現状は、Warning含めエラーを表示していないだけの状態で、同じようにPHP5.4対応されている以下のサイトにもあるように「date関数の設定値」「参照渡し」まわりや「クラスの継承」まわりなどで、もろもろ問題は残ってしまっている状況だと思います。
(dateの設定とかはやっておいたほうがベターかもしれません)

martianの日記: PHP5.4 と PukiWiki

 

今回は、ユーザ向けに提供しているシビアな環境ではなかったので、エラー発生ベースで直していく方針にしています。
クリティカルな問題がでてきてしまったら、追記していきたいと思います。

viva pukiwiki


[DNS] digコマンドでドメインからネームサーバを確認する

ドメインやサーバの引越しなどで、DNSレコードの保持先とネームサーバを切り替える際、伝搬のタイミングが読みにくいので、変更後の設定があっているのか、やきもきすることがあります。

ネームサーバが切り替わった状態で、ドメインが正しく名前解決できていることを確認できればよいので、「今どっちのネームサーバを見ているか」が把握できればよさそうです。

 

そんなときに使えるのが「dig」コマンドです。

 

「hogehoge.com」というドメインを切り替える想定で、整理してみます。

切り替え前

切り替え前は「before1.dns.com」「before2.dns.com」というネームサーバを利用していました。
このときコマンドを叩くと、「AUTHORITY SECTION」のところで現在のネームサーバの向き先が確認できます。

$ dig hogehoge.com

; < <>> DiG 9.3.6-P1-RedHat-9.3.6-16.P1.el5 < <>> hogehoge.com
;; global options: printcmd
;; Got answer:
;; ->>HEADER< <- opcode: QUERY, status: NOERROR, id: 64469
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 0

;; QUESTION SECTION:
;hogehoge.com. IN A

;; ANSWER SECTION:
hogehoge.com. 5 IN A XXX.XXX.XXX.XXX

;; AUTHORITY SECTION:
hogehoge.com. 5 IN NS before1.dns.com.
hogehoge.com. 5 IN NS before2.dns.com.

;; ADDITIONAL SECTION:
before1.dns.com.          5       IN      A       XXX.XXX.XXX.XXX
before2.dns.com.          5       IN      A       XXX.XXX.XXX.XXX

;; Query time: 9 msec
;; SERVER: XXX.XXX.XXX.XXX#53(XXX.XXX.XXX.XXX)
;; WHEN: Wed Apr 18 12:11:04 2012
;; MSG SIZE rcvd: 88

切り替え実施

ここで、DNSレコードとネームサーバの向き先を変更します。
バリュードメインやムームードメインなど、基本的にはウェブ画面上で設定変更ができると思います。

切り替え後

切り替え後は「after1.dns.com」「after2.dns.com」というネームサーバを利用することになります。
一定時間(経験則で数時間以内)経ってから、コマンドを叩くと、切り替え前と同じく「AUTHORITY SECTION」のところでネームサーバの向き先が変わっていることが確認できると思います。

$ dig hogehoge.com

; < <>> DiG 9.3.6-P1-RedHat-9.3.6-16.P1.el5 < <>> hogehoge.com
;; global options: printcmd
;; Got answer:
;; ->>HEADER< <- opcode: QUERY, status: NOERROR, id: 64469
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 0

;; QUESTION SECTION:
;hogehoge.com. IN A

;; ANSWER SECTION:
hogehoge.com. 5 IN A XXX.XXX.XXX.XXX

;; AUTHORITY SECTION:
hogehoge.com. 5 IN NS after1.dns.com.
hogehoge.com. 5 IN NS after2.dns.com.

;; ADDITIONAL SECTION:
after1.dns.com.          5       IN      A       XXX.XXX.XXX.XXX
after2.dns.com.          5       IN      A       XXX.XXX.XXX.XXX

;; Query time: 9 msec
;; SERVER: XXX.XXX.XXX.XXX#53(XXX.XXX.XXX.XXX)
;; WHEN: Wed Apr 18 14:11:01 2012
;; MSG SIZE rcvd: 88

 

この状態で、「nslookup」コマンドなどを利用して、変更前と同じように名前解決ができていれば、切り替えはめでたく完了ということになるかと思います。

知ってる方にとっては、「当たり前!」とも言われそうですが、以前に大きなDNS切り替えをおこなったときにやきもきしたので、確認手順を整理してみました。

 

2012/04/25追記
こちら

dig hogehoge.com

では、name server情報が拾えない場合もあるようです。

dig hogehoge.com ns

として、明示的にname server情報を見るのが一般的なやり方のようでした。
もし上記の手順でうまくいかなかった場合は、「ns」をつけて試してみてください。


[PHP] PHP5.4 + Apache 2.4でSegmentation fault

PHP5.4.0 + Apache2.4.1で、PHPプログラムを動かした際、数回アクセスすると500エラーが発生しているケースがちらほら。

 

Apacheのエラーログを確認すると、以下のようなメッセージが出ていました。

[Fri Apr 13 22:50:04.385611 2012] [core:notice] [pid 2499:tid 140578174879488] AH00051: child pid 3659 exit signal Segmentation fault (11)

 

同じページにアクセスした場合でも必ず発生するというわけではなく、いまいち規則性も見えない状況です。

 

利用してるApacheモジュール群が怪しいんじゃないかと思い、不要そうなmoduleのLoadModuleをコメントアウトして再起動してみるも、状況はあまり変わらず。

そもそもApache2.4系になってデフォルトで最小限のmodule読み込みになっているので、不要なモジュールが少ない、というものありました。

 

となると、通常のエラーログからはわからない「Segmentation fault」の根本原因を探らないとなのですが、いろいろ調べてみると、coreファイルを吐き出してデバッグ、がセオリーのようです。

http://d.hatena.ne.jp/sarface2012/20101027

手順を参考にさせてもらって、とにかくやってみます。

coreファイルの出力制限を解除

現在のcoreファイルの出力制限を確認します。

$ ulimit -a | grep "core file size"

値が0の場合は、制限を解除します。

$ vi /etc/profile
# ulimit -S -c 0 > /dev/null 2>&1
ulimit -c unlimited > /dev/null 2>&1

変更後は再ログインします。

確認して出力制限が外れていればOKです。

$ ulimit -a
core file size          (blocks, -c) unlimited。

Apacheにcoreファイル出力の設定を追加

httpd.confに設定を追加します。

$ vi httpd.conf
CoreDumpDirectory /tmp

追加後、再起動します。

apachectl restart

coreファイルのデバッグ

ここで、再度「Segmentation fault」を発生させます。
すると、/tmp下にcoreファイルが吐かれました。

$ ls -lr /tmp/core.*
core.3086

coreファイルをデバッグするためにはgdbコマンドを使います(CentOSの利用環境に入っていなかったので、yumインストールしました)

gdb <command> -c <core file>

で利用できるということで

$ gdb httpd -c /tmp/core.3086
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
...
Reading symbols from /XXXXX/httpd...done.

warning: exec file is newer than core file.

  :
  略
  :

(gdb)

ここで、「where」をタイプするとエラー発生箇所がでてきます。

(gdb) where
#0  0x00007fdae71d14ed in read () from /XXX/libpthread.so.0
#1  0x00000000004657f7 in ap_worker_pod_check (pod=) at pod.c:57
#2  0x0000000000463154 in child_main (child_num_arg=1) at worker.c:1332
#3  0x0000000000463fb6 in make_child (s=0x1dab4c0, slot=1) at worker.c:1417
#4  0x0000000000464b57 in perform_idle_server_maintenance (_pconf=, plog=, s=) at worker.c:1618
#5  server_main_loop (_pconf=, plog=, s=) at worker.c:1739
#6  worker_run (_pconf=, plog=, s=) at worker.c:1810
#7  0x000000000042de6e in ap_run_mpm (pconf=0x1d82138, plog=0x1de5528, s=0x1dab4c0) at mpm_common.c:98
#8  0x0000000000428874 in main (argc=3, argv=0x7fff9696e8c8) at main.c:777

正直わからない部分もあるのですが、

#1  0x00000000004657f7 in ap_worker_pod_check (pod=) at pod.c:57

あたりを見ると、workerでの実行に関係がありそうです。

 

そこで、現在のApache環境を確認してみると

$ apachectl -V | grep "Server MPM"
Server MPM:     worker

workerで動いてました。

もともとPHPはpreforkが基本の認識でいたのですが、今回Apacheのインストール時にMPMをworkerにしてしまっていたようです。

Apacheを自前インストールしていて、コンパイルをやり直す必要があるため、configure時に

--with-mpm=prefork

をつけて、Apacheを再インストール。
設定をもろもろして、起動。

結果、、、

 

「Segmentation fault」が発生しなくなりました!

 

調べてみたところ、PHPをworkerで動かす際はFastCGIなどを組み合わせるのが通例のようです。

マニュアルにも非推奨とありました。

http://www.php.net/manual/ja/faq.installation.php#faq.installation.apache2

ただ、Apache、PHPのバージョンが古い自前環境ではworkerでも問題なく動く環境もあったので、バージョン依存のある問題かもしれません。

workerのほうが、待機時のメモリ利用を抑えられるなど利点も大きいと思ってますが、基本PHPはpreforkという認識はあっていたみたいです。

 

今回は、

  • workerが悪さをしていたと思われる(詳細な原因の判明まではできず)
  • preforkにすることで解決できた

という結論になりました。

coreファイルのデバッグを実践できたのは収穫でしたが、コンパイル時に注意していればPHP+preforkで問題なく動いてくれて、調査もいらなかったんじゃないか・・・とも思ってしまいます。