2013年7月アーカイブ

HTTPD公開コンテンツをgitで管理してみる

そろそろ適当管理な自宅サーバもできるだけコンテンツをgit管理にしておこう、と思いゴニョゴニョと手を動かし始めてしまった日曜夜の作業メモ。

まずgitを入れる。CentOSの場合公式レポジトリにgitがないので、rpmforgeで追加レポジトリからgitを入れる必要がある。私の場合すでに過去に入れた rpmforge-release-0.3.6-1.el5.rf があったんで詳しいことは忘れたけど、確かこのへんに書いてあるようなことをして rpm 経由で rpmforgeの追加レポジトリを入れたと思う。入れた後は、/etc/yum.repos.d/rpmforge.repo の「enabled = 0」を「enabled = 1」にして準備完了。あとはいつものように「yum install git」とかすれば git が入ってくれます。個人的に公式レポジトリ以外はあんま使いたくないので、用が終わったら「enabled = 0」としている。ただ、あまりyumのレポジトリ管理を汚したくない人は、gitをソースコードから入れた方がいいかもしれない。

次にgitを使ってみる。gitは、CVSのようなディレクトリごとに管理するのとは違いレポジトリ化したディレクトリ以下まるごと管理できるので、レポジトリにしたいディレクトリにいって「$ git init; git add . ; git commit」すればほぼ完了。またsubversionとかのようなサーバを必ずしも必要としないので、とりあえずはレポジトリをローカルにおいとけばいいと思う。「git init」したディレクトリに「.git/」というディレクトリができてこれがレポジトリになるので、この「.git」さえバックアップしとけばOKになる。

いきなりgit commitしようとしたら実は怒られる。そのときはこういうページ参考に自分の設定を作ろう。慣れてる人は、~/.gitconfig を直接編集した方が手っ取り早いかもしれない。

$ git config --global user.name rarul
$ git config --global user.email rarul@rarul.com
$ git config --global color.ui auto

gitに全部登録したいからといって、あんまり登録するのにはふさわしくないファイルもあるかもしれない。そのときは .gitignore というファイルを作ってそこに登録したくないファイルを書きつつ、このファイルをgit commitしてしまえばいい。よくある.gitignoreの例

*~
\#*

git commitしてから.gitignoreを用意すると、.gitignoreに書かれてるファイルのgit操作ができなくてgit deleteもできなくなることが時々ある。どうするのが正しいのかよく知らないけど、個人的には.gitignoreファイルを一時的にmvしたりして回避してる。

最後に、Apacheなどで公開してるコンテンツの下に .git を残したままだと、リモートから HTTP ごしに gitレポジトリ丸ごととれちゃいます。これはまずいので、Apacheで「.git」は見えないようにする必要がある。httpd.confにはたいてい「^\.ht」を無視する設定があるので、それを参考に追加で書く。

<Files ~ "^\.git">
Deny from all
</Files>
<Directorymatch "^/.*/\.git+/">
Deny from all
</Directorymatch>

.gitディレクトリだけでなく.gitignoreファイルも場合によっちゃ見えると困るので設定。ちなみに上記だと、もしアクセスがあったときに HTTP STATUS 403 が返るので「何か設定している」ということはバレてしまう。それすらイヤな場合は、今のところはmod_rewriteとか使ってうまくごまかすしかなさげっぽい。

Effective C++ は必ず読んどこう

Amazonアフィリンク

なんとなく身近な方々が何人かここから買ってくれるような気がしたので「貼るなら今でしょ!」な感じで紹介しときます。

職場で「あれ?Effective C++読んでないの?え?Effective C++知らないの?」という感じの事例があってかなりカルチャーショックを受けた。これを読まずにC++のソースコードの森の中をさまようのは相当危険というかムチャですよと。

C++は変態的な仕様が結構あって、そのへんをヘタに使おうとするといろいろ罠にはまる。そのへんのありがちなとこを52項(+3項)の簡潔なルールで示しつつ、なぜそのルールを守らないといけないのかを解説する形の本になっている。この手の本にしてはそこまでページ数多いわけじゃないけど、内容はかなり凝縮されているので、熟読すべし。少なくともこの本を完全マスターしないとC++がわかったとはとてもいえない。

ちなみに、More Effective C++という続編っぽい本もあるけど、こっちは「必読」というほど内容が凝縮されているわけでなく比較的当たり前な話も多いように感じるので、個人的にはあまりおすすめしない。

自宅ネット用のDNSリゾルバを立てる

DNSといえばBINDしか知らないクチだったけど、最近は結構他にもあるもんだなぁとかとか。なぜDNSを立てようと思ったかとかも含めてグダグダ書こう。

自宅ネット内には自宅サーバ(外部公開の http://www.rarul.com/ とか)が立ってる。多分に漏れず、ISPから固定でないIPv4のグローバルIPアドレスをもらってて、それをNAPTでぶら下げる形でプライベートIPアドレス運用している。Webサーバとして公開するので、ポートマッピングとしてTCPポート80をこの自宅サーバのプライベートIPアドレスへと転送している。自宅サーバ的にはほぼこれでOKと。IPv6化計画がないわけでもないけど、まだ様子見、、、ASAHIネットよ早くJPNEのIPv6ローミングに対応してくれよと。

rarul.comドメインのネームサーバはValue-Domainで用意してくれている固定IPアドレスのを使っているのでネームサーバの心配はない。Value-Domainのネームサーバは動的DNSの運用も許してくれていて、自宅サーバから定期的にValue-Doaminの動的DNS設定をたたくことで、ISPからもらったグローバルIPアドレスが変わってしまった場合(PPPoEが切れたとか)にも勝手にネームサーバの配信する情報を変更してる。

これらにより、外部ネットワークからアクセスする場合には特に問題がなくなる。が、問題なのは内部からアクセスする場合。

何もせずに内部から「http://www.rarul.com/」にアクセスしようとすると、www.rarul.comをマジメに名前解決してしまい、グローバルIPアドレスが返ってきてしまう。このグローバルIPアドレスでアクセスした場合は使ってるルータ(というかNAPTというか)次第で、今使ってるフレッツ光プレミアムのCTUは、HTTP Response 500とともに「Internal Server Error」と書かれたページが返ってくる。たぶん外部ネットワークからCTU設定画面に入ろうとしたヤカラのための対策なんだろうなぁ。YAMAHAのRTX1100なんかの場合は、このグローバルIPアドレスでのアクセスを再ルーティングしてポートマッピング設定した先まで返してくれるという親切なのか怪しい仕様というべきなのかという挙動をすることで結構有名。よくあるルータなんかだと接続タイムアウトで何も返ってこなかったり、ルータの設定画面に入れてしまったり、結構怪しい部分だったりする。

マシンの台数が少なかったりすると、各マシンのhostsにプライベートIPアドレスが返ってくるようゴニョゴニョ書いておくという方法がとれる。Windowsだと「C:\Windows\System32\drivers\etc\hosts」をゴニョればいい。Vista以降は管理者権限がないとこのファイルを編集できないので不便だけど、そんな人のためのTips。でも、台数が多かったり、自宅サーバがよく移転したり、ノートPCのように内部ネットワークからだったり外部ネットワークからだったりが時と場合によったりする場合、このやり方も限界がある。最近新しくスマホ買った影響で、まさにこのノートPCのような例に引っかかり、限界を感じたところ。結局のところ、内部から「http://www.rarul.com/」に正しくアクセスできるようにするには、内部ネット用のDNSを立てるのが確実な解決方法だったりする。

そんなわけで、BIND以外のDNSとして候補を調べると、PowerDNS、NSD、Unbound、あたりが残る。今は(特に外部に公開するような)ネームサーバとして動作させたいわけではないので、NSDは対象外。この中で、CentOSのyumで管理できてかつ簡易なものということで、今回はdnsmasqをチョイス。

# yum install dnsmasq
# vi /etc/dnsmasq.conf

domain-needed
bogus-priv
local=/www.rarul.com/
local=/rinchan.com/
# /etc/init.d/dnsmasq start
# /sbin/chkconfig dnsmasq on

あらかじめ /etc/hosts にゴニョゴニョ書いてあるという想定で、dnsmasq は/etc/hostsを見て勝手に返信してくれる。
mail.rarul.comは外部ネットワークにいってほしいのでlocal=/www.rarul.com/で。
本当はmail.rinchan.comも外部ネットワークにいってほしいのだけどこっちは今のところあきらめ、、で local=/rinchan.com/

これであとはDHCPサーバで配信するDNSのIPアドレスをdnsmasq立てたマシンにし向ければ・・・と思ったら、フレッツ光プレミアムのCTUのDHCPサーバが、配信するDNSのIPアドレスは固定でCTU自身から変えられないでやんの。イヤなところで躓いた。

このCTUは代わりに、「ドメイン問い合わせ先設定」なんていう設定を持っている。これは、通常はCTU自身がDNS要求のリレーをする(内部クライアントから来た要求を外部ISPのDNSにリレーする)ところを、手動で入れたドメインに対する要求の時にだけ別のDNSにリレーするという設定。ここに「www.rarul.comはdnsmasq立てたマシンへ」「rinchan.comはdnsmasq立てたマシンへ」と書いとくと、この2つの名前解決の要求が内部のdnsmasqマシンへ飛んできてくれるようになる。ただし、dnsmasqの設定で「自分で解決できないrinchan.comドメインのは上位のリゾルバに任せる」としていると、dnsmasqから(上位リゾルバとして登録している)CTUに要求が飛び、CTUがdnsmasqにリレーして、dnsmasqがCTUに要求をとばし・・・を繰り返してCTUがしばらくハングしてしまう現象になる。ISPが変わるケースも考えるとdnsmasqの上位リゾルバはCTUしかあり得ないので、結局、dnsmasq.confの「local=」を記述することでこのループに入らないようにした。本当なら、CTUにDHCPサーバをやらすのをやめてdnsmasqのDHCPサーバを使うのがいいんだろうけど、これだとdnsmasq立ててるマシンが死ぬとネットワークが死んじゃうので却下。いろいろめんどいです。

さ、これで設定完了・・・と思ったらなぜか http://rinchan.com/ にアクセスできない。nslookupだとちゃんとプライベートIPアドレスが返ってきてるのに ping だとグローバルIPアドレスが返ってきてNG・・・結局調べると、WindowsのOSのDNSキャッシュでした・・・この手のを確認するときは「ipconfig /displaydns」「ipconfig /flushdns」を忘れずに、、

ちなみにUNIX系は、DNSの結果をプロセス単位でしかキャッシュせず、OSとしてはキャッシュしないそうな・・・え?そうなんや・・・言われてみれば確かにlibcの機能としてしか提供してなかったか・・・

今日のできごと

mmapが失敗すると言われてバグを追っかけてたら、引数のfdが間違っているようだった。ただ間違っているだけかと思ったら、問題が起きる瞬間にfdの入っているメモリが壊されているようだった。0x00とか0xFFとかに壊れてくれればよかったのに、中途半端に0x12に壊れていたせいで、有効なfdだと判断されてデバイスドライバのmmapにいってそこでエラーになっていた。

ファイルディスクリプタ番号すらプロセスのメモリ上の変数に過ぎないのは、やはり何かUNIXは設計を間違っていると思う。

というか、kernelのドライバのバグだと思ってたら、ユーザプロセス内のメモリ破壊だったわけで、結果utraceを調べてたりしてるわけで、必要だけど不毛なメモリ破壊の調査を行うという一日だった・・・というかまだ終わってない・・・

malloc_utraceの使い方

mallocでメモリ破壊とか2重freeとかやらかしてそうな時のutraceの使い方のメモ

まず、libcのmalloc.cあたりを眺めてutraceを有効にする。「HAS_UTRACE」とかで#ifdef切ってあってたぶん無効になっているので、有効にしてコンパイル。

次に、mallocデバッグしたいプロセス側からmalloc_utraceを有効にする。/etc/malloc.confでuを設定しといたり、MALLOC_OPTIONS環境変数にu入れといたり、void utrace_on(int on);関数を呼んだり、mallocライブラリによってまちまちなようなので、自分の使ってるのに合わせて対応する。
(※ 個々のプロセスでonするという仕組みなのは、こうなっていないとlibc使う人全員がデバッグオプションONになっていろいろうざいため)

最後に、utrace関数を実装する。適当に

typedef struct {
void *a;
size_t b;
void *c;
void *d;
} rinchan_t;
int rinchan=0;
void *rinchan_a[10000];
void *rinchan_c[10000];
void *rinchan_d[10000];
int utrace(rinchan_t *arg, int size){
int idx = rinchan++;
rinchan_a[idx] = arg->a;
rinchan_c[idx] = arg->c;
rinchan_d[idx] = arg->d;
}

とかをmallocデバッグしたいプロセス側で定義する。

FreeBSDに入っている(?)dlmallocの場合はこのプロトタイプでOKだけど、なんかmallocライブラリによってutraceプロトタイプが違うみたいなんでそこは適当に。C言語が高級アセンブラな人からすれば、プロトタイプなんてただの飾りですよ。

これでgdbつなぎながら問題を起こして、そのときgdbからrinchanを見ることで、どんな風なmalloc/freeとそれがどこから呼ばれたかのトレースがだいたいできる。見た結果、mallocを呼んでるのがcallocだとわかった、とかならなかなか泣けるけど・・・

rinchan配列1万で十分なのかどうかについては、私がデバッグしたかったアプリでは十分だっただけなので、適時適当に。rinchanがオーバフローするとリン廃が歓喜するだけなのでそれがイヤなマゾな人は、shigoto++とかどうぞ。え?shigotoはインクリメントじゃなくてデクリメントしてくものだ? そ う 思 っ て い た 時 期 が 私 に も あ り ま し た 。 ぜひ、がんばればshigotoがデクリメントしてく職場を紹介してください・・・

utrace関数内でprintfしようとすると、printfからmalloc呼ばれてそこからutrace呼ばれて・・・のコンボに入って、「あれutraceの使い方間違ってんのかな?」と変に調べ物に入って1時間くらい損するので注意。損した人が言うんだから間違いない。

utraceはweakシンボルかなにかになっているようで、utrace関数を定義しなかったらしなかったなりに適当にやってくれるっぽい。

なにぶん、ソースコードを斜め読みしながら試しに作ってみて動かした程度なので、間違ってたらすいません、、

このアーカイブについて

このページには、2013年7月に書かれたブログ記事が新しい順に公開されています。

前のアーカイブは2013年4月です。

次のアーカイブは2013年8月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

月別 アーカイブ

ウェブページ

Powered by Movable Type 7.9.0