<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>仙石浩明の日記</title>
	<atom:link href="http://www.gcd.org/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.gcd.org/blog</link>
	<description>CTO兼プログラマ兼システム管理者の視点から</description>
	<lastBuildDate>Wed, 04 Aug 2010 13:21:37 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>HYBRID W-ZERO3 の解約に続き、ウィルコムADSL も解約した。さよならウィルコム！</title>
		<link>http://www.gcd.org/blog/2010/08/630/</link>
		<comments>http://www.gcd.org/blog/2010/08/630/#comments</comments>
		<pubDate>Tue, 03 Aug 2010 23:07:18 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[その他]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=630</guid>
		<description><![CDATA[事業破綻の報道等で 「顧客 (サービス利用者) の保護」 という言葉をみかけることが多いが、 言語明瞭意味不明ワードの一つだと思う: PHS の解約が予想以上に進み、 2月に 417万人だった契約者数は 6月末には 388万人に減った。 7月下旬に予定していた裁判所への再建計画提出も、 「環境が変わった」として 10月に延期していた。 管財人らは、顧客を守るためにも、再建には通信会社の協力が必要と判断。 XGPを引き受けるソフトバンクに、PHS事業への支援に加わるよう求めていた。 朝日新聞 「ウィルコムのPHS事業縮小へ ソフトバンクが協力」 から引用 「顧客を守る」 などと書くと聞こえはよいが、 PHS サービス継続を望む顧客がどれだけいるかなんて、 「PHS の解約が予想以上に進」んでいることから明らかなはず。 現在 388万人(も) 契約者数が残っていると言ったって、 その大半は 2年縛りのせいで辞めるに辞められない人たちであって (私も 6月末時点だと契約者なのでこの 388万人に含まれている)、 現顧客にとって望ましいのはサービス継続じゃなくて 2年縛りを免除してあげることだと思う。 「顧客」 というと 「債権者」 というイメージが強いし、 この報道のように 「顧客を守る」 と書くとますます 「債権者たる顧客を守れ！」 というイメージが強調されるけど、 実状は 「辞めたいけど契約の縛りで払い続けざるを得ない債務者たる顧客」 と 「辞めていく顧客から少しでも多くの資金を回収したい債権者」 という (報道から受けるイメージとは真逆の) 構図であるわけで、 報道がいかに実状をゆがめたイメージを撒き散らしているかの一例だと思う。 私は丸 [...]]]></description>
			<content:encoded><![CDATA[<p>
事業破綻の報道等で
「顧客 (サービス利用者) の保護」
という言葉をみかけることが多いが、
言語明瞭意味不明ワードの一つだと思う:
</p>
<blockquote>
PHS の解約が予想以上に進み、
2月に 417万人だった契約者数は 6月末には 388万人に減った。
7月下旬に予定していた裁判所への再建計画提出も、
「環境が変わった」として 10月に延期していた。<br />
管財人らは、顧客を守るためにも、再建には通信会社の協力が必要と判断。
XGPを引き受けるソフトバンクに、PHS事業への支援に加わるよう求めていた。 
<div align="right">朝日新聞
「<a href="http://www.asahi.com/business/update/0802/TKY201008020401.html">ウィルコムのPHS事業縮小へ ソフトバンクが協力</a>」 
から引用</div>
</blockquote>
<p>
「顧客を守る」
などと書くと聞こえはよいが、
PHS サービス継続を望む顧客がどれだけいるかなんて、
「PHS の解約が予想以上に進」んでいることから明らかなはず。
現在 388万人(も) 契約者数が残っていると言ったって、
その大半は 2年縛りのせいで辞めるに辞められない人たちであって
(私も 6月末時点だと契約者なのでこの 388万人に含まれている)、
現顧客にとって望ましいのはサービス継続じゃなくて
2年縛りを免除してあげることだと思う。
</p>
<p>
「顧客」
というと
「債権者」
というイメージが強いし、
この報道のように
「顧客を守る」
と書くとますます
「債権者たる顧客を守れ！」
というイメージが強調されるけど、
実状は
「辞めたいけど契約の縛りで払い続けざるを得ない債務者たる顧客」
と
「辞めていく顧客から少しでも多くの資金を回収したい債権者」
という (報道から受けるイメージとは真逆の) 構図であるわけで、
報道がいかに実状をゆがめたイメージを撒き散らしているかの一例だと思う。
</p>
<p>
私は丸 4年 (なのでちょうど解約可能なタイミングだった)、
ウィルコムの PHS サービスを利用してきた。
また自宅のバックアップ回線として
4年間近く<a href="http://www.gcd.org/blog/2006/11/105/">ウィルコムADSL を利用してきた</a>。
なぜウィルコムだったかと言えば、
2006年当時まだ珍しかった 「スマートフォン」
<a href="http://www.sharp.co.jp/ws/007sh/">W-ZERO3[es]</a> を使いたかったから。
4年前ドコモショップで、
スマートフォンが使いたいから解約すると伝えたとき、
ドコモも近いうちにスマートフォンを出す予定 
(<a href="http://www.docomo.biz/html/product/htcz/">hTc Z</a>)
と担当者が言っていたのを思い出す。
</p>
<p>
その後、
W-ZERO3[es], Advanced W-ZERO3[es], HYBRID W-ZERO3 と 3代続けて 
W-ZERO3 シリーズを使い続けてきたが、
<a href="http://www.sharp.co.jp/ws/027sh/">HYBRID W-ZERO3</a> のあまりのデキの悪さ
(そもそも電話の着信音 /
バイブレーションが小さすぎて着信に気付けないのだから電話として失格)
に幻滅し、
乗り換えを決意した次第。
<a href="http://www.sharp.co.jp/ws/">輝かしい W-ZERO3 の歴史</a>の最後の最後で欠陥品を出してしまうとは 
(私は使ったことがないが一つ前の WILLCOM 03 も失敗作だった?)、
いったいシャープに何が起こったのか?
</p>
<blockquote>
iPhone を使いたいがためにソフトバンクに乗り換える人が圧倒的である昨今、
いまさら言うまでもないことだが、
(2006年ごろから?) 通信サービスは端末のオマケに成り下がったとつくづく思う。
いや、
端末ではなくコンテンツのほうがもっと大事だと言う人がいるかもしれないが、
そうなるのはもう少し先 (少なくともあと 3年くらい先)、
端末がコモディティ化した後の話だと思う。
少なくとも現時点では、
どんなに立派で魅力的なコンテンツでも、
それをユーザに届ける窓口である 「端末」 がヘタレであれば何の意味もない。
</blockquote>
<p>
一般に解約するのは骨が折れる。
多くの場合、解約は電話のみの受付だったり、
解約通知書を郵送しなければいけなかったりと、いろいろ手間がかかる。
Web だけで契約が済んでしまう入会の時とは対照的。
しかも解約の方法は Web を丹念に見ていかないと見つけられなかったりする。
退会率を下げたいという事業者側の気持ちも (職業がら) もちろん分かるのだが、
解約方法を分かりにくくすればするほどサポートコストは増えるしユーザ満足度も下がるわけで、
結局事業者自身の首を絞めることにしかなってないと思う。
</p>
<p>
特に、
ウィルコムADSL の解約は大変だったので以下にメモ:
</p>
<span id="more-630"></span>
<p>
まず何より、Web に 「ウィルコムADSL」 の解約方法が書かれていない。
少なくとも 
「<a href="http://www.google.co.jp/search?q=%E3%82%A6%E3%82%A3%E3%83%AB%E3%82%B3%E3%83%A0ADSL+%E8%A7%A3%E7%B4%84&amp;ie=utf-8&amp;hl=ja">ウィルコムADSL 解約</a>」 
で検索しただけでは適切なページを見つけられない。
どこに電話すべきか、
あるいはウィルコムプラザ (って神奈川県に一ヶ所しかないのか) 
に行く必要があるのかすら書いていない。
しかたないので総合窓口 0120-921-156 に電話してみたら、
本人認証を行なって解約理由まで聞いておきながら、
「この電話では解約を承れない、今から電話番号をお伝えするのでかけ直して欲しい」
などとおっしゃる。
</p>
<p>
こちらは最初から
「ウィルコムADSL を解約したい」
と伝えているのに、
10分以上かけていろいろ調べて手続きした挙げ句に
「かけ直せ」 
だったので呆れてしまった。
おそらく 「ADSL」 
が自分の担当範囲外であることすら分からない程度の知識で電話オペレータをやっているのだろう。
「ウィルコムADSL を解約したい」 と私が言っても、
「ウィルコムを解約したい」 に脳内で勝手に変換して聞いていたのではないか。
</p>
<p>
まあ、
コールセンタの電話オペレータのレベルなんて、
ウィルコムに限らずそんなものなのだろうが、
だからなおさら、
何でもかんでも電話オペレータが判断しなくても済むように 
Web を充実させてお客に勝手に判断させたほうが、
お互いハッピーになれるだろうにと思う。
</p>
<p>
ウィルコムADSL に関することは、
<a href="http://www.willcom-inc.com/ja/support/contact/index.html#3d">データ通信に関するお問合わせ</a>番号 0120-921-157
に電話すべきだったようだ。
メインメニューの 4番に 「ウィルコムADSL サービス」 があり、
その中のサブメニュー 2番に 「お申込・ご変更」 がある。
しかしここには 「解約」 の文字はない。
</p>
<p>
メインメニュー 3番のサブメニュー 2番には 「解約」 の文字があるが、
これはあくまでウィルコムの PHS データ通信の解約で、
eAccess のサービスであるところのウィルコムADSL とは関係ないのだろうと予想し、
メインメニューの 4番のサブメニュー 2番を選択し、
解約したい旨を伝えた。
すると、
「解約したいのはウィルコムの音声通話/データ通信サービスではなくて、
ADSL ですね？」
と聞き返された。
少なくともここのオペレータ (だけ?) は ADSL サービスが 
PHS サービスとは別に存在することを理解しているようだ。
</p>
<p>
ところが契約者が妻であることを伝えると
「契約者本人でないと解約できない」
などとおっしゃる。
そもそも契約したときだって私が妻の名義で契約した (^^;) のに、
なぜに解約の時だけ本人でないとダメなのか? とは思ったが、
たまたま隣に妻がいたので、電話を代わってもらう。
そうしたら住所と生年月日を確認しただけで 「本人認証」 が完了してしまい、
再び電話を代わる。
もちろん私だって、
妻の住所と生年月日くらいは知っている。
あまりの無意味さに妻も憮然としていた。
</p>
<p>
ウィルコムADSLサービスは
「年間契約」
での加入だそうで、
1年間の契約期間中の解約は 2100円を請求するとのこと
(1年超の場合。契約から1年以内の解約の場合は 4200円)。
月額料金 (2585円) より安いので躊躇せず今月〆日での解約を申し込む
(日割精算も可能)。
</p>
<p>
すると解約日から 10日以内にモデム
Aterm WD735GV を、
AC電源アダプタおよび縦置きスタンドと共に返却せよなどとおっしゃる。
なにぶん 4年近く使い続けたモデムなので送られてきたときの箱がまだあるかどうか...
見つからなければ適当な梱包材を準備しなければならない。
しかも送付先は今から口頭で伝えるのでメモしろとのたまう。
きょうび住所を電話で聞かされる羽目になるとは思ってもみなかった。
</p>
<p>
「かけがわし しもまた」
などと馴染みのない地名を口頭で言われて、
すぐメモできる人がどれだけいるというのか
(文字で書くと簡単そうに見えるが、
声で聞くと 「かけがわ」 だか 「たけかわ」 だか聞き分けにくい)。
「『かけがわ』は掛け布団の掛けに川、
『しもまた』は上下の下に水俣の俣」 
などと言われながら (こんな表現を聞くのは何年ぶりだろう?
昔の人はフツーにこういう効率の悪いことをやっていたのだろうけど)、
何度も聞き返しながら苦労して書き取ったので掲載:
</p>
<blockquote>
〒436-8501 静岡県 掛川市 下俣 800<br />
NECアクセステクニカ株式会社 SL製造部 レンタルグループ<br />
TEL 0537-23-1164
</blockquote>
<p>
この住所で検索してみると、
eAccess の
<a href="http://www.eaccess.net/support/modem/returnmanner3-1.html">モデム返却先ページ</a>が見つかった。
ウィルコムADSLサービスの Web サイト内に、
同様のページを作るか、
eAccess へのリンクを貼っておけば済む話なのに、
電話で口頭で伝えようとするからコールセンタの人件費は余計にかかるは、
ユーザ (つまり私) の満足度は著しく低下するはで、
何一ついいことがない。
さらに言えば、
10日以内に返却する必要があるならその旨と、
同梱すべきものや梱包方法などと一緒に Web ページに記載してあれば、
解約申込みをする前に余裕を持って準備することができただろう。
</p>
<p>
ちなみに、
「<a href="http://www.google.co.jp/search?q=%E8%BF%94%E5%8D%B4+NEC%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%86%E3%82%AF%E3%83%8B%E3%82%AB+SL%E8%A3%BD%E9%80%A0%E9%83%A8+%E3%83%AC%E3%83%B3%E3%82%BF%E3%83%AB%E3%82%B0%E3%83%AB%E3%83%BC%E3%83%97&amp;ie=utf-8&amp;hl=ja">返却 NECアクセステクニカ SL製造部 レンタルグループ</a>」
で検索すると、
同型モデムを利用している各プロバイダのモデム返却ページが続々と見つかる。
<a href="http://www.odn.ne.jp/">ODN</a>, 
<a href="http://dream.jp/">DTI</a>, 
<a href="http://support.biglobe.ne.jp/">Biglobe</a>,
<a href="http://www.so-net.ne.jp/">So-net</a>,
<a href="http://www.t-com.ne.jp/">@T COM</a>,
<a href="http://support.nifty.com/">@nifty</a>,
<a href="http://home.hi-ho.ne.jp/">hi-ho</a>,
<a href="http://search.asahi-net.jp/">ASAHIネット</a>,...
等々、
数多くのプロバイダがきちんと解約手続きの方法を Web で説明している中、
ウィルコムADSL だけ(?)が解約方法に全く言及していないのはいかがなものか。
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/08/630/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Nexus One で Android 2.2 froyo のマルチタッチを試してみる</title>
		<link>http://www.gcd.org/blog/2010/07/613/</link>
		<comments>http://www.gcd.org/blog/2010/07/613/#comments</comments>
		<pubDate>Sun, 25 Jul 2010 23:57:55 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[プログラミングと開発環境]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=613</guid>
		<description><![CDATA[Android は 2.1-update1 以降でマルチタッチ (Multi-touch) をサポートしている。 ところがマルチタッチといっても、 ピンチイン/ピンチアウトなどのジェスチャをサポートしているだけのアプリが大半で、 複数のタッチを独立に扱えるアプリはいまだほとんどなく、 iPhone と比べるとその差が際立っている。 どうして Android にはマルチタッチを活用したアプリケーションが無いのだろう？ と思ったので、 マルチタッチを試すテストアプリ MultiTouch.java (apk) を書いてみた: タッチした位置にタッチの強さに応じた大きさの円を表示するだけの単純なアプリ。 指を移動すれば円も追随する。 Android ではタッチID が順に割り振られるので、 ID が 0 のタッチを赤色の円で、 ID が 1 のタッチを緑色の円で描いている。 プログラム上は ID が 2 のタッチを青色の円で描くことになっているが、 残念ながら現行の Android で同時に扱えるタッチは 2箇所のみ (追記: Samsung Galaxy S は 5箇所のマルチタッチが可能らしい) なので、 3箇所にタッチしても三つ目の円が描かれることはない。 だから例えば iPhone のアプリにあるような鍵盤楽器アプリを作ろうと思っても、 三つ以上の音を同時に鳴らすことはできない。 とはいえ、 2箇所のタッチを独立に扱えれば、 [...]]]></description>
			<content:encoded><![CDATA[<p>
Android は 2.1-update1 以降でマルチタッチ (Multi-touch) をサポートしている。
ところがマルチタッチといっても、
ピンチイン/ピンチアウトなどのジェスチャをサポートしているだけのアプリが大半で、
複数のタッチを独立に扱えるアプリはいまだほとんどなく、
iPhone と比べるとその差が際立っている。
</p>
<p>
どうして Android にはマルチタッチを活用したアプリケーションが無いのだろう？
と思ったので、
マルチタッチを試すテストアプリ
<a href="http://www.gcd.org/sengoku/android/MultiTouch.java">MultiTouch.java</a>
(<a href="http://www.gcd.org/sengoku/android/MultiTouch-debug.apk">apk</a>)
を書いてみた:
</p>
<img src="http://www.gcd.org/sengoku/android/MultiTouch.jpg" alt="MultiTouch" width="350" height="225" />
<p>
タッチした位置にタッチの強さに応じた大きさの円を表示するだけの単純なアプリ。
指を移動すれば円も追随する。
Android ではタッチID が順に割り振られるので、
ID が 0 のタッチを赤色の円で、
ID が 1 のタッチを緑色の円で描いている。
</p>
<p>
プログラム上は ID が 2 のタッチを青色の円で描くことになっているが、
残念ながら現行の Android で同時に扱えるタッチは 2箇所のみ
(追記: <a href="http://www.youtube.com/watch?v=KRCDRXYJBCY">Samsung Galaxy S は
5箇所のマルチタッチが可能</a>らしい)
なので、
3箇所にタッチしても三つ目の円が描かれることはない。
だから例えば iPhone のアプリにあるような鍵盤楽器アプリを作ろうと思っても、
三つ以上の音を同時に鳴らすことはできない。
</p>
<p>
とはいえ、
2箇所のタッチを独立に扱えれば、
いろいろ応用が効くだろうにと思いつつ、
このテストアプリをいじっていると...
</p>
<span id="more-613"></span>
<p>
ありゃ?
</p>
<p>
上の写真の状態から、
左指を上方へ、右指を下方へ動かしただけなのだが、
両方の指の X 座標 (画面では上下方向) が交わった時点で両方の円の 
X 座標が入れ替わってしまって、
緑円は右指の動きに合わせて下へ動き、
赤円は左指の動きに合わせて上に動いた。
その結果、
指の位置と円の位置がずれてしまった:
</p>
<img src="http://www.gcd.org/sengoku/android/MultiTouch2.jpg" alt="MultiTouch Error" width="350" height="225" />
<p>
つまり複数のタッチ 
(x<sub>i</sub>, y<sub>i</sub>),
(x<sub>j</sub>, y<sub>j</sub>)
を個別に扱えるのではなく、
X 座標の集合 {x<sub>i</sub>, x<sub>j</sub>} と
Y 座標の集合 {y<sub>i</sub>, y<sub>j</sub>} として検知している模様。
例えて言うならダイオード無しのスイッチ・マトリックスみたいな感じ。
だから (x<sub>i</sub>, y<sub>i</sub>), (x<sub>j</sub>, y<sub>j</sub>) の
2点をタッチしているのか、
(x<sub>i</sub>, y<sub>j</sub>), (x<sub>j</sub>, y<sub>i</sub>) の
2点をタッチしているのか、
の区別ができない。
</p>
<p>
Android のマルチタッチを扱うプログラムの書き方について<a href="http://www.zdnet.com/blog/burnette/how-to-use-multi-touch-in-android-2-part-3-understanding-touch-events/1775">解説している Web ページ</a>をほとんど見かけないので、
<a href="http://www.gcd.org/sengoku/android/MultiTouch.java">MultiTouch.java</a>
についても一応解説:
</p>
<p>
タッチイベントは、
<a href="http://developer.android.com/reference/android/view/MotionEvent.html">MotionEvent</a> 
インスタンスとして Android OS からアプリへ伝えられる。
MotionEvent#getAction() メソッドでイベント種別 
(ACTION_DOWN ならタッチ開始、
ACTION_MOVE ならドラッグ、
ACTION_UP ならタッチ終了といった具合) が取得できる。
</p>
<p>
マルチタッチの場合、
すなわちすでにタッチしている状況において追加で別の場所にタッチした場合、
ACTION_DOWN の代りに ACTION_POINTER_DOWN イベントが伝えられる。
パネルから指が離れてタッチが終わる場合、
まだ他の場所でタッチが継続しているのなら、
ACTION_UP の代りに ACTION_POINTER_UP イベントが伝えられる。
</p>
<p>
今回のテストプログラムの場合、
タッチの座標が分かればよいので、
ACTION_DOWN, ACTION_POINTER_DOWN, ACTION_MOVE は区別せずに、
put_points メソッド (後述) でイベントの座標を記録している。
</p>
<pre class="code">
	public boolean onTouchEvent(MotionEvent ev) {
	    int action = ev.getAction();
	    switch (action &amp; MotionEvent.ACTION_MASK) {
	    case MotionEvent.ACTION_DOWN:
	    case MotionEvent.ACTION_POINTER_DOWN:
	    case MotionEvent.ACTION_MOVE:
		put_points(ev);
		break;
	    case MotionEvent.ACTION_UP:
		points.remove(ev.getPointerId(0));
		break;
	    case MotionEvent.ACTION_POINTER_UP:
		put_points(ev);
		int index = (action &amp; MotionEvent.ACTION_POINTER_INDEX_MASK)
		    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
		points.remove(ev.getPointerId(index));
		break;
	    }
	    Canvas canvas = getHolder().lockCanvas();
	    if (canvas != null) {
		onDraw(canvas);
		getHolder().unlockCanvasAndPost(canvas);
	    }
	    return true;
	}
</pre>
<p>
MotionEvent インスタンスには
複数 (現状 2個までだが、
API 仕様的には 256個まで可能) のタッチの座標および強さ (pressure) 
が格納できて、
getX(i), getY(i), getPressure(i) の各メソッドを呼び出すことにより、
i番目 (i=0, 1,...) のタッチの X座標、Y座標、強さがそれぞれ取得できる。
タッチの個数は getPointerCount() メソッドで取得できる。
マルチタッチをサポートしない Android 2.0 eclair だと
getPointerCount() は常に 1 を返す
(ちなみに 1.5 cupcake や 1.6 donut だと getPointerCount() は存在しない)。
</p>
<p>
各タッチは、
指がパネルに触れてから離れるまで固有の ID が OS によって割り振られるので、
アプリは各タッチの動きをそれぞれ追うことができる。
getPointerId(i) メソッドを使えば i番目のタッチの ID が取得できる。
</p>
<p>
put_points(MotionEvent ev) メソッドでは、
id をキーとし、
TouchPoint (X座標、Y座標、強さの組) を値とするハッシュテーブル points に、
各タッチの現在値を登録している:
</p>
<pre class="code">
    class TouchPoint {
	public float x;
	public float y;
	public float p;
    }
    Hashtable&lt;Integer, TouchPoint> points;
	...
	points = new Hashtable&lt;Integer, TouchPoint>();
	...

	void put_points(MotionEvent ev) {
	    int count = ev.getPointerCount();
	    for (int i=0; i < count; i++) {
		int id = ev.getPointerId(i);
		TouchPoint p = new TouchPoint();
		p.x = ev.getX(i);
		p.y = ev.getY(i);
		p.p = ev.getPressure(i);
		points.put(id, p);
	    }
	}
</pre>
<p>
現状 ID は 0 または 1 の値しかとらないようなので、
ハッシュテーブル (java.util.Hashtable) は牛刀な感を否めないが、
API 仕様上 ID は int 型としか規定していない
(つまり最大値が 1ではなく int 型の上限となる可能性がある ^^;)
ので、
配列ではなくハッシュテーブルを使った次第。
</p>
<p>
あとは
SurfaceView#onDraw(Canvas canvas)
にて、
points ハッシュテーブルに登録された座標、強さのデータ通りに円を描くだけ。
</p>

<p>
追記:<br />
twitter で <a href="http://galaxys.samsungmobile.com/">Galaxy S</a> のマルチタッチはマトモと教えていただきました (_O_)<br />
以下、頂いたツイートを (時間順に) 引用:
</p>
<table class="tweet">
<tr><td><a href="http://twitter.com/gcd_org"><img alt="仙石浩明" height="48" src="http://a3.twimg.com/profile_images/488643843/sengoku_normal.jpg" width="48" /></a></td>
<td><strong><a href="http://twitter.com/gcd_org">gcd_org</a></strong>
Androidのマルチタッチについて書いてるページが見当たらなかったのでブログを書いてみました <a href="http://www.gcd.org/blog/2010/07/613/">http://www.gcd.org/blog/2010/07/613/</a> NexusOneだと座標を正しく取得できないケースがあるのですが他の機種だとどうですか？ <a href="http://twitter.com/search?q=%23androidjp">#androidjp</a><br />
<a class="entry-date" href="http://twitter.com/gcd_org/status/19536531076">2010年7月26日 10:41:34</a>
</td></tr>
<tr><td><a href="http://twitter.com/northeye"><img alt="Northeye" height="48" src="http://a1.twimg.com/profile_images/875256693/doya_normal.jpg" width="48" /></a></td>
<td><strong><a href="http://twitter.com/northeye">northeye</a></strong>
@<a href="http://twitter.com/gcd_org">gcd_org</a> Galaxy Sはわりとまとものようです <a href="http://www.youtube.com/watch?v=hVlsRCMltDg">http://www.youtube.com/watch?v=hVlsRCMltDg</a><br />
<a class="entry-date" href="http://twitter.com/northeye/status/19536763704">2010年7月26日 10:45:20</a>
</td></tr>
<tr><td><a href="http://twitter.com/gcd_org"><img alt="仙石浩明" height="48" src="http://a3.twimg.com/profile_images/488643843/sengoku_normal.jpg" width="48" /></a></td>
<td><strong><a href="http://twitter.com/gcd_org">gcd_org</a></strong>
@<a href="http://twitter.com/northeye">northeye</a> すごい＞Galaxy Sのマルチタッチ。これで3点以上のマルチタッチがサポートされたら言うことなしですね。 RT Galaxy Sはわりとまとものようです <a href="http://www.youtube.com/watch?v=hVlsRCMltDg">http://www.youtube.com/watch?v=hVlsRCMltDg</a><br />
<a class="entry-date" href="http://twitter.com/gcd_org/status/19537320366">2010年7月26日 10:54:20</a>
</td></tr>
<tr><td><a href="http://twitter.com/northeye"><img alt="Northeye" height="48" src="http://a1.twimg.com/profile_images/875256693/doya_normal.jpg" width="48" /></a></td>
<td><strong><a href="http://twitter.com/northeye">northeye</a></strong>
@<a href="http://twitter.com/gcd_org">gcd_org</a> 5点までいけるそうです <a href="http://www.youtube.com/watch?v=KRCDRXYJBCY">http://www.youtube.com/watch?v=KRCDRXYJBCY</a><br />
<a class="entry-date" href="http://twitter.com/northeye/status/19537433283">2010年7月26日 10:56:10</a>
</td></tr>
<tr><td><a href="http://twitter.com/nakamichito"><img alt="なかみちと" src="http://a1.twimg.com/profile_images/250689694/IAKlLwG2IZ__Clb1NZqDgxLvRu_uf8x4MrSp6N4ey1-AzfpyZ2d9efuh0R8ra-nj_normal.jpg" width="48" height="48" /></a></td>
<td><strong><a href="http://twitter.com/nakamichito">nakamichito</a></strong>
Galaxy Sではこんな感じです RT @<a href="http://twitter.com/gcd_org">gcd_org</a>: Androidのマルチタッチについて書いてるページが見当たらなかったのでブログを書いてみました <a href="http://www.gcd.org/blog/2010/07/613/">http://www.gcd.org/blog/2010/07/613/</a> <a href="http://twitter.com/search?q=%23androidjp">#androidjp</a><br />
<a class="entry-date" href="http://twitter.com/nakamichito/status/19538572777">2010年7月26日 11:13:40</a>
</td></tr>
<tr><td><a href="http://twitter.com/nakamichito"><img alt="なかみちと" src="http://a1.twimg.com/profile_images/250689694/IAKlLwG2IZ__Clb1NZqDgxLvRu_uf8x4MrSp6N4ey1-AzfpyZ2d9efuh0R8ra-nj_normal.jpg" width="48" height="48" /></a></td>
<td><strong><a href="http://twitter.com/nakamichito">nakamichito</a></strong>
URL忘れてた！ <a href="http://bit.ly/bFBicn">http://bit.ly/bFBicn</a> RT @<a href="http://twitter.com/nakamichito">nakamichito</a>: Galaxy Sではこんな感じです RT @<a href="http://twitter.com/gcd_org">gcd_org</a>: Androidのマルチタッチについて書いてるページが見当たらなかったのでブログを書いてみました <a href="http://twitter.com/search?q=%23androidjp">#androidjp</a><br />
<a class="entry-date" href="http://twitter.com/nakamichito/status/19543213654">2010年7月26日 12:25:39</a>
</td></tr>
</table>
<p>
すでに同種のテストアプリが Android Market で公開されていたのですね。<br />
車輪の再発明をしてしまった (後悔はしていない ;-)。
</p>]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/07/613/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Nexus One の近接センサ/環境光センサは、どこにあるのか？調べてみた</title>
		<link>http://www.gcd.org/blog/2010/07/609/</link>
		<comments>http://www.gcd.org/blog/2010/07/609/#comments</comments>
		<pubDate>Fri, 16 Jul 2010 23:55:21 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[プログラミングと開発環境]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=609</guid>
		<description><![CDATA[Nexus One など最近のスマートフォンには、 加速度 (Accelerometer)、 環境光 (照度, Ambient Light)、 磁場 (磁界, Magnetic Field)、 方位 (電子コンパス, Orientation)、 近接 (Proximity) など、 様々なセンサがついている。 いろいろ応用できそうで夢がふくらむが、 携帯電話本来の使い方 (つまり通話すること) において、 使い勝手に直接影響する重要なセンサが近接センサ。 Nexus One や iPhone など全面タッチパネルの携帯電話だと、 (受話器として使うために) 耳に近づけたときタッチパネルが反応しては困る。 そこで近接センサを使って顔が接近してくることを感知し、 タッチパネルを無効にする (ついでにディスプレイをオフにして消費電力を抑える)。 私は Proximity なんて聞くと、 Proximity Warning System (接近警報システム) を思い浮かべてしまうくらいで、 携帯電話用の近接センサがどういうしくみか全く知らなかった。 今年1月の Nexus One の発表の時に近接センサのことを初めて知り、 その時はタッチパネル全体への接近を感知する (静電容量の変化を検知して?) のかと想像したが、 後述するように Nexus One の近接センサはタッチパネルの左上にしかなく、 [...]]]></description>
			<content:encoded><![CDATA[<p>
Nexus One など最近のスマートフォンには、
加速度 (Accelerometer)、
環境光 (照度, Ambient Light)、
磁場 (磁界, Magnetic Field)、
方位 (電子コンパス, Orientation)、
近接 (Proximity) など、
様々なセンサがついている。
いろいろ応用できそうで夢がふくらむが、
携帯電話本来の使い方 (つまり通話すること) において、
使い勝手に直接影響する重要なセンサが近接センサ。
</p>
<p>
Nexus One や iPhone など全面タッチパネルの携帯電話だと、
(受話器として使うために) 耳に近づけたときタッチパネルが反応しては困る。
そこで近接センサを使って顔が接近してくることを感知し、
タッチパネルを無効にする 
(ついでにディスプレイをオフにして消費電力を抑える)。
</p>
<blockquote>
私は Proximity なんて聞くと、
Proximity Warning System (接近警報システム) を思い浮かべてしまうくらいで、
携帯電話用の近接センサがどういうしくみか全く知らなかった。
今年1月の Nexus One の発表の時に近接センサのことを初めて知り、
その時はタッチパネル全体への接近を感知する (静電容量の変化を検知して?) 
のかと想像したが、
後述するように Nexus One の近接センサはタッチパネルの左上にしかなく、
タッチパネルの下方への接近は感知できないことが分かった。
</blockquote>
<p>
Nexus One のどこに近接センサが搭載されているか、
センサの感応範囲がどれくらいなのか、
私には見当もつかなかったし、
google で検索してもその手の情報は見つからなかったので、
近接センサが感知した値を表示するだけの<a href="http://www.gcd.org/sengoku/android/Proximity.java">簡単なプログラム</a>を書いてみた。
</p>
<p>
実は、
私にとって初めての android アプリ (^^;)。
しかも、
ここ数年 Java から遠ざかっていたので、
久々に書く Java プログラムだったりする。
</p>
<p>
お膳立ては Android SDK が全てやってくれるので、
わずか 74行のプログラム。
まず
SensorManager#getSensorList メソッドで、
PROXIMITY タイプのセンサを取得し (sensor)、
</p>
<pre class="code">
	sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
	List&lt;Sensor> sensors
	    = sensorManager.getSensorList(Sensor.TYPE_PROXIMITY);
	Sensor sensor = sensors.get(0);
</pre>
<p>
この sensor の値が変化したときなどにセンサの値を受け取るリスナ
(SensorEventListener) を、
SensorManager#registerListener メソッドで登録するだけ。
</p>
<pre class="code">
public class Proximity implements SensorEventListener {
	...
	sensorManager.registerListener(this, sensor,
				       SensorManager.SENSOR_DELAY_NORMAL);
	...
    @Override
    public void onSensorChanged(SensorEvent event) {
	view.update(event.values);
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
}
</pre>
<p>
近接センサの値が変化するとリスナの onSensorChanged メソッドが呼び出されるので、
新しいセンサ値を描画する (view.update):
</p>
<pre class="code">
	void update(float[] values) {
	    Canvas canvas = getHolder().lockCanvas();
	    if (canvas == null) return;
	    canvas.drawColor(Color.WHITE);
	    Paint paint = new Paint();
	    paint.setColor(Color.BLACK);
	    paint.setTextSize(40);
	    float height = paint.getTextSize();
	    for (int i = 0; i &lt; values.length; i++) {
		canvas.drawText(" "+values[i], 0, height * (i + 1), paint);
	    }
	    getHolder().unlockCanvasAndPost(canvas);
	}
</pre>
<p>
Nexus One のタッチパネル左上隅近く (黒枠部分) にセンサがあるらしく、
パネルまで 2cm ほどの距離に物体を近づけると反応する 
(センサの値が 9.0 から 0.0 へ変化する)。
また、
パネルと並行に物体を動かす場合、
センサの真上から 1cm ほど外れると反応しなくなる。
</p>
<table>
<tr>
<td>
<img src="http://www.gcd.org/sengoku/android/ProximitySensor.jpg" alt="Proximity Sensor" width="210" height="210" />
</td>
<td>　←　</td>
<td>
鈴を近づけたことにより、<br />
近接センサが反応して、<br />
値が 0.0 になっている<br />
</td>
</tr>
</table>
<p>
赤外線型の近接センサ 
(赤外線を照射し、近接する物体からの反射光を測定するセンサ) 
なので、
凸面の物体など赤外線があさっての方向へ反射してしまって受光素子に正しく届かない場合や、
あるいは黒色の物体などあまり反射しない場合などでは、
より近づけないと反応しない。
</p>
<p>
例えば、
黒く細い丸棒などだと 1cm 以下に近づけないと反応しない。
逆に白い紙 (凹面〜平面) など、
効果的に赤外線を反射し、かつ受光素子に反射光が効率的に届くケースだと、
2cm より遠くても (8cm くらいでも) 反応する。
</p>
<p>
ちなみに、
Nexus One に搭載されている近接センサは、
<a href="http://www.capellamicro.com.tw/">Capella Microsystems</a> の 
<a href="http://www.capellamicro.com.tw/EN/products_view.php?id=45">CM3602</a>
という、
環境光センサ付短距離近接センサ 
(Short Distance Proximity Sensor with Ambient Light Sensor) であるようだ。
名前の通り環境光も測定できる。
おそらく近接センサの受光素子をそのまま使って照度を測定しているのだろう。
</p>
<p>
<a href="http://www.gcd.org/sengoku/android/Proximity.java">前述したプログラム</a>において、
「Sensor.TYPE_PROXIMITY」 を
「Sensor.TYPE_LIGHT」 に置き換えれば、
環境光センサの値を読み取ることができる。
</p>
<span id="more-609"></span>
<p>
Android SDK において、
Eclipse を使わずにアプリを開発する方法を<a href="http://dsas.blog.klab.org/archives/51165740.html">説明しているページ</a>が少ないので、
蛇足ながらビルド〜インストール方法を書いてみる。
まず android コマンド (Android SDK の tools ディレクトリにある)
を使って Project ディレクトリを作る:
</p>
<pre class="terminal">
T:\src\android>android create project --target 1 --path Proximity --activity Proximity --package org.gcd.test
Created project directory: T:\src\android\Proximity
Created directory T:\src\android\Proximity\src\org\gcd\test
Added file T:\src\android\Proximity\src\org\gcd\test\Proximity.java
Created directory T:\src\android\Proximity\res
Created directory T:\src\android\Proximity\bin
Created directory T:\src\android\Proximity\libs
Created directory T:\src\android\Proximity\res\values
Added file T:\src\android\Proximity\res\values\strings.xml
Created directory T:\src\android\Proximity\res\layout
Added file T:\src\android\Proximity\res\layout\main.xml
Created directory T:\src\android\Proximity\res\drawable-hdpi
Created directory T:\src\android\Proximity\res\drawable-mdpi
Created directory T:\src\android\Proximity\res\drawable-ldpi
Added file T:\src\android\Proximity\AndroidManifest.xml
Added file T:\src\android\Proximity\build.xml

T:\src\android>
</pre>
<p>
「--target &lt;target_ID>」 オプションは、
利用する Android platform library を指定する。
&lt;target_ID> の一覧は、
「android list targets」 を実行することで得られる。
</p>
<p>
「--activity &lt;activity_name>」 オプションは、
アプリを起動したとき最初に表示される画面のクラス名を指定する。
</p>
<p>
作った Project ディレクトリ T:\src\android\Proximity の中に、
T:\src\android\Proximity\src\org\gcd\test\Proximity.java があるので、
これを<a href="http://www.gcd.org/sengoku/android/Proximity.java">書き換える</a>。
私の場合、
T: ドライブは Linux マシン (正確に言えば coLinux) のファイルシステムなので、
Linux 上の emacs を使って書き換えている。
</p>
<p>
あとは ant コマンドを使ってビルドするだけ。
Eclipse などの IDE (統合開発環境) を使う必要性を全く感じないのだが...
</p>
<pre class="terminal">
T:\src\android>cd Proximity

T:\src\android\Proximity>ant debug
Buildfile: T:\src\android\Proximity\build.xml
    [setup] Android SDK Tools Revision 6
    [setup] Project Target: Google APIs
    [setup] Vendor: Google Inc.
    [setup] Platform Version: 2.1-update1
    [setup] API level: 7
    [setup] WARNING: No minSdkVersion value set. Application will install on all Android versions.
    [setup] Importing rules file: platforms\android-7\ant\ant_rules_r2.xml

-compile-tested-if-test:

-dirs:
     [echo] Creating output directories if needed...
    [mkdir] Created dir: T:\src\android\Proximity\gen
    [mkdir] Created dir: T:\src\android\Proximity\bin\classes

-resource-src:
     [echo] Generating R.java / Manifest.java from the resources...

-aidl:
     [echo] Compiling aidl files into Java classes...

compile:
    [javac] C:\android-sdk-windows\platforms\android-7\ant\ant_rules_r2.xml:255: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [javac] Warning: org\gcd\test\R.java modified in the future.
    [javac] Compiling 2 source files to T:\src\android\Proximity\bin\classes

-dex:
     [echo] Converting compiled files and external libraries into T:\src\android\Proximity\bin\classes.dex...

-package-resources:
     [echo] Packaging resources
 [aaptexec] Creating full resource package...

-package-debug-sign:
[apkbuilder] Creating Proximity-debug-unaligned.apk and signing it with a debug key...
[apkbuilder] Using keystore: C:\Documents and Settings\sengoku\.android\debug.keystore

debug:
     [echo] Running zip align on final apk...
     [echo] Debug Package: T:\src\android\Proximity\bin\Proximity-debug.apk

BUILD SUCCESSFUL
Total time: 6 seconds
T:\src\android\Proximity>
</pre>
<p>
T:\src\android\Proximity\bin\Proximity-debug.apk
に apk ファイル (Android Package) ができるので、
adb コマンド
(これも Android SDK の tools ディレクトリにある)
を使って Nexus One へインストールする。
</p>
<pre class="terminal">
T:\src\android\Proximity>adb install bin\Proximity-debug.apk
296 KB/s (14596 bytes in 0.048s)
        pkg: /data/local/tmp/Proximity-debug.apk
Success

T:\src\android\Proximity>
</pre>
<p>
あるいは、
T:\src\android\Proximity\bin\Proximity-debug.apk を 
<a href="http://www.gcd.org/sengoku/android/Proximity-debug.apk">Web サーバ上に置いて</a>、
Internet 経由でダウンロード &amp; インストールしてもよい
(アプリケーションの設定にて、
「提供元不明のアプリ」 のインストールの許可が必要)。
</p>
<p>
以上、
Windows のコマンドプロンプトでの実行例を示したが、
もちろん Linux 上でも全く同様にビルド〜インストールが可能。
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/07/609/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Google カレンダーの過去の予定を自動的に削除する方法</title>
		<link>http://www.gcd.org/blog/2010/07/606/</link>
		<comments>http://www.gcd.org/blog/2010/07/606/#comments</comments>
		<pubDate>Thu, 08 Jul 2010 00:44:19 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[プログラミングと開発環境]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=606</guid>
		<description><![CDATA[ここ 2ヶ月ほど、 Willcom の HYBRID W-ZERO3 から Nexus One への移行を徐々に進めてきた。 Windows Mobile (HYBRID W-ZERO3) は PC とのデータ同期が基本なので、 PC にスケジュールや電話帳など丸ごと入れておけば済むが、 Android (Nexus One) の場合は 「クラウド」 との同期が基本なので一筋縄にはいかない。 つまり 「クラウド」 に全てのスケジュールを置いていいのか？ という問題。 私の場合、 「スケジュール」 といいつつ会議の議事メモから個人的な日記まで、 プライベートな情報を全て集積しているので問題がより深刻になる。 ちなみに私は、 1999年に WorkPad 30J を使い始めて以来、 プライベートなデータを PDA / スマートフォンに集積してきた。 今まで使ってきた PDA / スマートフォンをまとめてみる: 購入月機種OS 1999-04 WorkPad 30J PalmOS 3.1J 2000-03 TRGpro PalmOS [...]]]></description>
			<content:encoded><![CDATA[<p>
ここ 2ヶ月ほど、
Willcom の 
<a href="http://www.willcom-inc.com/ja/lineup/ws/027sh/">HYBRID W-ZERO3</a>
から 
<a href="http://www.google.com/phone/">Nexus One</a> 
への移行を徐々に進めてきた。
Windows Mobile (HYBRID W-ZERO3) は PC とのデータ同期が基本なので、
PC にスケジュールや電話帳など丸ごと入れておけば済むが、
Android (Nexus One) の場合は
「クラウド」
との同期が基本なので一筋縄にはいかない。
つまり
「クラウド」
に全てのスケジュールを置いていいのか？
という問題。
私の場合、
「スケジュール」
といいつつ会議の議事メモから個人的な日記まで、
プライベートな情報を全て集積しているので問題がより深刻になる。
</p>
<p>
ちなみに私は、
1999年に WorkPad 30J を使い始めて以来、
プライベートなデータを PDA / スマートフォンに集積してきた。
今まで使ってきた PDA / スマートフォンをまとめてみる:
</p>
<table class="ruler">
<tr><th>購入月</th><th>機種</th><th>OS</th></tr>
<tr><td>1999-04</td>
<td><a href="http://ja.wikipedia.org/wiki/WorkPad">WorkPad 30J</a></td>
<td>PalmOS 3.1J</td></tr>
<tr><td>2000-03</td>
<td><a href="http://www.handera.com/Products/TRGpro.aspx">TRGpro</a></td>
<td>PalmOS 3.5</td></tr>
<tr><td>2000-08</td>
<td><a href="http://en.wikipedia.org/wiki/Palm_m100_series">Palm m100</a></td>
<td>PalmOS 3.5.1</td></tr>
<tr><td>2002-09</td>
<td><a href="http://www.sharp.co.jp/products/mie1/">Zaurus MI-E1</a></td>
<td>ZaurusOS</td></tr>
<tr><td>2002-12</td>
<td><a href="http://ezaurus.com/lineup/sl/slc700/slc700_spec.html">Linux Zaurus SL-C700</a></td>
<td>Linux 2.4 Embedix</td></tr>
<tr><td>2003-06</td>
<td><a href="http://ezaurus.com/lineup/sl/slc750/slc750_spec.html">Linux Zaurus SL-C750</a></td>
<td>Linux 2.4 Embedix</td></tr>
<tr><td>2006-07</td>
<td><a href="http://www.sharp.co.jp/ws/007sh/">W-ZERO3[es]</a></td>
<td>Windows Mobile 5.0</td></tr>
<tr><td>2007-07</td>
<td><a href="http://www.sharp.co.jp/ws/011sh/">Advanced W-ZERO3[es]</a></td>
<td>Windows Mobile 6 Classic</td></tr>
<tr><td>2008-12</td>
<td><a href="http://www.htc.com/jp/product/p3600/specification.html">HTC P3600</a></td>
<td>Windows Mobile 5.0</td></tr>
<tr><td>2010-01</td>
<td><a href="http://www.sharp.co.jp/ws/027sh/">HYBRID W-ZERO3</a></td>
<td>Windows Mobile 6.5 Professional</td></tr>
<tr><td>2010-04</td>
<td><a href="http://www.google.com/phone/">Nexus One</a></td>
<td>Android 2.2 froyo</td></tr>
<tr><td>2010-06</td>
<td><a href="http://mb.softbank.jp/mb/iphone/">iPhone4</a></td>
<td>iOS 4</td></tr>
</table>
<p>
WorkPad 30J から HYBRID W-ZERO3 に至るまで、
全て PC とのデータ同期が基本だったし、
それぞれデータ移行ツールが用意されていたので移行は容易だった。
ところが Nexus One で同じような同期を行なうには、
データを PC ではなく 
<a href="http://www.google.com/calendar/render">Google Calendar</a> 
へ置かなければならない。
</p>
<p>
もちろん、 Google Calendar 
は共有設定さえ行なわなければ他人に読まれることはないだろうし、
「don't be evil」
と言ってるくらいだから、
Google が勝手にユーザのデータを活用する可能性も無い (と信じたい)。
</p>
<p>
だからといって、
個人的なデータや会社の超機密事項 (議事メモにはそういった情報も含まれる)
も洗いざらい Google に預けてしまう、
なんてことは小心な私にはとてもできない。
よく知られているように Google Calendar は
「<a href="http://www.google.com/support/calendar/bin/answer.py?hl=jp&amp;answer=37536">限定公開 URL</a>」
が漏れるだけで一巻の終わりであるわけで、
漏れることを前提でリスク評価すべき。
</p>
<p>
というわけで、
議事メモや日記を Google Calendar に置くことはハナからあきらめて、
Google Calendar には直近の予定だけを置くことにした。
万一漏れても、向こう一週間くらいの予定だけであれば、
致命的というほどでもない。
</p>
<p>
ところが驚いたことに Google Calendar には過去の予定を一括削除する機能がない。
手作業でいちいち消していかない限り、
データは残り続けるようだ。
当たり障りのない 「予定」 でも積み重なればいろいろ見えてくることがあるわけで、
長年にわたって溜った予定データは脅威となりうる。
</p>
<p>
どうしてこんな超基本機能が無いのだろうと思いつつ google で検索してみると、
見つかるのは
「どうやったら過去のデータを (一括) 消去できるのか？」
という質問のページばかり。
過去データを削除したい、
というニーズは確実にありそうなのに、
なぜ Google は実装しようとしないのか？
そういえば gmail も過去のメールを溜め続けるのが基本だし、
Google Calendar に自動的に削除する機能がないのは意図的なのかもしれない。
</p>
<p>
無い機能は作ってしまえと、
<a href="http://code.google.com/intl/ja/apis/calendar/data/2.0/developers_guide.html">Google Calendar API</a> 
のドキュメントを眺めてみる。
API を叩くためのクライアントライブラリが用意されているようだ。
が、.NET とか Java とか Python とか PHP とか、
あまり気の進まない (^^;) 言語ばかりが並んでいる。
Perl は無いのかっと思って 
<a href="http://www.cpan.org/">CPAN</a> を検索したら、
Net::Google::Calendar があっさり見つかった。
</p>
<p>
マニュアル片手にテストプログラムを書いてみる:
</p>
<pre class="code">
use Net::Google::Calendar;
use DateTime;
my $cal = Net::Google::Calendar->new;
$cal->login('sengoku@gmail.com', 'xxxxxxxx');
for my $event ($cal->get_events('start-max' => DateTime->now
				- DateTime::Duration->new(days => 7))) {
    $cal->delete_entry($event);
}
</pre>
<p>
たったこれだけ。
最初に login($user, $pass) して、
get_events(%opts) で「予定」データを取り出して、
delete_entry($event) で削除。
get_events の引数で、
開始時刻が一週間以上過去の予定のみ取り出すよう指定している。
</p>
<p>
ちょっと書き足して、
ユーザID やパスワードを設定ファイルで設定できるようにしたスクリプト
<a href="http://www.gcd.org/sengoku/docs/gcal_remove">gcal_remove</a>
を作ってみた。
</p>
<pre class="terminal">
% gcal_remove -c /path/to/config.yaml -d 7
</pre>
<p>
などと実行すると、
7日間以上前の予定を削除する。
</p>
<p>
設定ファイル config.yaml は以下のような感じ: 
</p>
<pre class="code">
google_user: sengoku@gmail.com
google_passwd: xxxxxxxx
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/07/606/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linode から prgmr.com へ乗り換えてみた</title>
		<link>http://www.gcd.org/blog/2010/06/590/</link>
		<comments>http://www.gcd.org/blog/2010/06/590/#comments</comments>
		<pubDate>Thu, 17 Jun 2010 00:44:22 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=590</guid>
		<description><![CDATA[今年 2月から使っている Linode (VPS, 仮想サーバ) は、 月額 $19.95 の最小プラン Linode 360 だと RAM 360MB なので WordPress を動かす (Apache+PHP+MySQL) には若干足らない (稀に スラッシング 状態に陥って無反応になる)。 もちろん、 一つ上のプラン Linode 540 (RAM 540MB) にすればいいのであるが、 これだと月額 $29.95 でちょっと高い (私の感覚だと月額 $20 あたりに心理的な壁がある)。 prgmr.com ならば、 RAM 512MB, ディスク 12GB が月額 $12 (年一括払いなら $115.2) で済むので乗り換えてみた。 Linode のようなユーザフレンドリーな Web ユーザインタフェースは無いし、 時々新規申込みの受付を中止したりする (今も RAM 1024MB 以上のプランは受付けていない) [...]]]></description>
			<content:encoded><![CDATA[<p>
今年 
<a href="http://www.gcd.org/blog/2010/02/544/">2月から使っている Linode</a>
(VPS, 仮想サーバ) は、
月額 $19.95 の最小プラン Linode 360 だと RAM 360MB なので
<a href="http://www.gcd.org/blog/2010/01/494/">WordPress</a> 
を動かす
(Apache+PHP+MySQL)
には若干足らない (稀に 
<a href="http://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%A9%E3%83%83%E3%82%B7%E3%83%B3%E3%82%B0">スラッシング</a> 状態に陥って無反応になる)。
もちろん、
一つ上のプラン Linode 540 (RAM 540MB) にすればいいのであるが、
これだと月額 $29.95 でちょっと高い
(私の感覚だと月額 $20 あたりに心理的な壁がある)。
</p>
<p>
<a href="http://prgmr.com/xen/">prgmr.com</a> ならば、
RAM 512MB, ディスク 12GB が月額 $12 (年一括払いなら $115.2) 
で済むので乗り換えてみた。
Linode のようなユーザフレンドリーな Web ユーザインタフェースは無いし、
時々新規申込みの受付を中止したりする
(今も RAM 1024MB 以上のプランは受付けていない)
が、
逆に言えば
Web インタフェースが不要な人 (含む私) にとっては 
prgmr.com のシンプルさがむしろ望ましく、
レスキュー用のブートイメージなど必要最低限のものは揃っているので、
不便と感じることはない。
また、
いったん申込みが完了してしまえば受付の一時停止は関係ないわけで、
むしろサーバリソースに見合ったユーザ数しか受付けない姿勢は好感が持てる。
</p>
<p>
Web から Signup すると、
以下のようなメールが届く。
</p>
<pre class="code">
you ordered a xen vps, 512MiB ram, 12GiB Disk 80 GiB transfer Xen VPS,  $12/month username sengoku 

Before I can set you up, I need to know what distro you would like and an OpenSSH format public key (on a *NIX, run ssh-keygen  and send me either the id_dsa.pub or id_rsa.pub file in an attachment)    

on putty/windows, see

http://www.unixwiz.net/techtips/putty-openssh.html#keypair

and scroll down to the screen shot where it says 'openssh format public key for pasting into authorized_keys' - that is what I need.

thanks. 
</pre>
<p>
OpenSSH の公開鍵くらい Web から登録させればいいのにと思いつつ、
「I need to know what distro you would like」
などと聞いてくるということは多少の融通は聞くのかと思い、
パーティションの切り方をついでにリクエストしてみた (6/4 15:46)。
つまり、
<a href="http://www.gcd.org/blog/2007/09/129/">自分で作った Linux distro</a>
(配布していないから
「distribution」 と呼ぶのは正確性に欠けるのだが、
それなら何て呼んだらいいのだろう?)
をインストールしたいから、
最小のパーティション (例えば 2GB) に 
ubuntu をインストールしておいてもらえるか？
などとリクエストしてみた:
</p>
<pre class="code">
Hi,

At Fri, 04 Jun 2010 05:48:05 +0000,
support@prgmr.com wrote:

> Before I can set you up, I need to know what distro you would like and
> an OpenSSH format public key (on a *NIX, run ssh-keygen and send me
> either the id_dsa.pub or id_rsa.pub file in an attachment)

I'd like to use my own linux-based image that is running on
fremont.gcd.org (linode) now.  Is it possible to make two partitions
(plus swap partition) ?  One is for a normal ubuntu, and the other is
for my own image.  I'd like to switch these two OSs by pv-grub.

Please minimize the ubuntu partition in order to maximize my own image.
For example, 2GiB for ubuntu, 1GiB for swap, and 9GiB for me.

I've attached my public key id_dsa.pub at the end of this mail.


#9860.
http://www.gcd.org/sengoku/		Hiroaki Sengoku &lt;sengoku@gcd.org&gt;
</pre>
<p>
すると、
4日後の 6/8 09:03 に返事が来た。
リクエストはあっさり無視されて、
ディスク一杯に ubuntu をインストールした状態の VPS が提供されただけ。
個別対応しないのであれば、
自動化したほうがいいような。
</p>
<pre class="code">
Your vps is setup now, your host server is whetstone.prgmr.com.
Login with your username and ssh key and follow the instructions
at <a href="http://book.xen.prgmr.com/mediawiki/index.php/Quickstart">http://book.xen.prgmr.com/mediawiki/index.php/Quickstart</a>
For more information about repartitioning, see
<a href="http://book.xen.prgmr.com/mediawiki/index.php/Repartition">http://book.xen.prgmr.com/mediawiki/index.php/Repartition</a>
The default root password for the vps is password, and you
should receive a bill soon by email. If you have any questions
please email support@prgmr.com. Thanks very much.
Nick Schmalenberger
</pre>
<p>
言われた通り ssh でログインしてみる:
</p>
<pre class="terminal">
senri:/home/sengoku % ssh whetstone.prgmr.com
The authenticity of host 'whetstone.prgmr.com (65.49.73.105)' can't be established.
RSA key fingerprint is 97:52:07:d4:52:8d:c0:cc:13:c7:fb:5d:5d:1b:ab:b4.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'whetstone.prgmr.com,65.49.73.105' (RSA) to the list
of known hosts.
Name                                        ID   Mem VCPUs      State   Time(s)
sengoku                                    xxx   512     1     -b----     11.5

Options for sengoku
1. console
2. create/start
3. shutdown
4. destroy/hard shutdown
5. reboot
6. swap i386/amd64 bootloaders (pvgrub)
7. exit
press the number> 
</pre>
<p>
「6. swap i386/amd64 bootloaders (pvgrub)」
が目を引くが、
PV-GRUB は DomainU 側で実行されるので、
PV-GRUB にて 32bit/64bit の両方のカーネルを立ち上げることができず、
PV-GRUB を実行する前にどちらか一方に決めておく必要がある、
ということのようだ。
デフォルトは (残念なことに) 32bit になっていて、
セットアップしてもらった ubuntu も 32bit 版だった。
</p>
<p>
「1」
を入力して VPS のコンソールを表示させてみる:
</p>
<pre class="terminal">
press the number> 1

Ubuntu 10.04 LTS sengoku.xen.prgmr.com hvc0

sengoku.xen.prgmr.com login: root
Password:
Last login: Mon May 31 23:51:42 UTC 2010 on hvc0
Linux sengoku.xen.prgmr.com 2.6.32-22-generic-pae #33-Ubuntu SMP Wed Apr 28 14:57:29 UTC 2010 i686 GNU/Linux
Ubuntu 10.04 LTS

Welcome to Ubuntu!
 * Documentation:  https://help.ubuntu.com/
root@sengoku:~# df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/xvda1            11812024   1068124  10143876  10% /
none                    249488       136    249352   1% /dev
none                    253744         0    253744   0% /dev/shm
none                    253744        28    253716   1% /var/run
none                    253744         0    253744   0% /var/lock
none                    253744         0    253744   0% /lib/init/rw
none                  11812024   1068124  10143876  10% /var/lib/ureadahead/debugfs
root@sengoku:~# 
</pre>
<p>
前掲のメールに書いてあった通り、
ユーザ 「root」
パスワード 「password」
でログインできた。
初めてログインしたのに
「Last login: Mon May 31 23:51:42 UTC 2010 on hvc0」
などと出ているが、
この ubuntu イメージを作ったとき試しに root 
でログインしたのが残ってしまったのだと思われる。
新規ユーザのセットアップのたびに、
5/31 に作ったイメージを使いまわしているのだろう。
</p>
<p>
ディスク 12GB のプランのはずなのに 1GB ほど足りないのは 
swap パーティションがあるから？と思い、
fdisk で確認してみる:
</p>
<pre class="terminal">
root@sengoku:~# fdisk -l /dev/xvda

Disk /dev/xvda: 12.8 GB, 12884901888 bytes
255 heads, 63 sectors/track, 1566 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

    Device Boot      Start         End      Blocks   Id  System
/dev/xvda1               1        1494    12000523+  83  Linux
root@sengoku:~# 
</pre>
<p>
シリンダの最大値は 1566 なので、
72 (=1566-1494) シリンダが使われずに余っている。
試しにパーティションを割当ててみたら普通に使えた。
</p>
<p>
自前 distro 10GB, swap 1GB, ubuntu 1GB といった切り分けかたにするため、
レスキュー用のカーネル 「CentOS 5.5 rescue」 をブートしてみる:
</p>
<pre class="terminal">
    GNU GRUB  version 0.97  (65536K lower / 0K upper memory)

 +-------------------------------------------------------------------------+
 | user bootloader configuration                                           |  
 | CentOS 5.5 rescue (2.6.18-194.3.1.el5xen)                               |
 | CentOS 5.5 installer                                                    |
 | NetBSD 5.0.2 installer                                                  |
 | Ubuntu 10.04 LTS installer                                              |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                                                                         |  
 +-------------------------------------------------------------------------+
    Use the ^ and v keys to select which entry is highlighted.
    Press enter to boot the selected OS, 'e' to edit the
    commands before booting, or 'c' for a command-line.
</pre>
<p>
二行目の 
「CentOS 5.5 rescue (2.6.18-194.3.1.el5xen)」
を選択してブート。
root でログイン (パスワードは無し) して
fdisk を使ってパーティションを切り直した。
せっかくセットアップしてもらった Ubuntu も、
3分と使わずにお別れ。
</p>
<pre class="terminal">
CentOS release 5.5 (Final)
Kernel 2.6.18-194.3.1.el5xen on an x86_64

sengoku.xen.prgmr.com login: root
[root@sengoku ~]# 
	...
[root@sengoku ~]# fdisk -l /dev/xvda

Disk /dev/xvda: 12.8 GB, 12884901888 bytes
255 heads, 63 sectors/track, 1566 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

    Device Boot      Start         End      Blocks   Id  System
/dev/xvda1               1        1306    10490413+  83  Linux
/dev/xvda2            1307        1438     1060290   82  Linux swap / Solaris
/dev/xvda3            1439        1566     1028160   83  Linux
[root@sengoku ~]# mount /dev/xvda1 /mnt
[root@sengoku ~]# 
</pre>
<p>
私は個人で管理している Linux マシン 
(自宅と職場と VPS 合わせて十数台, 
もちろん商用サービスを行なっているサーバではない)
のディスクの内容を、
コマンド一発で同一に保てるようにしている
(つまりマスタで変更/追加/削除したファイルを各マシンへコピー/各マシンで削除)。
</p>
<p>
もちろん、
各マシンの性能や 32bit/64bit などの CPU 種別、
ディスクの容量に応じて違う部分もあるわけで、
この同期コマンドは同期先の各マシンに合わせてコピーしないファイルのリストを生成した上で 
rsync を実行している。
なので、
新しくマシンを増やすときもマスタ上で同期コマンドを実行するだけで、
インストールが完了するはずだが...
</p>
<span id="more-590"></span>
<pre class="terminal">
senri.gcd.org:/ # mirror -v -r /mnt 'core64[sengoku.xen.prgmr.com]'
BEGIN core64[sengoku.xen.prgmr.com] Tue Jun  8 13:59:14 JST 2010
type: core64
target: sengoku.xen.prgmr.com
rsync --relative -a --numeric-ids --delete -v /bin /etc /lib /sbin /boot /lib64 /usr32 sengoku.xen.prgmr.com:/mnt/
bash: rsync: command not found
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: remote command not found (code 127) at io.c(600) [sender=3.0.6]
senri.gcd.org:/ # 
</pre>
<p>
ありゃ、レスキュー用のディスクには rsync がインストールされていないのか...
</p>
<p>
レスキュー用のディスクは (当然) 書き込みができないので、
rsync をインストールするのも
(tmpfs をマウントすれば可能だが) 
面倒。
そこで先に /dev/xvda3 パーティション (1GB) に 
ubuntu をインストールすることにする。
幸い /dev/xvdf で CentOS, Debian, Ubuntu, NetBSD 
それぞれのディスクを tar+gzip で固めたもの (tar ball) が提供されているので、
64bit 版 ubuntu と思われる ubuntu64.tar.gz を /dev/xvda3 へ展開:
</p>
<pre class="terminal">
[root@sengoku ~]# mkdir /tmp/xvdf
[root@sengoku ~]# mount /dev/xvdf /tmp/xvdf
mount: block device /dev/xvdf is write-protected, mounting read-only
[root@sengoku ~]# ls -ln /tmp/xvdf
total 1295052
-rw-r--r-- 1 500 500 242130294 May 17 18:10 centos32.tar.gz
-rw-r--r-- 1 500 500 264319597 May 17 16:25 centos64.tar.gz
-rw-r--r-- 1   0   0 184512292 Jun  3 23:30 debian32.tar.gz
-rw-r--r-- 1   0   0 185773340 Jun  3 11:35 debian64.tar.gz
drwx------ 2   0   0     16384 May 24 04:08 lost+found
-rw-r--r-- 1 500 500   4999925 Feb 28 00:46 netbsd64.tar.gz
-rw-r--r-- 1   0   0 219621325 Jun  3 11:30 ubuntu32.tar.gz
-rw-r--r-- 1   0   0 223411583 Jun  3 11:30 ubuntu64.tar.gz
[root@sengoku ~]# mkdir /tmp/xvda3
[root@sengoku ~]# mount /dev/xvda3 /tmp/xvda3
[root@sengoku ~]# tar xzf /tmp/xvdf/ubuntu64.tar.gz -C /tmp/xvda3
[root@sengoku ~]# vi /tmp/xvda3/boot/grub/menu.lst
</pre>
<p>
この tar ball は /dev/xvda1 に展開することを想定しているらしく、
/boot/grub/menu.lst に (hd0,0) と書いてあるので全て (hd0,2) に置換。
/dev/xvda1 にも /boot/grub/menu.lst を置いて以下の行を追加し、
/dev/xvda3 の menu.lst も起動できるようにしておく。
</p>
<pre class="code">
title  Ubuntu
root (hd0,2)
configfile /boot/grub/menu.lst
</pre>
<p>
前述した 
「swap i386/amd64 bootloaders (pvgrub)」
を選択して PV-GRUB を 64bit に切り替えておいて、
このインストールしたばかりの Ubuntu を立ち上げる。
そして前述した mirror コマンドをマスタで実行すれば、
/dev/xvda1 への自前 distro のインストールが完了する。
</p>
<p>
もちろん、
ホスト名などの設定ファイルや /etc/fstab など各マシンで固有のファイル、
/var/log, /tmp などのディレクトリは
mirror コマンドではコピー対象外だが、
これらのファイルやディレクトリが存在しない場合は、
ブートスクリプトにおいて勝手に生成する仕掛けになっている。
</p>
<blockquote>
<a href="http://book.xen.prgmr.com/mediawiki/index.php/Using_a_kernel.org_kernel_under_xen">Using a kernel.org kernel under xen</a> に書かれているように、
Linux カーネルを Xen のゲスト OS として動かすには
Paravirtualized guest support (CONFIG_PARAVIRT_GUEST) 
を有効にしておく必要がある。<br />
また、xen_blkfront.ko (CONFIG_XEN_BLKDEV_FRONTEND) と 
xen_netfront.ko (CONFIG_XEN_NETDEV_FRONTEND) の両モジュールも必要。
もちろんカーネルに組み込んでおいてもいいが、
私は実サーバと仮想サーバの両方で同じカーネルを使いたいので、
モジュールにしている。
</blockquote>
<p>
これで /dev/xvda1 からブートすれば、
使いなれた自前 distro 環境が立ち上がるはず...
</p>
<pre class="terminal">
root (hd0,0)
 Filesystem type is ext2fs, partition type 0x83
kernel /boot/linuz-2.6.31.13-x86_64 root=/dev/xvda1
initrd /boot/initz-2.6.31.13-x86_64

ERROR Invalid kernel: xc_dom_probe_bzimage_kernel unable to LZMA decompress kernel

xc_dom_bzimageloader.c:393: panic: xc_dom_probe_bzimage_kernel unable to LZMA decompress kernel
ERROR Invalid kernel: xc_dom_find_loader: no loader found

xc_dom_core.c:517: panic: xc_dom_find_loader: no loader found
xc_dom_parse_image returned -1

Error 9: Unknown boot failure

Press any key to continue...
</pre>
<p>
うっ、
LZMA 圧縮したカーネル (CONFIG_KERNEL_LZMA) には対応していないのか。
Linode の場合は非圧縮のカーネルのみブートできたのだが、
prgmr.com の場合は gzip 圧縮したカーネル (CONFIG_KERNEL_GZIP) 
のみブートできるようだ。
VPS ごとに起動できるカーネルの種類が異なっていて面倒。
</p>
<p>
gzip 圧縮したカーネル 
「linuz-2.6.31.13-gz-x86_64」
を用意して再度挑戦:
</p>
<pre class="terminal">
root (hd0,0)
 Filesystem type is ext2fs, partition type 0x83
kernel /boot/linuz-2.6.31.13-gz-x86_64 root=/dev/xvda1
initrd /boot/initz-2.6.31.13-x86_64

close blk: backend at /local/domain/0/backend/vbd/xxx/51712
close blk: backend at /local/domain/0/backend/vbd/xxx/51776
close blk: backend at /local/domain/0/backend/vbd/xxx/51792
[    0.000000] Initializing cgroup subsys cpuset
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Linux version 2.6.31.13-x86_64 (sengoku@senri.gcd.org) (gcc version 4.3.4 (GCC) ) #1 SMP Tue Apr 13 17:48:44 JST 2010
[    0.000000] Command line: root=/dev/xvda1
	...
[    0.003333] installing Xen timer for CPU 0
[    0.003333] Calibrating delay loop (skipped), value calculated using timer frequency.. 4003.20 BogoMIPS (lpj=6669120)
	...

Welcome to Linux 2.6.31.13-x86_64.

none.gcd.org login:
</pre>
<p>
無事ブートした。
</p>
<p>
あとは Linode の時と同様に、
自宅サーバ <a href="http://gw.gcd.org/test">gw.gcd.org</a> 
との間にトンネルを設置したり、 
gw.gcd.org 上の MySQL から VPS 上の MySQL 
に対してレプリケーションを行なうようにしたり、
<a href="http://www.gcd.org/blog/2010/03/573/">MyDNS</a> を動かしたり、
ns4.gcd.org を 65.49.73.116 へ向けたり、
等々... 
</p>
<p>
最後に、 
<a href="http://www.gcd.org/test">www.gcd.org</a> の 
IP アドレスとして VPS の IP アドレス 65.49.73.116 を追加した。
</p>
<p>
すなわち、
いま読者の皆さんが読んでるこのページは、 
1/2 の確率で prgmr.com の VPS が送信したページということになる。
すでに一週間近くこの状態を続けているが、
Linode 360 のときよりはメモリに余裕がある:
</p>
<pre class="terminal">
prgmr:/home/sengoku % free
             total       used       free     shared    buffers     cached
Mem:        534796     501456      33340          0      35568     114452
-/+ buffers/cache:     351436     183360
Swap:      1060280        556    1059724
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/06/590/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>サイボウズ オフィス8 のカレンダーを iCalendar 形式に変換するスクリプトを書いてみた</title>
		<link>http://www.gcd.org/blog/2010/05/585/</link>
		<comments>http://www.gcd.org/blog/2010/05/585/#comments</comments>
		<pubDate>Wed, 26 May 2010 07:01:33 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[プログラミングと開発環境]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=585</guid>
		<description><![CDATA[私の職場ではグループウェアとしてサイボウズを利用している。 自分のスケジュールを iCalendar 形式で取得してみたくなった (Nexus One を買うとそういう気分になる ^^;) ので、 google で検索してみたところ次の二つのスクリプトが見つかった: サイボウズオフィス6のカレンダーをiCalendar形式に変換するスクリプト。 サイボウズofffice8と同期するツール暫定版 あいにく職場のサイボウズはオフィス8 なので、 オフィス8 に未対応の前者は使えない。 後者はオフィス8 用だが、 「暫定版」 と書いてある通りいろいろバグがある。 さくっと修正してみて一応それっぽく動かすことはできたのだが、 使い続けていくとなると一から書き直したほうがいいような気がしてきた (わたし的にはこの手のものを PHP スクリプトでは書きたくない) ので、 perl で書き直してみた。 この perl スクリプト cybozu8_ical を、 % cybozu8_ical --conf /path/to/config.yaml などと実行すると、 オフィス8 の 「月予定」 (月間スケジュール) ページをアクセスして、 iCalendar 形式に変換する。 向こう一週間以内の予定については 「予定の詳細」 ページもアクセスして、 「メモ」 および 「設備」 も取得する。 「月予定」 の全ての予定について [...]]]></description>
			<content:encoded><![CDATA[<p>
私の<a href="http://www.klab.jp/">職場</a>ではグループウェアとしてサイボウズを利用している。
自分のスケジュールを iCalendar 形式で取得してみたくなった
(Nexus One を買うとそういう気分になる ^^;) ので、
<a href="http://www.google.co.jp/search?q=iCalendar+cybozu">google で検索してみた</a>ところ次の二つのスクリプトが見つかった:
</p>
<ul>
<li><a href="http://code.google.com/p/ogawa/wiki/Cybozu2ICal?wl=ja">サイボウズオフィス6のカレンダーをiCalendar形式に変換するスクリプト。</a></li>
<li><a href="http://ameblo.jp/sitescope/entry-10260113074.html">サイボウズofffice8と同期するツール暫定版</a></li>
</ul>
<p>
あいにく職場のサイボウズはオフィス8 なので、
オフィス8 に未対応の前者は使えない。
後者はオフィス8 用だが、
「暫定版」
と書いてある通りいろいろバグがある。
さくっと修正してみて一応それっぽく動かすことはできたのだが、
使い続けていくとなると一から書き直したほうがいいような気がしてきた
(わたし的にはこの手のものを PHP スクリプトでは書きたくない) ので、
<a href="http://www.gcd.org/sengoku/docs/cybozu8_ical">perl で書き直してみた</a>。
</p>
<p>
この perl スクリプト <a href="http://www.gcd.org/sengoku/docs/cybozu8_ical">cybozu8_ical</a> を、
</p>
<pre class="terminal">
% cybozu8_ical --conf /path/to/config.yaml
</pre>
<p>
などと実行すると、
オフィス8 の 「月予定」 (月間スケジュール) ページをアクセスして、
iCalendar 形式に変換する。
向こう一週間以内の予定については 「予定の詳細」 ページもアクセスして、
「メモ」 および 「設備」 も取得する。
「月予定」 の全ての予定について 「予定の詳細」をアクセスすると、
オフィス8 サーバに負荷をかけすぎる懸念があったので、
このような仕様にしてみた。
</p>
<p>
設定ファイル config.yaml は以下のような感じ:
</p>
<pre class="code">
cybozu_url: http://intra.klab.org/cgi-bin/klcb/ag.cgi
calname: cybozu
userid: 73
password: xxxxxxxx
time_zone: Asia/Tokyo
input_encoding: shiftjis
output_file: /home/sengoku/tmp/cybozu.ics
</pre>
<p>
「cybozu_url」 はオフィス8 の URL を指定する
(もちろん intra.klab.org は KLab 社内LAN からしかアクセスできない)。
「userid」 と 「password」 
はスケジュールを取得したいユーザの ID とパスワード。
「output_file」 に指定したファイルへ 
iCalendar 形式のスケジュールを出力する。
「output_file」 を省略すると標準出力へ書き出す。
</p>
<p>
なお 
「--conf /path/to/config.yaml」 オプションを省略すると、
cybozu8_ical と同じディレクトリにある config.yaml が読み込まれる。
</p>
<p>
cybozu8_ical で生成した iCalendar 形式のファイルは、
とりあえず <a href="http://www.google.com/calendar/render?hl=ja">Google カレンダー</a> および
<a href="http://mozilla.jp/thunderbird/">Thunderbird</a> +
<a href="http://www.mozilla-japan.org/projects/calendar/lightning/">Lightning</a> で読み込めることは確認したが、
なにぶんまだ
<a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a> 
を真面目に読んでいないので、
不具合などあったらご指摘頂けると有難い。
</p>
<p>
私はオフィス8 の API を知らないので、
「月予定」 「予定の詳細」 のページを取得してきて scrape しているだけ。
「繰り返し予定」 は個々の予定として扱っている。
iCalendar の UID (各予定固有のID) が同じままだと 
2個目以降の予定が無視されてしまうので、
UID の末尾に通し番号をつけて互いに区別できるようにしている。
</p>
<p>
サイボウズのスケジュールは 「場所」 を登録できない。
その代わり 「施設」 として会議室を登録する。
ところが私の職場の場合 
「施設」 には会議室だけでなく 
「ビデオ会議システム」 などもあったりするので、
「施設」 に登録されているデータをそのまま 
iCalendar の LOCATION として使うわけにもいかない。
そこで 「施設」 に会議室の名称が登録されている場合のみ、
その会議室名を LOCATION として出力するようにしている。
</p>
<p>
iCalendar の LOCATION として出力すべき会議室名の一覧を、
cybozu8_ical スクリプトの始めの方で、
</p>
<pre class="code">
my @facility = (
	'20F 大会議室1', '20F 大会議室2', '20F 大会議室3',
	'20F 中会議室1', '20F 中会議室2',
	'20F 小会議室1', '20F 小会議室2',
	'20F 和室', '22F 社長室前MTGｽﾍﾟ-ｽ'
);
</pre>
<p>
などと定義している。
ここに列挙されていない会議室等は  「施設」 に登録されていても単に無視される。
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/05/585/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>米国のプリペイド SIM カードで Nexus One を使ってみた</title>
		<link>http://www.gcd.org/blog/2010/05/582/</link>
		<comments>http://www.gcd.org/blog/2010/05/582/#comments</comments>
		<pubDate>Mon, 03 May 2010 09:04:27 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[Hawaii]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=582</guid>
		<description><![CDATA[私は米国ではプリペイド SIM カードを購入して携帯電話を使っている。 AT&#38;T の Pay As You Go $1 Mobile to Mobile Plan は、 携帯電話同士の通話が 1日 1ドルで使い放題になるので便利。 一方データ通信は、 米国だとホテルをはじめ多くの場所で無線LAN が使えるので、 今までは必要性をあまり感じなかったのだが、 今回のハワイ滞在で Nexus One を入手したら、 携帯電話でもデータ通信をしてみたくなってしまった。 日本でも HTC Desire が発売された今となっては 「いまさら」 感もあるが、 SIM ロック フリーな Nexus One を入手する意味はあると思い、 渡米前に Web サイトで Nexus One を購入し (日本からは、 このサイトへアクセスできないと書いてあるページを見かけるが、 トップページ以外は普通にアクセスできるような...?)、 配送先として滞在する予定のホテルを指定しておいた (配送先に日本の住所は指定できない)。 ただし米国のホテルはテロを警戒して、 非滞在者宛の荷物の受け取りを (事前連絡しても) 拒否するそうなので、 購入手続きは出発直前に行なった。 [...]]]></description>
			<content:encoded><![CDATA[<p>
私は米国では<a href="http://www.gcd.org/blog/2009/04/173/">プリペイド SIM カードを購入</a>して携帯電話を使っている。
AT&amp;T の 
<a href="http://www.wireless.att.com/cell-phone-service/cell-phone-plans/pyg-cell-phone-plans.jsp">Pay As You Go</a>
<a href="http://www.wireless.att.com/cell-phone-service/go-phones/gophone-plan-details.jsp?q_sku=sku980122&amp;q_planCategory=cat20002">$1 Mobile to Mobile Plan</a> は、
携帯電話同士の通話が 1日 1ドルで使い放題になるので便利。
一方データ通信は、
米国だとホテルをはじめ多くの場所で無線LAN が使えるので、
今までは必要性をあまり感じなかったのだが、
今回のハワイ滞在で Nexus One を入手したら、
携帯電話でもデータ通信をしてみたくなってしまった。
</p>
<blockquote>
日本でも HTC Desire が発売された今となっては
「いまさら」 感もあるが、
SIM ロック フリーな Nexus One を入手する意味はあると思い、
渡米前に
<a href="https://www.google.com/phone/choose?hl=en&amp;gl=US&amp;s7e=">Web サイト</a>で Nexus One を購入し
(日本からは、
このサイトへアクセスできないと書いてあるページを見かけるが、
<a href="http://www.google.com/phone/">トップページ</a>以外は普通にアクセスできるような...?)、
配送先として滞在する予定のホテルを指定しておいた
(配送先に日本の住所は指定できない)。
ただし米国のホテルはテロを警戒して、
非滞在者宛の荷物の受け取りを
(事前連絡しても) 拒否するそうなので、
購入手続きは出発直前に行なった。<br />
日本を発つ日 (フライトは晩の 20:50) の早朝 4:57 <a href="http://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E6%A8%99%E6%BA%96%E6%99%82">JST</a>
(12:57 <a href="http://ja.wikipedia.org/wiki/%E5%A4%AA%E5%B9%B3%E6%B4%8B%E5%A4%8F%E6%99%82%E9%96%93">PDT</a>) に購入手続きを行なったら、
その翌日 0:02 JST (8:02 PDT) に発送され (FedEx overnight)、
ハワイ到着日の翌日昼間 13:30 <a href="http://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%AF%E3%82%A4%E3%83%BB%E3%82%A2%E3%83%AA%E3%83%A5%E3%83%BC%E3%82%B7%E3%83%A3%E3%83%B3%E6%A8%99%E6%BA%96%E6%99%82">HAST</a> にホテルに配達された
(日本時間でいうと購入日の 2日後の朝 8:30 JST)。
出発日の前日に購入手続きを行なっていれば到着日に受け取れたかもしれないが、
ホテルにチェックイン (12:00 HAST) する前に配達されるとマズイかも？
</blockquote>
<p>
ホテルのフロントで Nexus One の小包を受け取って、
SIM を入れて電源 ON したら、
勝手に 3G (W-CDMA) でデータ通信を始めてしまった (当たり前 ^^;)。
Pay As You Go (AT&amp;T のプリペイドSIM) は、
<a href="http://www.wireless.att.com/cell-phone-service/cell-phone-plans/prepaid-feature-packages.jsp">Data Package</a> を追加購入せずにデータ通信を行なうと、
1KB あたり 1セントも課金されるので一瞬で (例えば、わずか 10MB で約1万円)
パケ死してしまう。
慌てて無線LAN に切り替えたが既に 87セント課金されていた。
さらに、
位置情報を取得するために 1セント (基地局ID の取得?)、
無線LAN 経由だと google アカウントの同期が行なえなかった
(ホテルの LAN の問題?) ので 
3G でつなぎ直したら 29セントかかった。
この時点で残度数 $22.73 (SIM カード利用開始時は $25)。
</p>
<p>
<a href="http://www.paygonline.com/">Pay As You Go Online</a> で、
「Login」 ボタンの上にある
「<a href="https://www.paygonline.com/websc/ForgotPassword.do">Forgot your password?</a>」 をクリックすると
「Request Your Password」 ページに遷移するので、
AT&amp;T プリペイド SIM の携帯電話番号を入力して
「Send Password」 ボタンを押す。
すると、
</p>
<blockquote>
1111509064: S: Your account status has changed.<br />
ATT Free Msg: Your password has been changed to 8181.
</blockquote>
<p>
などといった SMS (<a href="http://ja.wikipedia.org/wiki/%E3%82%B7%E3%83%A7%E3%83%BC%E3%83%88%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9">ショートメッセージサービス</a>) が送られてくる。
このメッセージ中に書いてある password を使ってログインし、
任意の password に設定し直せばよい。
いったんログインできるようになれば、
米国内外を問わず Web 上で度数の追加 (refill) や追加パッケージ 
(feature packages) の購入が可能。
</p>
<p>
<a href="http://www.wireless.att.com/cell-phone-service/cell-phone-plans/prepaid-feature-packages.jsp">追加パッケージ</a>には、
Unlimited Talk &amp; Text ($60 で通話と SMS が 30日間使い放題),
Messaging Packages ($19.99 で SMS 無制限),
Data Packages (データ通信),
Night and Weekend Minutes Package 
($19.99 で通話と SMS が夜間・週末無制限) があって、
Data Package は、
1MB ($4.99) と 100MB ($19.99) のいずれかを選べる。
</p>
<p>
Nexus One をいじっていたらすぐ 100MB くらいはいくだろうと思い
100MB を購入した。
実際、
google map をナビ代わりに使う (海外旅行ではすごく便利!) と、
20MB くらいはすぐ使ってしまう。
また google アカウントとの自動同期を行なうと、
何もしなくても一日 1MB くらいは使ってしまうようだ。
あっというまに 100MB を使い切りそうなので、
Data Package 100MB を追加購入することにした。
</p>
<p>
追加パッケージは、
Credit/Debit カードで支払うこともできるが、
SIM カードの残度数で払うこともできる。
つまり、
いったん<a href="https://www.myprepaidrefill.com/ATT/AddFunds.aspx">度数を購入 (refill)</a> してから、
その度数で追加パッケージを購入するということができる。
なんでそんな回りくどいことを？と思うかも知れないが、
refill だと SIM カードの有効期限を延長できるという違いがある。
例えば $100 refill すると有効期限が 1年後になる
($25〜$75 だと 90日後、$15 だと 30日後)。
</p>
<blockquote>
有効期限は追加できないので、
例えば有効期限が 3ヶ月後の場合 (新規に SIM カードを購入した場合など) に、
$100 refill したからといって有効期限が 15ヶ月後
(=3ヶ月+1年) になるわけではなく、
refill した時点の 1年後が有効期限になる。
有効期限はもちろん短くはならない。
例えば $15 refill だと有効期限は 30日後だが、
refill 以前の有効期限が 3ヶ月後なら、
3ヶ月後のままで変わらない。<br />
一方、
refill 以前の度数も新しい期限まで有効期限が延長される。
つまり有効期限に達する前に refill し続ければ、
度数を失効させずに済む。
ただし一枚の SIM カードに溜められる度数は $500 が上限。
</blockquote>
<p>
というわけで、
最初に購入した Data Package 100MB の残りがわずかになった段階で、
$100 refill し、
その度数から $19.99 を使って Data Package 100MB を追加購入した。
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/05/582/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux サーバ同士をシリアルケーブルでつなぐには寡黙な getty が必要</title>
		<link>http://www.gcd.org/blog/2010/04/579/</link>
		<comments>http://www.gcd.org/blog/2010/04/579/#comments</comments>
		<pubDate>Mon, 19 Apr 2010 22:48:14 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=579</guid>
		<description><![CDATA[私の自宅 LAN (GCD) の対外ルータである 2台の Linux サーバ (senri と asao) は、 双方のシリアルポート (つまり DSUB9ピンの RS-232C) 同士がつながっている。 いまさら RS-232C ? と思う人が大半だと思うが、 最後の命綱としての安心感は測り知れない。 つまりサーバをリモートから (ネットワーク経由で ssh ログインして) いじっていると、 ちょっとしたミスで操作不能に陥ってしまうことがある。 例えば NIC を down してしまった、 iptables で DROP する設定にしてしまった、 sshd を殺してしまった、 等々。 あるいは、 リモートから再起動を行なう場合、 起動スクリプトのほんの些細な設定ミスで、 リモートからは操作不能になってしまう。 こんなとき、 2台のサーバのシリアルポート同士を RS-232C クロスケーブル (シリアルケーブル) で接続しておけば、 生きている方のサーバからシリアルケーブル経由でログインできて、 リモートからは操作不能に陥ったサーバのネットワーク設定を修復したり、 sshd を起動し直したり、 あるいは再起動の場合であれば、 GRUB [...]]]></description>
			<content:encoded><![CDATA[<p>
私の<a href="http://gw.gcd.org/Welcome.ja.html">自宅 LAN (GCD)</a> 
の対外ルータである 
2台の Linux サーバ 
(<a href="http://www.gcd.org/sengoku/machine.ja.html#SENRI">senri</a> と 
<a href="http://www.gcd.org/sengoku/machine.ja.html#ASAO">asao</a>) は、
双方のシリアルポート (つまり DSUB9ピンの 
<a href="http://ja.wikipedia.org/wiki/RS-232">RS-232C</a>) 
同士がつながっている。
いまさら RS-232C ? と思う人が大半だと思うが、
最後の命綱としての安心感は測り知れない。
</p>
<p>
つまりサーバをリモートから (ネットワーク経由で ssh ログインして) 
いじっていると、
ちょっとしたミスで操作不能に陥ってしまうことがある。
例えば NIC を down してしまった、
iptables で DROP する設定にしてしまった、
sshd を殺してしまった、
等々。
あるいは、
リモートから再起動を行なう場合、
起動スクリプトのほんの些細な設定ミスで、
リモートからは操作不能になってしまう。
</p>
<p>
こんなとき、
2台のサーバのシリアルポート同士を
RS-232C クロスケーブル (シリアルケーブル) で接続しておけば、
生きている方のサーバからシリアルケーブル経由でログインできて、
リモートからは操作不能に陥ったサーバのネットワーク設定を修復したり、
sshd を起動し直したり、
あるいは再起動の場合であれば、
<a href="http://www.gnu.org/software/grub/index.html">GRUB</a> 
を操作することができる
(<a href="http://www.gcd.org/blog/2008/03/151/">ML115</a> 
などであれば BIOS 設定までいじれる)。
</p>
<p>
<a href="http://www.klab.jp/dsas/">職場のサーバ群</a>は、
もちろん <a href="http://www.intel.com/design/servers/ipmi/">IPMI</a> を利用していて大抵の操作は 
IPMI でできてしまうのでシリアルケーブルの出番はあまりないのだが、
それでもやはり最後の命綱がある安心感は何ものにも代えがたい。
しかも数百円のケーブル一本で済むのだから、
掛けておくべき保険だろう。
</p>
<p>
シリアルケーブル経由でサーバにログインするには、
そのサーバの /dev/ttyS0 (シリアルポート COM1 の場合) で 
<a href="http://www.gcd.org/sengoku/docs/NikkeiLinux01-03.pdf">getty</a> 
を動かしておく必要がある。
例えば GCD の場合 <a href="http://mgetty.greenie.net/">mgetty</a> を使っているので、
/etc/inittab に
</p>
<pre class="code">
# Dialin lines
s0:25:respawn:/usr/sbin/mgetty ttyS0
</pre>
<p>
などと設定している。
シリアルケーブルでつながった 2台の Linux サーバ senri と asao
において、
asao から senri へシリアルケーブル経由でアクセスすると、
senri のシリアルコンソールが表示される:
</p>
<pre class="terminal">
asao:/home/sengoku % cu ttyS0
Connected.

Welcome to Linux 2.6.31.13-x86_64.


senri.gcd.org!login: root
otp-md5 299 se5047 ext
Response or Password:
Last login: (null) on /dev/ttyS0

senri.gcd.org:/root # tty
/dev/ttyS0
senri.gcd.org:/root # 
</pre>
<p>
たとえ senri のネットワーク機能が全滅していたとしても、
mgetty は /sbin/init から直接起動されるので影響を受けない。
シリアルコンソールから root でログインして復旧作業を行なうことができる。
逆に、
asao のネットワーク機能が全滅したときは、
senri へ ssh ログインした上で
asao へシリアルケーブル経由でアクセスすればよい。
ネットワーク機能が麻痺する可能性があるような危険な作業を
senri と asao の両方で同時に行なったりしないようにすれば、
何が起きてもどちらかは生きていることが保証できる。
</p>
<p>
さて、
両サーバで mgetty が動いているときは以上で問題無いのだが、
どちらかのサーバを再起動するときなど、
mgetty 以外のものが動くときは状況が変わってくる。
</p>
<span id="more-579"></span>
<p>
例えば asao を再起動すると、
まず <a href="http://www.gnu.org/software/grub/grub-2.en.html">GRUB2</a> が動く。
</p>
<blockquote>
以前は <a href="http://www.gnu.org/software/grub/grub-legacy.en.html">GRUB Legacy</a> を使っていたのだが、
ハードディスクを入れ替えたとき
(数年ごとに 2〜3倍の容量のハードディスクと交換している)
勢い余って 
root パーティションまで ext4 にしてしまい、
GRUB Legacy だと ext4 をサポートしていないことに気付き、
慌てて GRUB2 をインストールした。
</blockquote>
<p>
GRUB2 では、
/boot/grub/grub.cfg に
</p>
<pre class="code">
serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
terminal_input serial console
terminal_output serial console
</pre>
<p>
などと設定しておくことにより、
VGA コンソール (/dev/tty1) とシリアルコンソール (/dev/ttyS0) の両方で 
GRUB の操作 (つまり起動するパーティション/カーネルを選択する等) 
を行なうことができる。
つまり VGA コンソールで操作しているときも、
同じ画面がシリアルコンソールでも出力され、
途中からシリアルコンソールで操作することも可能。
</p>
<p>
GRUB Legacy では、
最初にキー入力があった側のコンソールでの操作が有効になる仕様だった。
最初に VGA コンソール (つまり PC に直接接続された PS/2 or USB キーボード) 
からキー入力を行なえば、
以後キーボードからの操作が有効になり、
最初にシリアルコンソールからキー入力を行なえば、
以後シリアルケーブル経由の操作が有効になる。
</p>
<p>
一見 GRUB2 の仕様のほうが便利のように見えるが、
二つの Linux サーバをシリアルケーブルでつないでいると、
GRUB2 の仕様は少し困る。
つまり以下のような GRUB2 
のメニュー画面が常にシリアルケーブルへも出力されてしまい、
それが相手サーバの mgetty への入力として扱われてしまうので、
mgetty が余計な動きをする。
</p>
<pre class="terminal">

                             GNU GRUB  version 1.97

 +--------------------------------------------------------------------------+
 |GNU/Linux 2.6.31.13-x86_64                                                |
 |GNU/Linux 2.6.31.13-i386                                                  |
 |GNU/Linux 2.6.31.12-x86_64                                                |
 |GNU/Linux 2.6.31.12-i386                                                  |
 |GNU/Linux 2.6.31.11-x86_64                                                |
 |GNU/Linux 2.6.31.11-i386                                                  |
 |GNU/Linux 2.6.30.9-x86_64                                                 |
 |GNU/Linux 2.6.30.9-i386                                                   |
 |GNU/Linux 2.6.27.34-x86_64                                                |
 |GNU/Linux 2.6.27.34-i386                                                  |
 |Memtest86+                                                                |
 +--------------------------------------------------------------------------+

      Use the ^ and v keys to select which entry is highlighted.
      Press enter to boot the selected OS, 'e' to edit the commands before
      booting or 'c' for a command-line.


</pre>
<p>
すなわち mgetty は何かキー入力があると、
前述したような 「Welcome to Linux ...」 
メッセージとログインプロンプト 「senri.gcd.org!login: 」 を出力する。
この出力がそのまま相手サーバの GRUB2 への入力として扱われてしまう。
</p>
<p>
つまり 「Welcome to Linux ...」 メッセージは前と後ろに
「改行コード」 が出力されるので、
GRUB2 へ改行が入力されてしまう
(改行コード以外のアルファベットが GRUB2 によってコマンドとして認識されると、
もっと面倒なことになる)。
すなわち VGA コンソールに表示された GRUB2 メニューにおいて、
どの OS を起動しようか選ぼうとする間もなく、
改行キーが (シリアルコンソールから) 
入力されたことになってしまってデフォルトの OS が勝手に起動してしまう。
</p>
<p>
一方、
GRUB Legacy の場合は、
何かキー入力があるまでシリアルケーブル側へは何も出力されない
(正確に言うと 「Press any key to continue.」 というメッセージのみ出力される)。
相手サーバの mgetty が何か出力すると破綻してしまうが、
以下のような簡単なパッチを GRUB Legacy にあてることにより、
「any key」 ではなく mgetty が出力しないコードを入力したときのみ
シリアルコンソールへ切り替えるようにできる。
以下のパッチでは ^G (コントロールキーを押しながら G)
をシリアルコンソールで入力したときのみ、
GRUB Legacy のメニュー画面がシリアルコンソールに表示されるようにしている。
</p>
<pre class="code">
diff -ur grub-0.97.org/stage2/builtins.c grub-0.97/stage2/builtins.c
--- grub-0.97.org/stage2/builtins.c	2005-02-16 06:58:23.000000000 +0900
+++ grub-0.97/stage2/builtins.c	2009-04-06 13:31:02.288106510 +0900
@@ -4208,10 +4208,13 @@
 		{
 		  if (term_table[i].checkkey () >= 0)
 		    {
-		      (void) term_table[i].getkey ();
-		      default_term = i;
-		      
-		      goto end;
+                      int term_key_code;
+                      term_key_code = term_table[i].getkey();
+		      if( ( (char)term_key_code ) == 7 )
+                      {
+		      	default_term = i;
+		      	goto end;
+                      }
 		    }
 		}
 	    }
@@ -4227,7 +4230,7 @@
 		    if (term_bitmap &amp; (1 &lt;&lt; i))
 		      {
 			current_term = term_table + i;
-			grub_printf ("\rPress any key to continue.\n");
+			grub_printf ("\rPress Ctrl+G key to continue.\n");
 		      }
 		  
 		  /* Restore CURRENT_TERM.  */
</pre>
<p>
GRUB2 でも同様のパッチを作ろうと思ったのだが、
GRUB2 は GRUB Legacy と違って、
シリアルと VGA を切り替える仕組みにはなっておらず、
GRUB Legacy の時のような一行パッチでは済みそうにない。
</p>
<p>
また、
GRUB2 はまだ開発途上ということもあり、
バージョンが上がるたびにパッチを作るのも大変そうである。
GRUB2 以外のブートローダに乗り換える可能性もあるかもしれない。
一方 mgetty は既に枯れたソフトウェアと言ってよい。
そこで mgetty にパッチをあてて、
mgetty が 「Welcome to Linux ...」 
メッセージとログインプロンプトを出力するのを抑制することにした。
mgetty が寡黙になりさえすれば、
相手サーバで GRUB2 だろうがその他のブートローダだろうが、
何が動いていても問題は起きない。
</p>
<p>
シリアルコンソールから ^G を入力したときのみ、
mgetty が 「Welcome to Linux ...」 
メッセージとログインプロンプトを出力し、
^G が入力されない限りは何も出力しないようにするパッチを作ってみた:
</p>
<pre class="code">
--- mgetty.c.org	2006-01-02 02:13:18.000000000 +0900
+++ mgetty.c	2010-04-17 14:20:54.149714734 +0900
@@ -706,6 +706,11 @@
 	       
 	    if ( c_bool(direct_line) )			/* no RING needed */
 	    {
+		char ch;
+		signal(SIGALRM, sig_goodbye);
+		alarm(ring_chat_timeout);
+		int r = mdm_read_byte(STDIN, &amp;ch);
+		if (r <= 0 || ch != 0x07) break;	/* ^G */
 		mg_get_ctty( STDIN, devname );		/* get controll.tty */
 		mgetty_state = St_get_login;
 		break;
</pre>
<p>
このパッチにより、
^G が入力された時
(ch != 0x07 が不成立) のみ状態 「St_get_login」 へ遷移する。
つまりシリアルコンソールへログインプロンプトを出力する。
^G 以外が入力されると、
上記パッチ中の if 文の条件が成立し、
状態変数 mgetty_state が更新されず、
状態 「St_wait_for_RINGs」 に留まる。
つまり何も出力されない。
</p>
<blockquote>
蛇足だが、
状態 「St_wait_for_RINGs」 というのは、
mgetty がモデムからの 「RING」 送信を待っている状態。
モデムに (公衆回線から) 着呼要求が届いたとき 
(いわゆる電話機が鳴ってる状態)、
モデムは RS-232C 経由で PC に対して 「RING」 を一定間隔で送信する。
mgetty は RING の回数を数えて指定回数を超えれば、 
「ATA」 コマンドをモデムに送信して着呼する (いわゆる受話器を取った状態)。
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/04/579/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>「無知の無知」への 3ステップ</title>
		<link>http://www.gcd.org/blog/2010/04/576/</link>
		<comments>http://www.gcd.org/blog/2010/04/576/#comments</comments>
		<pubDate>Fri, 02 Apr 2010 04:19:06 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[仙石浩明CTO の日記]]></category>
		<category><![CDATA[自己啓発]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=576</guid>
		<description><![CDATA[KLab(株)は、 2007年から新卒採用を行っています。 今年も例年通り、4月1日に入社式を行いました。 - o - みなさん、入社おめでとうございます。 社長のお話が終わり、みなさんは挨拶が終わって、 緊張もほぐれてきたのではないかと思います。 ここからは各役員の挨拶なのですが、 なぜか私だけは挨拶だけでなく (長くならない範囲で ^^;) 話もしろということになったらしいので、 話を捻り出してみます。 「考えることが大事」ということは社長のお話にもありましたし、 みなさんも既によく知っていることだと思います。 ただ、 知っていることと実際に実行できることとの間には越えがたい壁があることも事実で、 分かっちゃいるけどついつい考えなくなってしまう、 あるいは考えているつもりが、 いつのまにか深く考えることがなくなってしまうものなのでしょう。 そもそも自分がどのくらい考えているかなんて、 なかなか意識するのは難しいですよね。 入社時にはみんな等しく、 これからは「考える習慣」を身につけよう、 と決意を新たにしたはずなのに、 3年で大きな差がついてしまうのが現実です。 考える習慣を身につけた人がどんどん成長するのに対し、 考えることを習慣化し損なった人は停滞したままなので、 その後も差は広がる一方。 10年 20年もたつととんでもない差になってしまいます。 なぜこんなにも差がついてしまうのでしょうか？ ただやみくもに 「考えよう」 としても、 具体的に何をすればいいのかよく分かりませんよね？ なので逆に何をしてはいけないかについて今日はお話ししたいと思います。 今日からみっちり 2ヶ月間、 新人研修でいろいろなことを学んでいくみなさんにとって、 「何をしてはいけないか」 を押えておくことはきっと役に立つと思います。 私は仕事柄、 面接をする機会が多いのですが、 お会いする人の中には考える習慣が全くない人もいます。 ご本人にはそういう自覚が全く無く、 人並みには考えていると思っているようなのですが、 私から見ると正に 「人生オワタ」 状態で、 なぜそうなってしまったのだろうかと考えているうちに、 ついついその人の身の上を根掘り葉掘り聞いて人生相談モードへ入ってしまうので、 そういうことを繰り返すうちに、 [...]]]></description>
			<content:encoded><![CDATA[<p>
KLab(株)は、 2007年から新卒採用を行っています。<br />
今年も例年通り、4月1日に入社式を行いました。 
</p>
<div align="center">- o -</div>
<p>
みなさん、入社おめでとうございます。
社長のお話が終わり、みなさんは挨拶が終わって、
緊張もほぐれてきたのではないかと思います。 
ここからは各役員の挨拶なのですが、
なぜか私だけは挨拶だけでなく (長くならない範囲で ^^;) 
話もしろということになったらしいので、
話を捻り出してみます。
</p>
<p>
「考えることが大事」ということは社長のお話にもありましたし、
みなさんも既によく知っていることだと思います。
ただ、
知っていることと実際に実行できることとの間には越えがたい壁があることも事実で、
分かっちゃいるけどついつい考えなくなってしまう、
あるいは考えているつもりが、
いつのまにか深く考えることがなくなってしまうものなのでしょう。
そもそも自分がどのくらい考えているかなんて、
なかなか意識するのは難しいですよね。
</p>
<p>
入社時にはみんな等しく、
これからは「考える習慣」を身につけよう、
と決意を新たにしたはずなのに、
3年で大きな差がついてしまうのが現実です。
考える習慣を身につけた人がどんどん成長するのに対し、
考えることを習慣化し損なった人は停滞したままなので、
その後も差は広がる一方。
10年 20年もたつととんでもない差になってしまいます。
</p>
<p>
なぜこんなにも差がついてしまうのでしょうか？
ただやみくもに 「考えよう」 としても、
具体的に何をすればいいのかよく分かりませんよね？
なので逆に何をしてはいけないかについて今日はお話ししたいと思います。
今日からみっちり 2ヶ月間、
新人研修でいろいろなことを学んでいくみなさんにとって、
「何をしてはいけないか」
を押えておくことはきっと役に立つと思います。
</p>
<p>
私は仕事柄、
面接をする機会が多いのですが、
お会いする人の中には考える習慣が全くない人もいます。
ご本人にはそういう自覚が全く無く、
人並みには考えていると思っているようなのですが、
私から見ると正に 「人生オワタ」 状態で、
なぜそうなってしまったのだろうかと考えているうちに、
ついついその人の身の上を根掘り葉掘り聞いて人生相談モードへ入ってしまうので、
そういうことを繰り返すうちに、
だんだん原因が見えてきたように感じています。
</p>
<p>
考える習慣を持たない人に共通する問題点として
「無知の知」 ならぬ 「無知の無知」 があります。
自分が分かってないことを分かってない、
自分の浅慮では到底及ばない領域があることが全く想像できない、
いわば 「井の中の蛙」 状態ですね。
「無知の無知」 状態になってしまうと、
今の自分の考え方で満足してしまい、
今の自分の枠を越えて考えてみようという発想がでてきません。
当然、
考える習慣もなくなってしまうわけです。
なぜこうなってしまうのか？
次の 3つのステップを経て
「無知の無知」 状態に至るケースが多いように思います:
</p>
<ol>
<li>後で調べればいいや</li>
<li>何が分からないのか分からない</li>
<li>分かったつもり</li>
</ol>
<p>
「質問する前に考えろ」
「それくらい Google 検索などを使って調べろ」
「ググれカス」
などと言われるようになったのはいつのころからでしょうか？
確かに何でもかんでも 「教えて君」 では困りますが、
「すぐ質問すること」 ＝ 「悪いこと」
とする風潮はいかがなものかと思います。
こういう風潮が、
「聞くのは恥ずかしい」
「後で調べればいいや」
という意識を生んでいないでしょうか。
</p>
<blockquote>
ちなみに私は全く正反対で、
<a href="http://www.gcd.org/blog/2006/03/327/">「分からないと言える」 ＝ 「カッコイイ」</a>
と中学生の頃から思い込んでるので、
今でも堂々と 「分からない」 を連発しています。
もちろん、
「CTO がこんなことも知らないのか？」
と思われる (^^;) リスクも無いわけではないので、
TPO を考慮せざるを得ないこともあるのがちょっと残念です。
</blockquote>
<p>
確かに本当に後で調べるのならまだいいのですが大抵は忘れてしまいます。
仮に忘れずに後で調べて言葉の意味を知ったとしても時間は遡れません。
その場で意味を聞いていれば相手と有益な議論を展開できたのかもしれないのに、
その機会は永久に失われてしまうわけです。
</p>
<p>
そして何より怖いのは、
知らない言葉が出てきても何とも思わなくなってしまうことです。
ふつう、
相手の話の中に意味が取りにくい言葉があれば気持ち悪く感じ、
なんとかその気持ち悪さを解消しようと思うものだと思うのですが、
「後で調べればいいや」
と思ってるとだんだんこの 「気持ち悪さ」 が無くなってきます。
この 「不感症」 が最初のステップです。
</p>
<p>
そして一つの話の中に知らない言葉がいくつも出てきても、
何とも思わない状態になると次のステップが見えてきます。
知らない言葉が複数出てくれば、
当然話全体が分からなくなるし、
そもそも話自体に興味が持てなくなります。
こうなってしまうと、
「何が分からないか分からない」 状態に陥ります。
いわゆる 「質問したくても何を質問したらいいのか分からない」 状態ですね。
</p>
<p>
質問しようとしない人に、
「もっと質問しようよ」
と言うと返ってくる言葉が、この
「何を質問すればいいのか分からない」
です。
話を興味を持って聞くということが無くなってしまっているために、
相手が話していない部分を聞き出そうという意欲が失われてしまっています。
もちろん、
人の興味は十人十色ですから、
ある話題に興味が持てないというのは普通でしょう。
問題なのは、
どんな話であっても興味が持てないことにあります。
しかも本人的にはそれが普通な状態なので、
他人の話を興味を持って聞くということがどんな体験だったのか、
もう覚えていなかったりします。
この 「無関心・無気力」 が第2 のステップとなります。
</p>
<p>
この第2 のステップの怖いところは自覚症状があまり無いことにあります。
自分は 「無気力」 ではない、
と思う人が大半だとは思いますが、
どんな話を聞いても、
質問したいことが思い浮かばないようだと
「第2 のステップ」
がかなり進行中であるわけで要注意です。
なんとか気付いて危機感を持って欲しいですし、
私はことある毎に
「<a href="http://www.gcd.org/blog/2008/04/398/">質問しろ〜</a>」
と口を酸っぱくして言ってるのですが、
長年染み付いた習慣というのはなかなか克服できないものなのかも知れません。
</p>
<p>
第2 のステップは
「分からない」
ということ自体は自覚できているのでまだ救いがあるのですが、
この症状が進むと第3 のステップである
「分かったつもり」
に至ります。
「分からない」
という意識さえあれば何かのきっかけで
「分かろうとする意欲」
が芽生えるかもしれないのですが、
分かっていないこと自体が分からなくなると、
その可能性すら無くなってしまうので末期的です。
</p>
<p>
何を聞いても
「分かったつもり」
になってしまって、
表面的な理解だけで満足するようになってしまうと、
自分の考え方と相手の考え方が異なっていてもそれに気付かない、
ということが起り得ます。
つまり相手の話を自分の思考の枠内だけで理解してしまうわけです。
まさに
「無知の無知」
です。
</p>
<p>
と、
いうわけで今日からの研修では決して疑問点を残さないよう、
積極的に質問してください。
また、
「分かった」
と思っても本当に理解しているのか、
今一度自分を疑ってみてください。
</p>
<p>
例えば、
半年前に<a href="http://www.gcd.org/blog/2009/10/401/">内定式で渋滞の話をしました</a>。
忘れてしまった人は私のブログを参照してください。
社会人経験がまだ無いみなさんには、
あの話を完全に理解することはなかなか難しいのではないかと思いますが、
「分からない」
という感覚を持っていますか？
そして分からないとすればどこが分からないのか考え、
何を質問すれば自分の理解を深められるか是非考えてみてください。
</p>
<p>
まだまだ自分は
「分かっていない」
と自分を疑うことこそが、
考えを深めていくために必要なのだと思います。
KLab への入社が、
みなさんの人生において飛躍のきっかけとなることを期待します。
</p>]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/04/576/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>tinydns のゾーンを MyDNS へ反映させるスクリプトを書いてみた</title>
		<link>http://www.gcd.org/blog/2010/03/573/</link>
		<comments>http://www.gcd.org/blog/2010/03/573/#comments</comments>
		<pubDate>Mon, 08 Mar 2010 00:25:06 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[プログラミングと開発環境]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=573</guid>
		<description><![CDATA[MyDNS というのは MySQL (あるいは PostgreSQL) のレコードを、 そのままゾーンレコードとして扱えるネームサーバ。 私は MyDNS を使って実験的にダイナミックDNSサービスを (もちろん無償で) 提供している。 実験と言いつつ、 サービス開始以来 2年以上安定的に継続できているし、 先月から海外レンタルサーバを使って地域分散したので、 仮に私の自宅の回線が切れてもネームサーバが見えなくなることはない。 gcd.jp のマスタネームサーバである ns.gcd.jp と、 スレーヴネームサーバである ns2.gcd.jp (海外サーバ fremont.gcd.org の別名。 名前の通りカルフォルニア州フリーモントにある) とは、 MySQL のレプリケーションによってゾーンデータを同期させている。 したがってマスタ側での変更が即座にスレーヴ側に伝わる。 ちなみに、 ネームサーバ間の同期でよく利用されるゾーン転送 (AXFR) は、 ゾーンデータを丸ごと転送するので (ゾーンが大きくなってくると) 同期頻度を高くすることが難しく、 ダイナミックDNSサービスにはあまり向いていない。 MySQL のレプリケーションでネームサーバ間の同期が行なえてしまう MyDNS は、 ダイナミックDNSサービス向きと言える。 ところが gcd.org では (ダイナミックではない) 普通のネームサーバ ns1.gcd.org も運用していて、 これは tinydns を利用している。 せっかく海外にサーバ [...]]]></description>
			<content:encoded><![CDATA[<p>
<a href="http://mydns.bboy.net/">MyDNS</a> というのは MySQL 
(あるいは PostgreSQL) のレコードを、
そのままゾーンレコードとして扱えるネームサーバ。
私は MyDNS 
を使って実験的に<a href="http://www.gcd.org/blog/2007/08/124/">ダイナミックDNSサービス</a>を
(もちろん無償で) 提供している。
実験と言いつつ、
サービス開始以来 2年以上安定的に継続できているし、
先月から<a href="http://www.gcd.org/blog/2010/02/544/">海外レンタルサーバ</a>を使って地域分散したので、
仮に私の自宅の回線が切れてもネームサーバが見えなくなることはない。
</p>
<p>
gcd.jp のマスタネームサーバである ns.gcd.jp と、
スレーヴネームサーバである ns2.gcd.jp 
(海外サーバ fremont.gcd.org の別名。
名前の通りカルフォルニア州フリーモントにある) とは、
MySQL のレプリケーションによってゾーンデータを同期させている。
したがってマスタ側での変更が即座にスレーヴ側に伝わる。
</p>
<p>
ちなみに、
ネームサーバ間の同期でよく利用されるゾーン転送 
(AXFR) は、
ゾーンデータを丸ごと転送するので (ゾーンが大きくなってくると) 
同期頻度を高くすることが難しく、
ダイナミックDNSサービスにはあまり向いていない。
MySQL のレプリケーションでネームサーバ間の同期が行なえてしまう MyDNS は、
ダイナミックDNSサービス向きと言える。
</p>
<p>
ところが gcd.org では (ダイナミックではない) 
普通のネームサーバ ns1.gcd.org も運用していて、
これは
<a href="http://www.gcd.org/blog/2006/07/85/">tinydns</a> を利用している。
せっかく海外にサーバ fremont.gcd.org を借りたのだから、
tinydns で管理しているゾーンも fremont.gcd.org で引けるようにしたいが、
fremont に複数 IP アドレスを付与するのはお金がかかる (月額 $1 追加) ので、
fremont で MyDNS と tinydns の両方を走らせるわけにもいかない。
</p>
<blockquote>
私が借りてるレンタルサーバ (正確に言うと VPS) 
<a href="http://www.linode.com/?r=577c3721c763acfc298a4e4cc4acaf654b4dbdc8">Linode</a> は、
もともと DNS サービス (マスタ/スレーヴどちらでも、ドメインいくつでも) 
を無料で利用できるので、
DNS のためだけに 1IP 追加する気にはちょっとなれない。
</blockquote>
<p>
もちろん、
tinydns を止めてしまって全てのゾーンを MyDNS 
へ移行してしまうという解決策も無くはないのだが、
MyDNS に全面的に依存してしまうのは恐い気もする。
できれば tinydns 側はいじらずに、
tinydns のゾーンデータを MyDNS 側へ反映させる仕掛けを作りたい。
</p>
<p>
というわけで、
tinydns のゾーンデータを読み込み、
MyDNS のゾーンデータの形式で MySQL へレコードを書込む
<a href="http://sourceforge.jp/cvs/view/stone/dns/tinydns2mydns?revision=1.1">perl スクリプト tinydns2mydns</a>
を書いてみた
(<a href="http://sourceforge.jp/cvs/view/stone/dns/tinydns2mydns?view=log">CVSリポジトリ</a>)。
</p>
<p>
なお MyDNS には、
<a href="http://mydns.bboy.net/doc/html/mydns_26.html#SEC26">mydnsimport</a> という外部からゾーンデータを取り込むツールが付属していて、
tinydns のゾーンデータもインポートできるのだが、
以下の欠陥があってゾーン転送目的には使えない:
</p>
<ol>
<li>
ゾーンの全レコードをいったん削除した上でインポートを行なう実装になっている。
</li>
<li>
SRV レコードをインポートできない。
</li>
</ol>
<span id="more-573"></span>
<p>
1. tinydns 側で新しいレコードを追加したとき、
対応するレコードのみを DB に INSERT できる実装になっているかと思いきや、
ソースを確認してみると、
いったん全部 DELETE した後
tinydns のゾーンデータを 1レコードずつ INSERT する実装になっていた。
</p>
<p>
つまり、
短い時間とはいえ、
ゾーンにレコードが存在しないタイミングが存在する。
したがって、
その瞬間に届いたクエリに対して非存在 (NXDOMAIN) を回答してしまう。
mydnsimport のソースをいじって、
DELETE してから INSERT 完了まで読み込みをロックするようにすれば、
NXDOMAIN を返すこと自体は回避可能だが、
ネームサーバとしての性能を大いに損ねてしまう。
</p>
<p>
2. tinydns には 
A, AAAA, NS, CNAME, PTR, MX, TXT
以外の任意のタイプのレコードを定義する方法として、
タイプを数値で指定する汎用記法がある:
</p>
<blockquote>
:<i>fqdn</i>:<i>n</i>:<i>rdata</i>:<i>ttl</i>:<i>timestamp</i>:<i>lo</i><br />
<br />
Generic record for <tt><i>fqdn</i></tt>.
<tt>tinydns-data</tt> creates a record of type <tt><i>n</i></tt>
for <tt><i>fqdn</i></tt>
showing <tt><i>rdata</i></tt>.
<tt><i>n</i></tt> must be an integer between 1 and 65535;
it must not be
2 (NS), 5 (CNAME), 6 (SOA), 12 (PTR), 15 (MX), or 252 (AXFR).
The proper format of <tt><i>rdata</i></tt> depends on <tt><i>n</i></tt>.
You may use octal <tt>\<i>nnn</i></tt> codes
to include arbitrary bytes inside <tt><i>rdata</i></tt>.
<div align="right"><a href="http://cr.yp.to/djbdns/tinydns-data.html">The tinydns-data program</a> から引用</div>
</blockquote>
<p>
この記法を利用して、
例えば以下のように
<a href="http://www.ietf.org/rfc/rfc2782.txt">SRV レコード</a>を定義できる。
</p>
<pre class="code">
:_jabber._tcp.jabber.jp:33:\000\012\000d\024\225\006jabber\002jp\000:300
</pre>
<p>
つまり
<tt><i>fqdn</i></tt> が 「_jabber._tcp.jabber.jp」、
<tt><i>n</i></tt> が 「33」 で SRV の意味。
<tt><i>rdata</i></tt> はタイプによってデータの意味が変わってくるが、
SRV の場合は次のような構造になる:
</p>
<pre class="fig">
┌─┬─┬─┬─┬─┬─┬─┬─≫┬─┬─┬─┬─≫─┬─┬─┐
│優先度│ 重み │ポート│長│ホスト名│長│ドメイン名……│０│
└─┴─┴─┴─┴─┴─┴─┴─≪┴─┴─┴─┴─≪─┴─┴─┘
</pre>
<p>
つまり先頭から 2バイトずつ 3つの数値 (ネットワークバイトオーダ) を表わし、
複数個の文字列 (先頭に 1バイトの文字列長) が続いて最後に 0。
前述の SRV レコードの場合であれば、
優先度は 「\000\012」 つまり 8進数で 000 012 だから 10進数だと 10 になる。
以下同様に重みが 100 で、
ポート番号が 5269 で、
続く文字列が 「jabber」 と 「jp」 でホスト名 jabber.jp を意味する。
</p>
<pre class="fig">
　優先度　 重み 　ポート　ホスト名
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│00　0A│00　64│14　95│06│ｊ│ａ│ｂ│ｂ│ｅ│ｒ│02│ｊ│ｐ│00│
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
　　10　　 100　　 5269 　　　　　6 文字　　　　　　　　　2 文字
</pre>
<p>
ゾーンファイルの書式で書けば次のようなレコードになる:
</p>
<pre class="code">
_jabber._tcp.jabber.jp.	300	IN	SRV	10 100 5269 jabber.jp.
</pre>
<p>
つまり、
ドメイン <a href="http://www.jabber.jp/">jabber.jp</a> における 
TCP/IP の jabber サービスは、
ホスト jabber.jp の 5269 番ポートにアクセスすればよい、
ということを表わす。
同じサービスを提供するホスト・ポートが複数ある場合、
優先度の数値が (アクセス可能なものの中で) 
最小のものに接続することが求められる (MUST)。
同じ優先度のホスト・ポートが複数ある場合は、
重みの比率に応じてアクセスを振り分けるべき (SHOULD)。
</p>
<p>
tinydns2mydns スクリプトにおいて 
SRV レコードを変換する部分は次のようになっている:
</p>
<pre class="code">
    # :fqdn:n:rdata:ttl:timestamp:lo
    elsif ($top eq ':') {
	my ($fqdn, $n, $rdata, $ttl, $timestamp, $lo) = @_;
	my $domain = &amp;get_domain($fqdn);
	next unless defined $domain;
	my $name = &amp;get_host($fqdn, $domain);
	if ($n == 33) {
	    $rdata =~ s/\\(\d\d\d)/sprintf("%c", oct($1))/ge;
	    my ($priority, $weight, $port, @target)
		= unpack("nnn(C/a)*", $rdata);
	    &amp;record($sth_insert, $sth_update,
		    $Zones{$domain}, $name,
		    'SRV', "$weight $port " . join(".", @target),
		    $priority, $ttl, $lo);
	}
    }
</pre>
<p>
眺めるだけで処理が見えてくる見通しのよいスクリプトだと思う (自画自賛 ;)。
$<tt><i>n</i></tt> が 33 のとき SRV レコードだから
続く $<tt><i>rdata</i></tt> を読み込んで、
8進数表記 <tt>\<i>nnn</i></tt> をバイトデータへ変換し、
「優先度」 「重み」 「ポート」 および複数個の文字列に分解する:
</p>
<pre class="code">
$rdata =~ s/\\(\d\d\d)/sprintf("%c", oct($1))/ge;
my ($priority, $weight, $port, @target) = unpack("nnn(C/a)*", $rdata);
</pre>
<p>
C で書けば何十行にもおよぶであろうこの処理をわずか 
2行で書けてしまう perl はスゴイと改めて思う。
あとは分解したデータを MyDNS のレコードの形式に組み立てるだけ:
</p>
<pre class="code">
$Zones{$domain}, $name, "$weight $port " . join(".", @target), $priority, $ttl
</pre>
<p>
mydnsimport のソースが C で 2000行近くもある 
(MyDNS ライブラリ群のソースを除く) のに対し、
tinydns2mydns はわずか 353行で書けてしまい、
しかも上記 mydnsimport にある欠陥を解決できている。
現状では tinydns の汎用記法のうち SRV しか対応していないが、
スクリプトの見通しがよいため他タイプに対応させるのも容易だろう。
</p>
<p>
tinydns2mydns スクリプトが DB に書込んだレコードは以下のようになる:
</p>
<pre class="terminal">
mysql> select * from rr where name='_jabber._tcp';
+-----+------+--------------+------+---------------------+-----+-----+
| id  | zone | name         | type | data                | aux | ttl |
+-----+------+--------------+------+---------------------+-----+-----+
| 817 |   13 | _jabber._tcp | SRV  | 100 5269 jabber.jp. |  10 | 300 |
+-----+------+--------------+------+---------------------+-----+-----+
1 row in set (0.00 sec)

</pre>
<p>
外部のサイトから jabber.jp の SRV レコードを問合わせてみると、
fremont も正しいレコードを返すことが確認できる:
</p>
<pre class="terminal">
% host -t srv _jabber._tcp.jabber.jp fremont.gcd.org
Using domain server:
Name: fremont.gcd.org
Address: 74.207.241.21#53
Aliases: 

_jabber._tcp.jabber.jp has SRV record 10 100 5269 jabber.jp.

% host -t srv _jabber._tcp.jabber.jp
_jabber._tcp.jabber.jp has SRV record 10 100 5269 jabber.jp.
</pre>]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/03/573/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>x86_64 Linux などの 64bit 環境で MD5 を使うときの注意点</title>
		<link>http://www.gcd.org/blog/2010/03/556/</link>
		<comments>http://www.gcd.org/blog/2010/03/556/#comments</comments>
		<pubDate>Mon, 01 Mar 2010 00:34:46 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[プログラミングと開発環境]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=556</guid>
		<description><![CDATA[MD5 (Message Digest Algorithm 5) は、 RFC 1321 でアルゴリズムが紹介されていて、 Appendix (付録) として C によるリファレンス実装が付属しているが、 その global.h に /* UINT4 defines a four byte word */ typedef unsigned long int UINT4; と書いてある。 すなわち 32bit 整数として UINT4 型を定義している。 x86_64 Linux を始め多くの 64bit Unix は LP64 すなわち long int (とポインタ) が 64bit な整数データモデルを採用している。 したがって UINT4 型の定義が 「unsigned long [...]]]></description>
			<content:encoded><![CDATA[<p>
MD5 (Message Digest Algorithm 5) は、
<a href="http://www.ietf.org/rfc/rfc1321.txt">RFC 1321</a>
でアルゴリズムが紹介されていて、
Appendix (付録) として C によるリファレンス実装が付属しているが、
その global.h に
</p>
<pre class="code">
/* UINT4 defines a four byte word */
typedef unsigned long int UINT4;
</pre>
<p>
と書いてある。
すなわち 32bit 整数として UINT4 型を定義している。
x86_64 Linux を始め多くの 64bit Unix は
<a href="http://www.unix.org/version2/whatsnew/lp64_wp.html">LP64</a> 
すなわち long int (とポインタ) が 64bit な整数データモデルを採用している。
したがって UINT4 型の定義が 「unsigned long int」 のままで、
この MD5 リファレンス実装を使ってしまうと、
32bit であるべき UINT4 型が 64bit になってしまい、
間違ったハッシュ値を算出してしまう。
</p>
<blockquote>
16bit CPU が主流だった大昔なら
「int が 16bit なデータモデルを採用している環境」 
が多かったのかもしれないが、
RFC 1321 が出た 1992年ごろは既に 32bit CPU が主流だったわけで、
UINT4 型を 「int」 と定義しておいてくれてもよかったのにと思う。
そうすれば、
「long が 64bit なデータモデルを採用している環境」
が多くなる昨今でも (int は 32bit のままなので) 
問題を起こさずに済んだだろうに。
</blockquote>
<p>
試しにテストプログラムを書いてみる:
</p>
<pre class="code">
#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;
#include &lt;string.h&gt;
#include "global.h"
#include "md5.h"
#define DIGEST_LEN 16
#define BUFFER_LEN 256

int main(int argc, char *argv[]) {
    MD5_CTX context;
    unsigned char digest[DIGEST_LEN];
    unsigned char buf[BUFFER_LEN];
    int i;
    MD5Init(&amp;context);
    while ((i=read(0, buf, BUFFER_LEN)) &gt; 0) MD5Update(&amp;context, buf, i);
    MD5Final(digest, &amp;context);
    for (i=0; i &lt; DIGEST_LEN; i++) printf("%02x", digest[i]);
    printf("\n");
    return 0;
}
</pre>
<p>
32bit 環境 (i686 Linux) では正しく動く:
</p>
<pre class="terminal">
senri:/home/sengoku/src/md5 % uname -m
i686
senri:/home/sengoku/src/md5 % ls
global.h  main.c  md5.h  md5c.c
senri:/home/sengoku/src/md5 % cc -Wall main.c md5c.c
senri:/home/sengoku/src/md5 % file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.0.0, dynamically linked (uses shared libs), not stripped
senri:/home/sengoku/src/md5 % echo "Hello, world" | ./a.out
a7966bf58e23583c9a5a4059383ff850
senri:/home/sengoku/src/md5 % echo "Hello, world" | openssl md5
a7966bf58e23583c9a5a4059383ff850
</pre>
<p>
ところが、
64bit 環境 (x86_64 Linux) だと:
</p>
<pre class="terminal">
senri:/home/sengoku/src/md5 % uname -m
x86_64
senri:/home/sengoku/src/md5 % cc -Wall main.c md5c.c
senri:/home/sengoku/src/md5 % file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), for GNU/Linux 2.4.0, dynamically linked (uses shared libs), not stripped
senri:/home/sengoku/src/md5 % echo "Hello, world" | ./a.out
fd578222c6a471623ea1e3eb2b6e6f6b
</pre>
<p>
などと、
誤った MD5 の値が出力されてしまう。
</p>
<p>
MD5 の値を求めること自体が目的であれば、
誤ったハッシュ値が出力されればすぐ気付くのでいいのだが、
値そのものが目的であることは (当然ながら) あまりなくて、
普通はアプリケーションの中で MD5 を利用するので、
32bit 環境で使っていたアプリケーションを
64bit 環境でコンパイルし直して使おうとするとハマる。
</p>
<span id="more-556"></span>
<p>
私は qmapop-0.3 (Bert Gijsbers 氏が開発した、qmail 用 APOP 認証パッケージ。
とうの昔に obsolete になってる ^^;)
を x86_64 Linux でコンパイルし直したら
APOP 認証が通らなくなってしまって焦った。
</p>
<p>
なにぶん古いプログラム (1995年5月リリース) に
いろいろ手を加えて使っていたので、
APOP 認証が失敗するのは必要なパッチをあてていないためだろうと思ってしまった。
しかし最後にコンパイルしたのは何年も前 (2002年4月) なので、
どんな修正を行ったか思い出せない。
</p>
<p>
仕方ないのでソースをながめて原因を探る羽目になり、
動作させつつ処理を追っていたら、
何のことはない単に算出した MD5 の値が誤ってるだけということに気付き、
(qmapop-0.3 が使ってた) 
MD5 リファレンス実装がデータモデルに依存していることを見つけた次第。
</p>
<p>
<a href="http://en.wikipedia.org/wiki/C99">C99</a> で 
<a href="http://en.wikipedia.org/wiki/Stdint.h">stdint.h</a>
が定義されて、
整数データモデルに依存しない、
移植性の高い書き方が可能になった。
以下のように global.h を修正すればよい:
</p>
<pre class="code">
senri:/home/sengoku/src/md5 % diff -ub global.h~ global.h
--- global.h~	1993-05-14 02:15:17.000000000 +0900
+++ global.h	2010-02-27 23:49:36.553633806 +0900
@@ -13,11 +13,13 @@
 /* POINTER defines a generic pointer type */
 typedef unsigned char *POINTER;
 
+#include &lt;stdint.h&gt;
+
 /* UINT2 defines a two byte word */
-typedef unsigned short int UINT2;
+typedef uint16_t UINT2;
 
 /* UINT4 defines a four byte word */
-typedef unsigned long int UINT4;
+typedef uint32_t UINT4;
 
 /* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
 If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
</pre>]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/03/556/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>本ブログのサーバを海外レンタルサーバ Linode を使って冗長化・地域分散してみた</title>
		<link>http://www.gcd.org/blog/2010/02/544/</link>
		<comments>http://www.gcd.org/blog/2010/02/544/#comments</comments>
		<pubDate>Wed, 24 Feb 2010 23:58:39 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=544</guid>
		<description><![CDATA[前回、 前々回に書いたように、 このブログ 「仙石浩明の日記」 (および 「仙石浩明CTO の日記」) は、 私の自宅にある PC サーバで動かしている。 2台の PC からなる冗長構成になっていて、 10年以上の安定稼働実績がある (両サーバ共に停止したことは皆無) が、 回線が (家庭用の) フレッツ光ネクストなので、 インターネットとの接続が切れてしまう可能性は皆無ではない。 実際、 Bフレッツからフレッツ光ネクストへ切り替えたときは 40分間ほど切れてしまった。 一応バックアップ回線として ADSL 回線も契約しているが、 こちらは固定 IP アドレスを割当てていないので WWW サービス用としては使いにくい。 短時間とはいえブログが見えないようなことがあると、 たまたまそのタイミングで (検索エンジン等で見つけて) 訪れた人が、 ブログが既に閉鎖してしまったものと勘違いしてしまうかもしれない。 ここは一つ自宅以外の場所にもサーバを立てて、 回線断の影響を受けないようにしたいところ。 海外のサーバなら地域分散もできてなおよい。 道楽で運営している自宅サーバにそこまで可用性を求めるのは、 やりすぎの感もあるが、 近頃流行りの VPS (Virtual Private Server, 仮想専有サーバ) は、 月額 $20 (初期費用無し) 程度で利用できるらしい。 $20 なら試しに使ってみるのも悪くない。 [...]]]></description>
			<content:encoded><![CDATA[<p>
<a href="http://www.gcd.org/blog/2010/02/540/">前回</a>、
<a href="http://www.gcd.org/blog/2010/01/494/">前々回</a>に書いたように、
このブログ 「仙石浩明の日記」 
(および 
「<a href="http://sengoku.blog.klab.org/">仙石浩明CTO の日記</a>」) は、
私の自宅にある <a href="http://gw.gcd.org/">PC サーバ</a>で動かしている。
2台の PC からなる冗長構成になっていて、
10年以上の安定稼働実績がある 
(両サーバ共に停止したことは皆無)
が、
回線が (家庭用の) フレッツ光ネクストなので、
インターネットとの接続が切れてしまう可能性は皆無ではない。
実際、
<a href="http://www.gcd.org/blog/2009/11/180/">Bフレッツからフレッツ光ネクストへ切り替えた</a>ときは 40分間ほど切れてしまった。
一応<a href="http://www.gcd.org/blog/2006/11/105/">バックアップ回線</a>として 
ADSL 回線も契約しているが、
こちらは固定 IP アドレスを割当てていないので WWW サービス用としては使いにくい。
</p>
<p>
短時間とはいえブログが見えないようなことがあると、
たまたまそのタイミングで (検索エンジン等で見つけて) 訪れた人が、
ブログが既に閉鎖してしまったものと勘違いしてしまうかもしれない。
ここは一つ自宅以外の場所にもサーバを立てて、
回線断の影響を受けないようにしたいところ。
海外のサーバなら地域分散もできてなおよい。
</p>
<p>
道楽で運営している自宅サーバにそこまで可用性を求めるのは、
やりすぎの感もあるが、
近頃流行りの VPS (Virtual Private Server, 仮想専有サーバ) は、
月額 $20 (初期費用無し) 程度で利用できるらしい。
$20 なら試しに使ってみるのも悪くない。
</p>
<table class="ruler">
<tr><th>サービス<br />プラン名</th>
<th>メモリ<br />ディスク</th><th>帯域<br />追加額/単位</th>
<th>月額<br />年額</th><th>データセンタの<br />場所</th></tr>
<tr><th><a href="http://serveraxis.com/vps.php">ServerAxis</a> Xen<br />
VS000.5G-0025.0GP</th>
<td>512MB<br />25GB</td><td>0GB<br />$.05/GB</td><td>$15<br />$180</td>
<td><a href="http://serveraxis.com/inf.php">Chicago</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%AA%E3%83%8E%E3%82%A4%E5%B7%9E">IL</a></td></tr>
<tr><th><a href="http://www.linode.com/?r=577c3721c763acfc298a4e4cc4acaf654b4dbdc8">Linode</a><br />
Xen 360</th>
<td>360MB<br />16GB</td><td>200GB<br />$10/100GB</td><td>$19.95<br />$215.46</td>
<td><a href="http://www.linode.com/wiki/index.php/Network#Fremont.2C_California">Fremont</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a><br />
<a href="http://www.linode.com/wiki/index.php/Network#Dallas.2C_Texas">Dallas</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%86%E3%82%AD%E3%82%B5%E3%82%B9%E5%B7%9E">TX</a> <a href="http://www.linode.com/wiki/index.php/Network">...</a></tr>
<tr><th><a href="http://vpslink.com/xen-vps/">VPSLink</a><br />
Xen VPS Link-3</th>
<td>256MB<br />10GB</td><td>300GB<br />$.50/GB</td><td>$19.95<br />$201.12</td>
<td><a href="http://www.spry.com/company/datacenter/">Seattle</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%AF%E3%82%B7%E3%83%B3%E3%83%88%E3%83%B3%E5%B7%9E">WA</a><br />
New York, <a href="http://ja.wikipedia.org/wiki/%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A8%E3%83%BC%E3%82%AF%E5%B7%9E">NY</a></td></tr>
<tr><th><a href="http://www.slicehost.com/">slicehost</a><br />
Xen VPS 256 slice
</th>
<td>256MB<br />10GB</td><td>150GB<br />$.30/GB</td><td>$20<br />$216</td>
<td><a href="http://www.slicehost.com/questions/#datacenters">Dallas</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%86%E3%82%AD%E3%82%B5%E3%82%B9%E5%B7%9E">TX</a><br />
St. Louis, <a href="http://ja.wikipedia.org/wiki/%E3%83%9F%E3%82%BA%E3%83%BC%E3%83%AA%E5%B7%9E">MO</a></td></tr>
</table>
<p>
VPSLink 以外は最安のプランで比較した。
VPSLink には、
メモリ 64MB で月額 $7.95 の Link-1 プラン、
メモリ 128MB で月額 $13.95 の Link-2 プランもあるが、
256MB 未満のメモリではできることも限られてしまうので比較対象から外した。
</p>
<p>
月額基本料だけを比較すると、
ServerAxis が格安に見えるが、
これは帯域課金が含まれていないためで、
例えば 100GB (送受信の合計バイト数) 使うと $5 加算されて $20 と他社並になる。
</p>
<p>
データセンタの場所 (というかネットワーク上での位置) 
によって遅延時間が変わってくるので、
どこのデータセンタを使っているかも重要。
例えば同じ Linode でもデータセンタによって、
日本からアクセスしたときの
RTT (Round Trip Time, 往復にかかる時間, ミリ秒) が倍以上変わってくる。
それぞれ 10回ずつ ping を打って、
RTT の最小/平均/最大を比較してみる:
</p>
<table class="ruler">
<tr><th>データセンタの場所</th><th>ホスト名</th>
<th>最小</th><th>平均</th><th>最大</th></tr>
<tr><td>Fremont, California</td><td>fremont1.linode.com</td>
<td>121.341</td><td>132.299</td><td>146.939</td></tr>
<tr><td>Dallas, Texas</td><td>dallas1.linode.com</td>
<td>171.049</td><td>173.152</td><td>174.905</td></tr>
<tr><td>Atlanta, Georgia</td><td>atlanta1.linode.com</td>
<td>183.184</td><td>185.938</td><td>190.174</td></tr>
<tr><td>Newark, New Jersey</td><td>newark1.linode.com</td>
<td>200.629</td><td>203.053</td><td>204.755</td></tr>
<tr><td>London, England</td><td>london1.linode.com</td>
<td>269.324</td><td>273.499</td><td>276.822</td></tr>
</table>
<p>
米国西海岸だと RTT は 130msec 程度で済むが、
東海岸は 200msec を超えてしまい、
英国はもっと遠い。
西海岸にデータセンタがあるのは Linode と VPSLink だが、
<a href="http://www.linode.com/wiki/index.php/Network">Linode がデータセンタの情報を詳細に公開している</a>のに対し、
VPSLink は、
<a href="http://www.spry.com/">Spry Hosting</a> のシアトルのデータセンタを使用している、
ということ<a href="http://vpslink.com/about/network-datacenter/pictures/">以上の情報を公開していない</a> 
(しかもこのページは 2006年8月の内容のまま)
のが少し気になる。
</p>
<p>
サポートしている Linux ディストリビューションは、
各社だいたい共通で、
CentOS, Debian, Gentoo, Ubuntu の各バージョンが利用できるが、
<a href="http://vpslink.com/ubuntu-vps/">VPSLink Ubuntu Plan のページ</a>に 
Ubuntu 9.10 Karmic が書かれていない点も、
ちょっと気になった
(ちゃんとポリシーを持って 9.10 をサポートしないのならいいのだが、
どうもそうではなく単に運用の手を抜いているだけのような気配がする)。
Web 界隈での評判も Linode のほうがよさげだったので、
Linode を使ってみることに決めた。
</p>
<p>
サポートしているディストリビューションのバージョンが古くてもアップグレードできるのだろうが、
帯域で課金されるので最初から新しいバージョンをインストールできる方が好ましい。
</p>
<table class="ruler">
<tr><th>サービス</th>
<th>CentOS</th><th>Debian</th><th>Gentoo</th><th>Ubuntu</th>
<th>Fedora</th><th>その他</th></tr>
<tr><th><a href="http://serveraxis.com/vps.php">ServerAxis</a></th>
<td>5.4</td><td>5.0</td><td>2008.0</td><td>9.10</td><td>-</td>
<td>Mandriva SUSE</td>
</tr>
<tr><th><a href="http://www.linode.com/?r=577c3721c763acfc298a4e4cc4acaf654b4dbdc8">Linode</a></th>
<td>5.3</td><td>5.0</td><td>2008.0</td><td>9.10</td><td>11</td>
<td>Arch SUSE Slack</td>
</tr>
<tr><th><a href="http://vpslink.com/xen-vps/">VPSLink</a></th>
<td>5</td><td>5</td><td>O</td><td>9.04</td><td>11</td>
<td>Arch Slack</td>
</tr>
<tr><th><a href="http://www.slicehost.com/">slicehost</a></th>
<td>5.4</td><td>5.0</td><td>2008.0</td><td>9.10</td><td>12</td>
<td>Arch RedHat</td>
</tr>
</table>
<p>
おそらく各社ともカーネルも入れ替えることができるのだと思うが、
Linode 以外は使っていないので未確認。
<a href="http://www.linode.com/?r=577c3721c763acfc298a4e4cc4acaf654b4dbdc8">Linode</a> の場合は pv_ops (paravirtualization) 
を有効にしておけば任意のカーネルを利用できる。
したがって (帯域課金さえ気にしなければ ;-) 
任意のディストリビューションの利用 (あるいは完全なカスタマイズ) が可能
(私はまだそこまではやっていない)。
</p>
<p>
なお、VPS サービスは探すと<a href="http://www.webhostingtalk.com/forumdisplay.php?f=104">いろいろある</a>ようだ。
前述した 4社と比べると公開している情報が不十分
(特に Virtuozzo/OpenVZ な VPS は実際のパフォーマンスを予想しにくく、
実地に使ってみないとなんとも...)
なので私は比較検討対象から外したが、
ダメ元で使ってみるのも面白いかもしれない:
</p>
<table class="ruler">
<tr><th>サービス<br />プラン名</th>
<th>メモリ<br />ディスク</th><th>帯域<br />追加額/単位</th>
<th>月額<br />年額</th><th>データセンタの<br />場所</th></tr>

<tr><th><a href="http://www.splitserv.com/portal/xen.htm">SplitServ</a><br />
Xen 1024</th>
<td>1024MB<br />15GB</td><td>200GBs<br />$8/400GB</td><td>$14.95<br />$149.50</td>
<td><a href="http://www.splitserv.com/portal/network.htm">Los Angeles</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a><br />Kansas City, <a href="http://ja.wikipedia.org/wiki/%E3%83%9F%E3%82%BA%E3%83%BC%E3%83%AA%E5%B7%9E">MO</a></td></tr>
<tr><th><a href="http://jp.webk.net/service/vps.html">WebKeepers</a><br />
Virtuozzo ライト</th>
<td>512MB<br />50GB</td><td>?</td><td align="right">1280円<br />11760円</td>
<td>?</td></tr>
<tr><th><a href="http://www.photonvps.com/xen.html">PhotonVPS</a><br />
Xen VPS WARP 1</th>
<td>512MB<br />35GB</td><td>500GB<br />?</td><td>$16.95<br />$203.40</td>
<td><a href="http://www.photonvps.com/networks.html">Los Angeles</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a></td></tr>
<tr><th><a href="http://knownhost.com/vps_packages.html">KnownHost</a><br />
Virtuozzo VS2</th>
<td>512MB<br />30GB</td><td>750GB<br />$40/100GB</td><td>$35<br />$400</td>
<td><a href="http://knownhost.com/network.html">Los Angeles</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a><br />
<a href="http://www.colo4dallas.com/">Dallas</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%86%E3%82%AD%E3%82%B5%E3%82%B9%E5%B7%9E">TX</a></td></tr>
<tr><th><a href="https://www.burst.net/linvps.shtml">BurstNet</a><br />
VPS PACKAGE #1</th>
<td>512MB<br />20GB</td><td>1000GB<br />$25/200GB</td><td>$5.95<br />$71.40</td>
<td><a href="https://www.burst.net/network.shtml">Scranton</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%9A%E3%83%B3%E3%82%B7%E3%83%AB%E3%83%99%E3%83%8B%E3%82%A2%E5%B7%9E">PA</a></td></tr>
<tr><th><a href="http://vpsnoc.com/Unmanaged_VPS.html">VPS NOC</a><br />
OpenVZ Bronze</th>
<td>512 MB<br />20 GB</td><td>400 GB<br />?</td><td>$12.95<br />$155.40</td>
<td><a href="http://vpsnoc.com/networkfacility.html">Kansas City</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%9F%E3%82%BA%E3%83%BC%E3%83%AA%E5%B7%9E">MO</a></td></tr>

<tr><th><a href="http://corenetworks.net/virtual/">CoreNetworks</a><br />CoreMR</th>
<td>512MB<br />12GB</td><td>500GB<br />?</td><td>$19.95<br />$239.40</td>
<td>?</td></tr>
<tr><th><a href="http://prgmr.com/xen/">prgmr.com</a><br />Xen VPS</th>
<td>512MB<br />12GB</td><td>80GB<br />?</td><td>$12<br />$115.20</td>
<td><a href="http://prgmr.com/san-jose-co-location.html">San Jose</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a></td></tr>
<tr><th><a href="http://hostcadet.com/vps.php">HostCadet</a><br />
VPS Micro</th>
<td>512MB<br />10GB</td><td>400GB<br />?</td><td>$9.99<br />$119.88</td>
<td><a href="http://hostcadet.com/network.php">Colorado Springs</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%AD%E3%83%A9%E3%83%89%E5%B7%9E">CO</a></td></tr>
<tr><th><a href="http://thenynoc.com/vps.html">NY NOC</a> OpenVZ<br />
Super VPS #1</th>
<td>512MB<br />10GB</td><td>1000GB<br />?</td><td>$10.00<br />$120</td>
<td><a href="http://thenynoc.com/network.html">Chicago</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%AA%E3%83%8E%E3%82%A4%E5%B7%9E">IL</a><br />North Bergen, <a href="http://ja.wikipedia.org/wiki/%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%BC%E3%82%B8%E3%83%BC%E5%B7%9E">NJ</a></td></tr>
<tr><th><a href="http://arpnetworks.com/vps">ARP Networks</a><br />
KVM/QEMU</th>
<td>512MB<br />10GB</td><td>100GB<br />?</td><td>$15<br />$180</td>
<td><a href="http://support.arpnetworks.com/faqs/main/ip-address-for-ping-tests">Los Angeles</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a></td></tr>

<tr><th><a href="http://virtuallydedicated.com/xen.html">VirtuallyDedicated</a><br />
Xen VX384</th>
<td>384MB<br />24GB</td><td>300GB<br />?</td><td>$10.99<br />$131.88</td>
<td><a href="http://virtuallydedicated.com/about.html">Chicago</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%AA%E3%83%8E%E3%82%A4%E5%B7%9E">IL</a><br />
Scranton, <a href="http://ja.wikipedia.org/wiki/%E3%83%9A%E3%83%B3%E3%82%B7%E3%83%AB%E3%83%99%E3%83%8B%E3%82%A2%E5%B7%9E">PA</a></td></tr>
<tr><th><a href="http://www.grokthis.net/Hosting/vps/">GrokThis.net</a><br />
Xen VPS</th>
<td>320MB<br />16GB</td><td>160GB<br />?</td><td>$20<br />$200</td>
<td>Philadelphia, <a href="http://ja.wikipedia.org/wiki/%E3%83%9A%E3%83%B3%E3%82%B7%E3%83%AB%E3%83%99%E3%83%8B%E3%82%A2%E5%B7%9E">PA</a></td></tr>

<tr><th><a href="http://www.dmehosting.com/budget-linux-vps.php">DMEHosting</a><br />
OpenVZ VPS1</th>
<td>256MB<br />25GB</td><td>1000GB<br />$10/200GB</td><td>$5.95<br />$71.40</td>
<td><a href="http://www.dmehosting.com/clients/announcements.php?id=19">Denver</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%AD%E3%83%A9%E3%83%89%E5%B7%9E">CO</a><br />
Chicago, <a href="http://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%AA%E3%83%8E%E3%82%A4%E5%B7%9E">IL</a>
</td></tr>
<tr><th><a href="https://www.quantact.com/openvz-plans.shtml">Quantact</a><br />
OpenVZ VS1</th>
<td>256MB<br />15GB</td><td>300GB<br />$1/GB</td><td>$14.99<br />$179.88</td>
<td>Santa Rosa, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a></td></tr>
<tr><th><a href="http://www.rackspacecloud.com/cloud_hosting_products/servers/pricing">RackspaceCloud</a><br />
Xen CloudServers</th>
<td>256MB<br />10GB</td><td>0GB<br />$.08-$.22/GB</td><td>$10.95<br />$131.40</td>
<td><a href="http://www.rackspacecloud.com/blog/2009/07/10/dallas-forth-worth-data-center-update/">Dallas</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%86%E3%82%AD%E3%82%B5%E3%82%B9%E5%B7%9E">TX</a></td></tr>
<tr><th><a href="http://www.xennode.net/pricing.html">XENnode</a><br />
XEN 256</th>
<td>256MB<br />10GB</td><td>200GB<br />?</td><td>$14.99<br />$143.99</td>
<td>Dallas, <a href="http://ja.wikipedia.org/wiki/%E3%83%86%E3%82%AD%E3%82%B5%E3%82%B9%E5%B7%9E">TX</a></td></tr>
<tr><th><a href="http://www.virtual-dedicated.net/index.shtml">Datarealm</a> Xen<br />
PowerVPS</th>
<td>256MB<br />10GB</td><td>1Mbps<br />Unmetered</td><td>$19.95<br />$215.52</td>
<td>?</td></tr>
<tr><th><a href="http://www.vr.org/vps-hosting-plans/plans-and-pricing/">HostVirtual</a> Xen<br />
XV0 Server</th>
<td>256MB<br />10GB</td><td>250GB<br />?</td><td>$14.95<br />$179.40</td>
<td><a href="http://status.vr.org/">San Jose</a>, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a></td></tr>
<tr><th><a href="http://rackunlimited.com/vps.html">RackUnlimited</a><br />
Xen RVPS-Starter</th>
<td>256MB<br />10GB</td><td>100GB<br />?</td><td>$8<br />$96</td>
<td><a href="http://rackunlimited.com/about.html">Asheville</a>, <a href="http://ja.wikipedia.org/wiki/%E3%83%8E%E3%83%BC%E3%82%B9%E3%82%AB%E3%83%AD%E3%83%A9%E3%82%A4%E3%83%8A%E5%B7%9E">NC</a></td></tr>
<tr><th><a href="http://quickweb.co.nz/xen-vps-linux-plans">QuickWeb</a><br />
Xen Lite</th>
<td>256MB<br />5GB</td><td>240GB<br />?</td><td>$17.95<br />$215.40</td>
<td>San Jose, <a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%95%E3%82%A9%E3%83%AB%E3%83%8B%E3%82%A2%E5%B7%9E">CA</a></td></tr>
<tr><th><a href="http://www.quickvps.net/xen/">QuickVPS</a><br />
Xen プラン1</th>
<td>256MB<br />10GB</td><td>50GB<br />?</td><td align="right">2300円<br />21600円</td>
<td>日本国内</td></tr>
<tr><th><a href="http://www.quillhost.com/openvz-vps.php">QuillHost</a><br />
OpenVZ Standard</th>
<td>256MB<br />8GB</td><td>150GB<br />?</td><td>$9.99<br />$119.88</td>
<td>Washington, <a href="http://ja.wikipedia.org/wiki/%E3%83%AF%E3%82%B7%E3%83%B3%E3%83%88%E3%83%B3D.C.">DC</a><br />
Charlotte, <a href="http://ja.wikipedia.org/wiki/%E3%83%8E%E3%83%BC%E3%82%B9%E3%82%AB%E3%83%AD%E3%83%A9%E3%82%A4%E3%83%8A%E5%B7%9E">NC</a></td></tr>
</table>
<p>
と、いうわけで、
Linode 360 を契約してみた。
</p>
<span id="more-544"></span>
<p>
<a href="http://www.linode.com/?r=577c3721c763acfc298a4e4cc4acaf654b4dbdc8">Linode のページ</a>で、
Linode 360 プランを選んで
「Sign Up Now!」をクリック。
氏名、住所、クレジットカード番号を入力し、
月々払い (Month to month) を選んで申し込むと、
$9.27 の注文書が表示された。
最初の月は月割りで月末までの分を注文するということのようだ
(ということは短期間試してみたいときは月末近くに申し込めばよい?)。
</p>
<blockquote>
米ドル支払いなので、
私が米国に滞在するとき使っている米銀のデビットカード 
(SSN がないとクレジットカードを発行してもらうのは難しい)
を試しに使ってみたら、
何の問題もなく普通に使えた
(もちろんクレジットカードならどこの国のカードでも普通に使えるはず)。
日本のデビットカード (<a href="http://ja.wikipedia.org/wiki/%E3%83%87%E3%83%93%E3%83%83%E3%83%88%E3%82%AB%E3%83%BC%E3%83%89#.E3.82.B8.E3.82.A7.E3.82.A4.E3.83.87.E3.83.93.E3.83.83.E3.83.88">J-Debit</a>) はキャッシュカードなので取扱店でしか使えず、
J-Debit が使える店の大半はクレジットカードも使えるので、
J-Debit を使うのは家電量販店くらいのものだが、
米国のデビットカードは (決裁までの期間が短い点以外は) 
クレジットカードと同等なので便利。
</blockquote>
<p>
2/17 13:18 に「Complete Order」 
ボタンを押して注文を確定すると、
「Payment Receipt」 がメールで届き、
1分後に 「Linode Account Activation」 メールが届いた (13:19)。
</p>
<p>
そのメールに書いてあった
<a href="https://www.linode.com/members/">Member's area</a> にログインすると、
データセンタの選択画面が表示されたので、
迷わず米国西海岸の Fremont, CA を選ぶ。
パーティションの設定をして、
インストールするディストリビューションとして 
「Ubuntu 9.10 64bit」 を選び、
root のパスワードの設定すると、
インストールが始まって 25秒ほどで完了 (13:22)。
</p>
<p>
注文してからここまで約5分。
</p>
<p>
ステータス表示が 「Powered Off」 になっているので、
「Boot」 ボタンを押す (13:29) と
8秒で起動が完了し、
ステータス表示が 「Running」 になった。
試しに手元の PC から ssh でログインしてみる (13:31)。
先ほど設定した root のパスワードを入力すると、
無事ログインできた。
</p>
<p>
注文してからここまで 15分かかっていない。<br />
あっけないほど簡単に VPS が使える状態になった。
</p>
<p>
次に、
ssh 公開鍵を設定して鍵認証で ssh ログインできることを確認した上で、
sshd_config を修正して sshd に HUP シグナルを送って、
パスワード認証ではログイン出来ないように設定。
続いて、
csh, tcsh, emacs, screen, stone, dbndns, daemontools, apache2, php5,
mysql-server, mysql-client, php5-mysql 
などを apt-get で次々インストールしていく。
実は stone を apt-get で入れたのは初めての経験。
ちゃんと最新版 
<a href="http://packages.ubuntu.com/ja/karmic/stone">stone 2.3e</a> 
がインストールされてちょっと感動 (*^_^*)。
ありがとうございます ＞ 北目さま。
</p>
<p>
最後に
chsh コマンドでシェルを /bin/csh (私の愛用シェルは今でも csh ^^;) に変更して、
私のホームディレクトリの環境一式を手元の PC から rsync で送り込んで、
emacs を起動すれば、
私の作業環境一式が VPS 上でも使えるようになる。
</p>
<p>
注文してからここまで 1時間かかっていない。
</p>
<p>
Linode では逆引きを設定させてくれるので、
私の VPS の IP アドレス 74.207.241.21 の逆引きを
<a href="http://fremont.gcd.org/test">fremont.gcd.org</a> に設定した。
あとは、
dnscache を動かして外部のキャッシュサーバに頼らず名前を引けるようにしたり、
私の自宅サーバ <a href="http://gw.gcd.org/test">gw.gcd.org</a> 
との間にトンネルを設置したり、
gw.gcd.org 上の MySQL からローカルの MySQL 
に対してレプリケーションを行なうようにしたり、
等々...
</p>
<blockquote>
もちろん、
Linode でもキャッシュサーバを提供しているので、
自前のキャッシュサーバを VPS 上で動かす必要は必ずしもないが、
名前引きができないことはあらゆるトラブルの元なので、
可能な限り自前で名前を引きに行くようにしたい。
なお、VPS は自身の IP アドレスを DHCP サーバに問合わせて設定しているので、
/etc/resolv.conf を直接書き換えても 
DHCP クライアントによって書き戻されてしまう。
そこで /etc/dhcp3/dhclient.conf に
「prepend domain-name-servers 127.0.0.1;」 を追加する
(元々コメントアウトされているので先頭の # を取り除く)。
</blockquote>
<p>
普通の ubuntu マシンをいじってるのと何ら変わらず、
しかも ssh でログインしたときのレスポンスも、
日本にあるサーバへアクセスしたときと同程度に速いので普通に使える。
apt-get しまくったので WordPress を動かす準備はすぐ整った。
VPS 上の MySQL のアカウントは、
gw.gcd.org とは (当然) 異なるパスワードを設定し、
WordPress からローカルの (つまり VPS 上の) MySQL へつなぐよう設定する。
これで (万一) gw.gcd.org 側が落ちても VPS 側だけでブログを表示できる。
</p>
<p>
一点注意すべきなのは、
レプリケーションは gw.gcd.org から fremont.gcd.org への一方通行なので、
VPS 上の MySQL へ何を書込んでもそれは gw.gcd.org へは反映されないという点。
したがってブログへコメントを書込むときは、
fremont.gcd.org ではなく gw.gcd.org 側へアクセスするように、
ブログの URL http://www.gcd.org/blog/ とは別に、
コメントやトラックバックの URL として 
http://feedback.gcd.org/blog/ を用意した
(feedback.gcd.org は gw.gcd.org のエイリアス)。
</p>
<blockquote>
もちろん双方向レプリケーションを行なうことも可能だが、
Linode の仮想環境に万一脆弱性があった場合に備えて
Linode 側で何が起きても構わないように一方通行にしておく方が無難だろう。
さらに、
Linode 側からレプリケーション用の 
ID 以外で gw.gcd.org の MySQL をアクセスしようとしても、
たとえ<a href="http://www.gcd.org/blog/2006/04/334/">パスワードが一致しても通信を遮断する仕掛け</a>を
gw.gcd.org 側で組み込んである。
</blockquote>
<p>
そして WordPress が設定するクッキーのドメインを
gcd.org に変更して、
feedback.gcd.org なページ (コメント書込み、トラックバック受付) 
で設定したクッキーを
www.gcd.org なページで参照できるようにする。
wordpress/wp-config.php に以下のコードを入れておけばよい:
</p>
<pre class="code">
if ( !defined('COOKIE_DOMAIN') )
	define('COOKIE_DOMAIN', "gcd.org");
</pre>
<p>
最後に、
www.gcd.org の IP アドレスとして VPS の IP アドレス 74.207.241.21 を追加した。
</p>
<pre class="terminal">
% dnsqr a www.gcd.org
1 www.gcd.org:
61 bytes, 1+2+0+0 records, response, noerror
query: 1 www.gcd.org
answer: www.gcd.org 12657 A 74.207.241.21
answer: www.gcd.org 12657 A 60.32.85.216
</pre>
<p>
すなわち、
いま読者の皆さんが読んでるこのページは、
1/2 の確率で Linode の VPS が送信したページということになる。
すでに一週間近くこの状態を続けていて、
今のところ 360MB のメモリで一応足りているが、
アクセスが増えてくるとどうなるか少し心許無い。
</p>
<pre class="terminal">
fremont:/home/sengoku % free
             total       used       free     shared    buffers     cached
Mem:        351508     264236      87272          0       4444      35316
-/+ buffers/cache:     224476     127032
Swap:       262136      39656     222480
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/02/544/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CTO日記も livedoorブログから WordPress へ引越しました  (URL は変更なし)</title>
		<link>http://www.gcd.org/blog/2010/02/540/</link>
		<comments>http://www.gcd.org/blog/2010/02/540/#comments</comments>
		<pubDate>Tue, 09 Feb 2010 22:46:28 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>
		<category><![CDATA[プログラミングと開発環境]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=540</guid>
		<description><![CDATA[「仙石浩明の日記」 に続いて、 「仙石浩明CTO の日記」 も先週末に livedoorブログから自宅サーバへ引っ越した (つまりネームサーバの設定を変更して切替。有料プランの解約はこれから)。 もともと両ブログは相互にリンクを張って密接に連係していたので、 引越を機会に両者を統合した。 統合といっても両ブログは微妙(?)に読者層が異なると思われるし、 何よりページの体裁が大きく変わってしまっては読者の方々を戸惑わせてしまうので、 CTO日記を 「仙石浩明の日記」 の一カテゴリという位置付けにして、 かつページの体裁は WordPress のテーマを切り替えることによって、 どちらのブログもあまり大きな変化がないようにしている。 「仙石浩明CTO の日記」 http://sengoku.blog.klab.org/ をアクセスすると、 次のような PHP スクリプトを走らせた上で、 WordPress を呼び出す (末尾の require 文): &#60;?php $new = NULL; if ($_SERVER['REQUEST_URI'] == "/") { $new = "/blog/category/cto/"; } elseif ($_SERVER['REQUEST_URI'] == "/feed/") { $new = "/blog/category/cto/feed/"; } elseif (preg_match('@^/\d+/\d+/\d+/@', $_SERVER['REQUEST_URI'], [...]]]></description>
			<content:encoded><![CDATA[<p>
「仙石浩明の日記」 に続いて、
「仙石浩明CTO の日記」 も先週末に 
livedoorブログから<a href="http://www.gcd.org/">自宅サーバ</a>へ引っ越した 
(つまりネームサーバの設定を変更して切替。有料プランの解約はこれから)。
もともと両ブログは相互にリンクを張って密接に連係していたので、
引越を機会に両者を統合した。
</p>
<p>
統合といっても両ブログは微妙(?)に読者層が異なると思われるし、
何よりページの体裁が大きく変わってしまっては読者の方々を戸惑わせてしまうので、
CTO日記を 
「<a href="http://www.gcd.org/blog/">仙石浩明の日記</a>」 
の<a href="http://www.gcd.org/blog/category/cto/">一カテゴリ</a>という位置付けにして、
かつページの体裁は 
<a href="http://ja.wordpress.org/">WordPress</a> 
のテーマを切り替えることによって、
どちらのブログもあまり大きな変化がないようにしている。
</p>
<p>
「仙石浩明CTO の日記」 
<a href="http://sengoku.blog.klab.org/">http://sengoku.blog.klab.org/</a>
をアクセスすると、
次のような PHP スクリプトを走らせた上で、
WordPress を呼び出す (末尾の require 文):
</p>
<pre class="code">
&lt;?php
$new = NULL;
if ($_SERVER['REQUEST_URI'] == "/") {
    $new = "/blog/category/cto/";
} elseif ($_SERVER['REQUEST_URI'] == "/feed/") {
    $new = "/blog/category/cto/feed/";
} elseif (preg_match('@^/\d+/\d+/\d+/@',
		     $_SERVER['REQUEST_URI'], $matches)) {
    $new = $_SERVER['REQUEST_URI'];
...(中略)...
}
if ($new) {
    ...(中略)...
    $ORIG_SERVER_NAME = $_SERVER['SERVER_NAME'];
    $host = "www.gcd.org";
    $_SERVER['SERVER_NAME'] = $host;
    $_SERVER['REQUEST_URI'] = $new;
    $_SERVER['SCRIPT_NAME'] = $new;
    $_SERVER['PHP_SELF'] = $new;
    $abspath = "/usr/local/www/wordpress/";
    $themepath = "${abspath}wp-content/themes/sengoku_cto/";
    define('WP_USE_THEMES', true);
    define('TEMPLATEPATH', $themepath);
    define('STYLESHEETPATH', $themepath);
    require("${abspath}wp-blog-header.php");
...(中略)...
}
?&gt;
</pre>
<p>
つまり
http://sengoku.blog.klab.org/ へのアクセスは、
パス名に 「/category/cto/」 を追加することによって、
CTO日記カテゴリへのアクセスに変換する。
</p>
<p>
ページの体裁については、
「wp-content/themes/sengoku_cto/」 ディレクトリが、
CTO日記のテーマフォルダで、
二つの PHP 定数 TEMPLATEPATH と STYLESHEETPATH 
をこのディレクトリへ設定することによって、
テーマの切り替えを行なっている。
</p>
<p>
テーマフォルダの中にあるテーマ関数ファイル 「functions.php」 は、
WordPress の初期化中に読み込まれるので、
ここに PHP スクリプトを書いておくことによって
WordPress の挙動を変更することができる。
例えばブログのタイトルを 「仙石浩明CTO の日記」 に変更するには、
以下のスクリプトを functions.php に追加しておけばよい:
</p>
<pre class="code">
function option_blogname_cto() {
    return '仙石浩明CTO の日記';
}
add_filter('pre_option_blogname', 'option_blogname_cto');
</pre>
<p>
つまり、
pre_option_blogname フックに、
option_blogname_cto フィルタを登録する。
</p>
<p>
WordPress では、
ブログのタイトルなど各種オプションの設定値 (DB に格納している) を、
get_option($setting) 関数を呼び出すことで参照している。
例えばタイトルは get_option('blogname') を呼び出すことで得られ、
URL は get_option('home') で得られる。
</p>
<p>
get_option($setting) 関数は wp-includes/functions.php で定義されていて、
以下のようにフィルタフック pre_option_* が定義されている:
</p>
<pre class="code">
function get_option( $setting, $default = false ) {
    global $wpdb;

    // Allow plugins to short-circuit options.
    $pre = apply_filters('pre_option_' . $setting, false);
    if ( false !== $pre )
	return $pre;
    ...(中略)...
}
</pre>
<p>
つまり、
「pre_option_設定名」 というフックに登録されたフィルタが値を返すなら、
get_option はオプションの設定値ではなくフィルタが返した値を返すようになる。
前述の例なら、
「pre_option_blogname」 フックに登録された
「option_blogname_cto」 フィルタが 「仙石浩明CTO の日記」 という値を返すので、
get_option('blogname') も 「仙石浩明CTO の日記」 という値を返すようになり、
結果としてブログのタイトルを変更できる、というわけ。
</p>
<p>
ただし、
前述したように CTO日記は 「仙石浩明の日記」 の一カテゴリという位置付けなので、
ブログのタイトルを変更しただけだと、
ブログ 「仙石浩明CTO の日記」 の 「仙石浩明CTO の日記」 カテゴリということで、
ページのタイトル等が
「仙石浩明CTO の日記 &raquo; 仙石浩明CTO の日記」
という冗長なものになってしまう。
そこで、
以下のようなスクリプトを 「functions.php」 に追加して、
タイトルとカテゴリ名が同じときはカテゴリ名が表示されないようにする:
</p>
<pre class="code">
function single_cat_title_cto($category_name) {
    $name = get_option('blogname');
    if ($category_name == $name) return "";
    return $category_name;
}
add_filter('single_cat_title', 'single_cat_title_cto');
</pre>
<p>
single_cat_title フックは、
wp-includes/general-template.php で定義されていて、
ページのタイトルなどに表示されるカテゴリ名を変更することができる。
</p>
<p>
以上で、
「仙石浩明の日記」 の一カテゴリを 
「仙石浩明CTO の日記」 の体裁で見せることができるようになる。
しかし、
元が 「仙石浩明の日記」 であるだけに、
リンク先が全て 「仙石浩明の日記」 のページになってしまう。
例えば、
<a href="http://sengoku.blog.klab.org/">「仙石浩明CTO の日記」 のトップページ</a>の一番下に、
「<a href="http://sengoku.blog.klab.org/page/2/">古い投稿 &raquo;</a>」 
というリンクがあるが、
このリンク先が http://www.gcd.org/blog/page/2/ になってしまい、
たどると <a href="http://www.gcd.org/blog/page/2/">「仙石浩明の日記」 のトップページの 2ページ目</a>へ遷移してしまう。
</p>
<p>
また、
本文中 (あるいはサイドバー) に現れるリンクも、
DB のデータは
「仙石浩明の日記」 のパーマリンクを用いているので、
たとえそれが 「仙石浩明CTO の日記」 カテゴリに含まれていても、
そのリンクをたどると
「仙石浩明の日記」 の記事として表示されてしまう。
</p>
<p>
そこで、
遷移先も 「仙石浩明CTO の日記」 として表示したいリンクを、
フィルタで書き換えることにした。
つまり DB のデータは 「仙石浩明の日記」 へのリンクのままで、
ブラウザに送信する前に都度書き換える。
</p>
<p>
対象となるリンクは、
記事本文中だけでなく、
前述したページナビ 「古い投稿」 「新しい投稿」 や、
サイドバー (「人気記事」 や 「最近の投稿」) にも現れる。
ページ丸ごと (つまり HTTP レスポンス丸ごと) 
HTML を書き換えられるフックがあるとよかったのだが、
残念ながらそういうフックは定義されていないようだ。
以下のフックそれぞれについてリンクを書き換えればよさげ:
</p>
<table class="ruler">
<tr><th>フィルタフック</th>
<th>フィルタが変更できる対象, 第2引数, ...</th></tr>
<tr><td>the_content</td>
<td>記事本文 HTML</td></tr>
<tr><td>the_category</td>
<td>記事の末尾に表示されるカテゴリーリストの HTML,<br />
$separator, $parents</td></tr>
<tr><td>get_pagenum_link</td>
<td>ページ末尾に表示されるページナビ 「古い投稿」 「新しい投稿」 の URL</td></tr>
<tr><td>post_link</td>
<td>記事の URL (パーマリンク), $post, $leavename</td></tr>
<tr><td>widget_text</td>
<td>サイドバーに表示されるテキストウィジェットの HTML, $instance</td></tr>
<tr><td>wp_list_categories</td>
<td>サイドバーに表示されるカテゴリーのリストの HTML</td></tr>
<tr><td>category_feed_link</td>
<td>カテゴリーの RSSフィードの URL, $feed</td></tr>
</table>
<p>
書き換え対象のリンクを決めるために、
まず CTO日記カテゴリに属す記事の ID を取得する:
</p>
<pre class="code">
function setup_cto_id() {
    global $wpdb;
    global $is_cto_id;
    $result = $wpdb-&gt;get_results("SELECT object_id FROM wp_term_relationships WHERE term_taxonomy_id=17", ARRAY_N);
    foreach ($result as $row) {
	$is_cto_id[$row[0]] = 1;
    }
}
</pre>
<p>
あるカテゴリに属す記事ID のデータを取得する関数など、
WordPress に含まれているんじゃないかと探してみたのだが、
見つからなかったので DB に問合わせて取得するようにしてみた。
毎回 DB アクセスが発生してしまうが、
キャッシュとかはアクセス数が増えてから考える (^^;)。
「term_taxonomy_id=17」 が CTO日記のカテゴリ 
(決め打ち ^^;)。
CTO日記カテゴリに属す記事は、
配列 $is_cto_id[記事ID] に 1 を代入しておく。
</p>
<p>
次に URL を書き換えるスクリプト:
</p>
<pre class="code">
function replace_URL_cto($matches) {
    global $is_cto_id;
    global $ORIG_SERVER_NAME;
    if (!is_array($is_cto_id)) {
	setup_cto_id();
    }
    if (is_null($matches[2])) {
	if ($matches[1] == "category/cto/") return "http://$ORIG_SERVER_NAME/";
	return "http://$ORIG_SERVER_NAME/$matches[1]";
    } elseif ($is_cto_id[$matches[2]]) {
	return "http://$ORIG_SERVER_NAME/$matches[1]";
    }
    return $matches[0];
}

function convert_URLs_cto($text) {
    $textarr = preg_split("/(<.*>)/U", $text, -1, PREG_SPLIT_DELIM_CAPTURE);
    $stop = count($textarr);
    for ($i = 0; $i < $stop; $i++) {
	$content = $textarr[$i];
	if (strlen($content) > 0) {
	    $content = preg_replace_callback(
		    '@http://www.gcd.org/blog/(\d+/\d+/(\d+)|category/cto/)@',
		    'replace_URL_cto', $content);
	}
	$output .= $content;
    }
    return $output;
}
</pre>
<p>
顔文字を画像に変換して表示するフィルタ wptexturize 
(wp-includes/formatting.php で定義されている) を参考にさせてもらった。
preg_replace_callback() を使って書き換え対象リンクを探し、
replace_URL_cto で記事ID がCTO日記カテゴリに属す
($is_cto_id[$matches[2]] が TRUE)
場合のみ書き換える。
</p>
<p>
最後に、
この書き換えフィルタ replace_URL_cto を前述したフィルタフックに追加:
</p>
<pre class="code">
add_filter('the_content', 'convert_URLs_cto');
add_filter('the_category', 'convert_URLs_cto');
add_filter('get_pagenum_link', 'convert_URLs_cto');
add_filter('post_link', 'convert_URLs_cto');
add_filter('widget_text', 'convert_URLs_cto');
add_filter('wp_list_categories', 'convert_URLs_cto', 12000);
add_filter('category_feed_link', 'convert_URLs_cto');
</pre>
<p>
wp_list_categories にフィルタを追加すると
<a href="http://www.coppit.org/code/">Category Order プラグイン</a>と衝突するので、
優先順位を下げて Category Order プラグインの後で実行されるようにしている。
</p>
<p>
また、
the_category, post_link, widget_text, category_feed_link 各フックは、
2つ以上の引数を持つが、
第1引数 (書き換え対象の HTML あるいは URL) 
のみ使用するので引数の数 (add_filter の第3引数) を省略している。
</p>]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/02/540/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>本ブログを livedoorブログから WordPress へ引越しました (URL は変更なし)</title>
		<link>http://www.gcd.org/blog/2010/01/494/</link>
		<comments>http://www.gcd.org/blog/2010/01/494/#comments</comments>
		<pubDate>Wed, 27 Jan 2010 23:26:08 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[システム構築・運用]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/?p=494</guid>
		<description><![CDATA[このブログ 「仙石浩明の日記」 は、 今まで livedoorブログの有料プラン (月額 315円) を利用していた。 比較的自由にデザインをカスタマイズできること、 URL のドメインを自由に設定できることから、 2006年3月9日にこのブログを開設して以来、 今まで 4年近く使い続けてきた。 しかし自前サーバでない不便さ、隔靴掻痒感はいかんともしがたく、 乗り換えを検討したことは何度もあって、 そのたびに不便さと乗り換えの面倒くささを天秤にかけて踏み切れずにいた。 例えば、 HTML文法エラーを修正できない問題点を見つけたときや、 ブログのサイドバーにある 「livedoor Reader」 「RSS」 「livedoor Blog」などのバナーを非表示にしていたら、 ライブドアから警告メールが来て、 やむなくバナー表示を復活させたときなど。 なかでも、 昨年ブログ管理画面 (有料プラン) がリニューアルされたときは、 とても大きなフラストレーションを感じた。 ライブドアはよかれと思ってリニューアルしているのだろうが、 JavaScript を多用した新管理画面は、 私にとっては使いにくいことこのうえないし、 生ログ取得スクリプトも使えなくなった。 そして、 今年に入って 「ブログ共通ヘッダー」 (最上端に表示される livedoor Blogのロゴ, 所属カテゴリ, テキスト広告) が勝手に表示されるようになったのが最後の背中のひと押しとなった。 これを非表示にしたければ PRO プラン (月額 315円) ではなく ADVANCE プラン (月額 [...]]]></description>
			<content:encoded><![CDATA[<p>
このブログ 「仙石浩明の日記」 は、
今まで livedoorブログの有料プラン (月額 315円) を利用していた。
比較的自由にデザインをカスタマイズできること、
URL のドメインを自由に設定できることから、
2006年3月9日にこのブログを開設して以来、
今まで 4年近く使い続けてきた。
しかし自前サーバでない不便さ、隔靴掻痒感はいかんともしがたく、
乗り換えを検討したことは何度もあって、
そのたびに不便さと乗り換えの面倒くささを天秤にかけて踏み切れずにいた。
</p>
<p>
例えば、
<a href="http://www.gcd.org/blog/2006/06/75/">HTML文法エラーを修正できない</a>問題点を見つけたときや、
ブログのサイドバーにある 
「livedoor Reader」
「RSS」
「livedoor Blog」などのバナーを非表示にしていたら、
ライブドアから警告メールが来て、
やむなくバナー表示を復活させたときなど。
なかでも、
昨年ブログ管理画面 (有料プラン) がリニューアルされたときは、
とても大きなフラストレーションを感じた。
ライブドアはよかれと思ってリニューアルしているのだろうが、
JavaScript を多用した新管理画面は、
私にとっては使いにくいことこのうえないし、
<a href="http://www.gcd.org/blog/2006/05/354/">生ログ取得スクリプト</a>も使えなくなった。
</p>
<p>
そして、
今年に入って
「ブログ共通ヘッダー」 
(最上端に表示される livedoor Blogのロゴ, 所属カテゴリ, テキスト広告) 
が勝手に表示されるようになったのが最後の背中のひと押しとなった。
これを非表示にしたければ
PRO プラン (月額 315円) ではなく
ADVANCE プラン (月額 840円) に変更しろという趣旨 (要は値上げ) 
なのだと思うが、
何の連絡もなく画面の一番目立つ最上端に、
醜悪なヘッダー
(ADVANCE プランへの移行を促すためにわざと醜悪にしている? *_*)
を勝手に挿入するデリカシーの無さに驚き呆れ、
<a href="http://www.gcd.org">自前サーバ</a>への引越を決めた。
</p>
<img src="http://www.gcd.org/sengoku/picts/livedoor-header.jpg" width="400" height="190" alt="common header" />
<p>
ブラウザ画面最上端 ↑ の 「ブログ共通ヘッダー」 
(所属カテゴリ 「IT > プログラミング 」 と 
「受験にまつわるエピソードを教えてください」 という広告? 表示) は、
ライブドアのサーバが HTML 中に自動挿入する以下の 
JavaScript (settings/header.js) 
によって body 要素へ追加 (appendChild) されている。
</p>
<pre class="code">
(function () {
    var hd = document.createElement('div');
    var str = '';
    str += '&lt;div id="header2" style="z-index:10001"&gt;&lt;table cellspacing="0" class="blog-common-header" id="header"&gt;';
	...(中略)...
    hd.innerHTML = str;
    document.body.appendChild(hd);
	...(後略)...
</pre>
<p>
ユーザがカスタマイズ可能な範囲の外であるため、
この JavaScript の実行そのものを止めることは無理っぽいが、
追加された子要素を削除する JavaScript を実行して消すことは可能。
例えば以下のようなコードを HTML ファイルのどこかに入れておけばよい。
</p>
<pre class="code">
&lt;script type="text/javascript" src="http://www.gcd.org/sengoku/docs/livedoor.js"&gt;&lt;/script&gt;
</pre>
<p>
livedoor.js は、
body 要素の子要素 「header2」 を取り除く (removeChild) だけの JavaScript:
</p>
<pre class="code">
(function () {
    var element = document.getElementById("header2");
    element.removeChild(element.childNodes.item(0));
})();
</pre>
<p>
正月早々 「ブログ共通ヘッダー」 
が勝手に表示されるようになったことに気付き、
直ちにこの対策を行なって非表示にしたのだが、
以前バナーを非表示にしたときライブドアから警告メールが来たことを考えれば、
今回も警告メールが来るのは時間の問題だろうと、
引越を急いだ次第。
</p>
<p>
私はブログを書くときも HTML で書いていて、
あまり CMS 的な機能は必要としないので、
トラックバックとコメントを受付けるだけの簡単な 
Web アプリを書こうと思っていたのだが、
試しにインストールしてみた 
<a href="http://ja.wordpress.org/">WordPress</a> 
が思いの外シンプルな作りで、
ちょっといじっているうちに簡単にカスタマイズできてしまったので、
これを使うことにした。
</p>
<blockquote>
livedoorブログでエクスポートしたデータを 
(Movable Type / TypePad のデータとして) WordPress でインポートしたら、
(pre 要素なのに) 行頭の空白文字が削除されてしまって往生したが、
大した量でもなかったので手作業で修正してしまった。
もう少し量が多かったら真面目に PHP コードを追ったのだが...
</blockquote>
<p>
こんなに簡単にできるなら、
もっと早く WordPress へ移行すればよかった。
既存のテーマを見よう見まねでいじっただけなのだが、
以前のデザインをほとんどそのまま踏襲することができた
(むしろ余計な div 要素を排除できたので、よりシンプルになった)。
</p>
<p>
livedoorブログと WordPress では URL (パーマリンク, permalink) 
の形式が異なる。
例えば、
<a href="http://www.gcd.org/blog/2008/04/154/">あるエントリ</a>の 
URL は、
livedoorブログ (旧URL) と
WordPress (新URL) で次のようになる:
</p>
<table class="ruler">
<tr><th width="70"></th>
<th>旧: livedoorブログ</th><th>新: WordPress</th></tr>
<tr><th>ベースURL</th>
<td>http://blog.gcd.org</td>
<td>http://www.gcd.org/blog</td></tr>
<tr><th>エントリ</th>
<td>/archives/51168556.html</td>
<td>/2008/04/154/</td></tr>
<tr><th>月別表示</th>
<td>/archives/2008-04.html</td>
<td>/2008/04/</td></tr>
<tr><th>カテゴリ</th>
<td>/archives/cat_50026701.html</td>
<td>/category/enlighten/</td></tr>
<tr><th>RSS</th>
<td>/index.rdf</td>
<td>/feed/rdf/</td></tr>
</table>
<p>
一見して、
WordPress のパーマリンクの方が分かりやすい。
正確に言えば、
<a href="http://wpdocs.sourceforge.jp/Using_Permalinks">WordPress のパーマリンクの形式</a>は任意に設定できて、
livedoorブログ (Movable Type ベース) の形式に合わせることも可能だが、
エントリID (上記の例だと 51168556 と 154) 
が異なるので形式を合わせること自体にはあまり意味はない。
</p>
<p>
livedoorブログを利用していた時の URL がそのまま使えるように、
旧URL を新URL へ変換する以下の PHP スクリプトを書いた
(switch 文で URL ごとにずらずら case を並べただけ)。
</p>
<pre class="code">
&lt;?php
$new = NULL;
if ($_SERVER['REQUEST_URI'] == "/") {
    $new = "/blog/";
} elseif ($_SERVER['REQUEST_URI'] == "/atom.xml") {
    $new = "/blog/feed/atom/";
} elseif ($_SERVER['REQUEST_URI'] == "/index.rdf") {
    $new = "/blog/feed/rdf/";
} elseif (preg_match('/^\/archives\/(20\d\d)-(\d\d)\.html$/',
		     $_SERVER['REQUEST_URI'], $matches)) {
    $year = $matches[1];
    $mon = $matches[2];
    $new = "/blog/$year/$mon/";
} elseif (preg_match('/^\/archives\/cat_(\d+)\.html$/',
		     $_SERVER['REQUEST_URI'], $matches)) {
    $id = $matches[1];
    $new = "/blog/category/";
    switch ($id) {
    case 50026701: $new .= "enlighten/";   break;
    case 50026704: $new .= "business/";    break;
    case 50026703: $new .= "engineer/";    break;
    case 50026699: $new .= "system/";      break;
    case 50035671: $new .= "hardware/";    break;
    case 50026700: $new .= "programming/"; break;
    case 50021362: $new .= "stone/";       break;
    case 50035209: $new .= "la-fonera/";   break;
    case 50041534: $new .= "hawaii/";      break;
    case 50045637: $new .= "hongkong/";    break;
    case 50026702: $new .= "others/";      break;
    default: $new = NULL;
    }
} elseif (preg_match('/^\/archives\/(\d+)\.html$/',
		     $_SERVER['REQUEST_URI'], $matches)) {
    $id = $matches[1];
    $new = "/blog/";
    switch ($id) {
    case 50071073: $new .= "2006/03/8/"; break;
	...(中略)...
    case 51168556: $new .= "2008/04/154/"; break;
	...(中略)...
    case 51552583: $new .= "2009/12/184/"; break;
    case 51555097: $new .= "2010/01/185/"; break;
    default: $new = NULL;
    }
}
if ($new) {
    $host = "www.gcd.org";
    $_SERVER['SERVER_NAME'] = $host;
    $_SERVER['REQUEST_URI'] = $new;
    $_SERVER['SCRIPT_NAME'] = $new;
    $_SERVER['PHP_SELF'] = $new;
    define('WP_USE_THEMES', true);
    require('/usr/local/www/wordpress/wp-blog-header.php');
} else {
    header("HTTP/1.1 301 Moved Permanently");
    header("Location: http://www.gcd.org/blog/");
    exit();
}
?&gt;
</pre>
<p>
http://blog.gcd.org/* にアクセスすると、
↑ この PHP スクリプトが実行される。
</p>
<p>
これに加え、
逆方向の変換、
つまり新URL から旧URL への変換も必要。
例えば 「はてなブックマーク」 
へ頂いたコメント
(例えば前述したエントリに対する<a href="http://b.hatena.ne.jp/entry/blog.gcd.org/archives/51168556.html">コメント一覧</a>) 
は旧URL に紐づいているので、
各エントリに旧URL を登録しておく必要がある。
WordPress には
<a href="http://wpdocs.sourceforge.jp/Using_Custom_Fields">カスタムフィールド</a>
という機能があって、ひとつのエントリに複数の 「キー」 と、
各キーにその値を登録することができる。
そこで沢山の 「ブックマーク」 を頂いたエントリには、
「hatena_b」 というキーで旧URL を登録しておくことにした。
すると、以下のような PHP スクリプトでブックマーク数
<a href="http://b.hatena.ne.jp/entry/http://blog.gcd.org/archives/51168556.html"><img style="border:0;" src="http://b.hatena.ne.jp/entry/image/http://blog.gcd.org/archives/51168556.html" alt="" /></a>
を表示できる。
</p>
<pre class="code">
&lt;?php
  $hatena_b = get_post_meta($post-&gt;ID, "hatena_b", true);
  if ($hatena_b)
    echo ' &lt;a href="http://b.hatena.ne.jp/entry/' . $hatena_b
       . '"&gt;&lt;img style="border:0;" src="http://b.hatena.ne.jp/entry/image/'
       . $hatena_b . '" alt="" /&gt;&lt;/a&gt;';
?&gt;
</pre>
<p>
最後に、
ネームサーバの設定変更を行なって、
blog.gcd.org レコード (TTL = 3時間) の値を 
「CNAME blog-01.livedoor.jp」 から 「CNAME www.gcd.org」 へ変更した。
1/26 19:35 に変更を行なったところ、
19:46 には最初のアクセスが www.gcd.org へ届き、
20時台には大半のアクセスが www.gcd.org へ届くようになった。
20時台にライブドア側へ届いた blog.gcd.org へのアクセスはわずかに 7件、
その後は 1 時間〜数時間ごとに、ぱらぱらアクセスがあるという状況が続いている。
旧レコードを保持しているキャッシュネームサーバが残っていて、
そのネームサーバを使ってるブラウザからのアクセスのみが、
ライブドア側へ届いているといった感じ。
</p>

<span id="more-494"></span>
<p>
同日 20:20 追記:
</p>
<p>
そろそろ帰ろうかと思っていたら、
<a href="http://d.hatena.ne.jp/hirose31/">ひろせ31さん</a>からチャットを頂いた:
</p>
<pre class="terminal">
(19:05:46) ひろせ: curl -I http://www.gcd.org/blog/ | grep X-Pingback
到達不可なヘッダついてますよー
(19:06:46) 仙石: ども、ご無沙汰してます。
(19:06:53) ひろせ: どもどもー
(19:08:21) 仙石: そっか、ヘッダに残ってましたか～
(19:08:34) 仙石: チェックするのを忘れていました。ありがとうございます
(19:08:40) ひろせ: いえいえー
</pre>
<p>
確かにヘッダに 「X-Pingback:」 フィールドがついている。↓
</p>
<pre class="terminal">
% curl -I http://www.gcd.org/blog/
HTTP/1.1 200 OK
Date: Thu, 28 Jan 2010 10:08:54 GMT
Server: Apache
X-Pingback: http://gt.gcd.org/wordpress/xmlrpc.php
Content-Type: text/html; charset=UTF-8

</pre>
<p>
<a href="http://wpdocs.sourceforge.jp/%E7%94%A8%E8%AA%9E%E9%9B%86#.E3.83.94.E3.83.B3.E3.83.90.E3.83.83.E3.82.AF">Pingback</a> 
って何だろう (^^;) と思いつつ、
「gt.gcd.org」 は <a href="http://www.gcd.org">GCD</a> (私の個人サイト) 
内部でのみ有効なホスト名 (IPアドレスは 192.168.1.1) なので、
このフィールドの目的が何であれ取り除いた方がいい。
実は、
HTTP レスポンス中にも
「gt.gcd.org」 
は出力されていて、
 ↓
</p>
<pre class="terminal">
&lt;link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://gt.gcd.org/wordpress/xmlrpc.php?rsd" /&gt;
&lt;link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://gt.gcd.org/wordpress/wp-includes/wlwmanifest.xml" /&gt; 
</pre>
<p>
これは公開前に気付いて取り除いたのだが、
HTTP ヘッダの方はチェックするのを忘れていた。orz
</p>
<p>
上記二つの link 要素は、
それぞれ rsd_link と wlwmanifest_link というフィルタが出力しているので、
このフィルタを wp_head フックから取り除いてやればよい。
具体的には、
現在のテーマの functions.php ファイルに、
以下のようなコードを追加する:
</p>
<pre class="code">
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wlwmanifest_link');
</pre>
<p>
ひろせ31さんから指摘を受けて、
条件反射的に WordPress の PHP コードを検索してみた:
</p>
<pre class="terminal">
% find /usr/local/www/wordpress -type f | xargs grep X-Pingback
/usr/local/www/wordpress/wp-includes/classes.php:	 * Sets the X-Pingback header, 404 status (if 404), Content-type. If showing
/usr/local/www/wordpress/wp-includes/classes.php:		$headers = array('X-Pingback' => get_bloginfo('pingback_url'));
</pre>
<p>
classes.php の該当箇所を見てみると、
</p>
<pre class="code">

	function send_headers() {
		$headers = array('X-Pingback' => get_bloginfo('pingback_url'));
		...
</pre>
<p>
ありゃ、ハードコードされてる...
</p>
<p>
前述した link 要素のように、
フィルタで出力するようになっていれば、
そのフィルタを無効化するだけで済むのに...
(*_*)
</p>
<p>
さて、どうしたものか。
この send_headers() 関数は、
$headers 配列にフィールドを加えていって、
最後に $headers を HTTP ヘッダとして送信する、
という処理の流れのようだ。
</p>
<p>
さらに読み進めていくと、
</p>
<pre class="code">
		...
		$headers = apply_filters('wp_headers', $headers, $this);
		...
</pre>
<p>
ありがたいことにフィルタ・フックが定義されていた。
この wp_headers フックに、
X-Pingback: フィールドを取り除くフィルタを登録すれば、
うまくいきそうだ。
</p>
<pre class="terminal">
(19:12:57) 仙石: X-Pingback はハードコードされてますねぇ... > WordPress
(19:13:12) ひろせ: うひー
(19:13:22) 仙石: フィルタで削除するしかないのかな... あるいは WordPress のコードを直接書き換えるか...
(19:13:22) ひろせ: gt.gcd.orgって、内部ホストなんですかね？
(19:13:26) 仙石: です
(19:13:43) 仙石: 管理用なので、外部からはアクセスできないようにしたいです
(19:16:46) ひろせ: Apacheで、 Header unset X-Pingback で消えるかもすね。
(19:17:58) 仙石: なるほど
</pre>
<p>
現在のテーマの functions.php ファイルに、
以下の PHP コードを追加してみた:
</p>
<pre class="code">
function remove_X_Pingback($headers) {
	unset($headers['X-Pingback']);
	return $headers;
}
add_filter('wp_headers', 'remove_X_Pingback');
</pre>
<p>
初めて書く WordPress のフィルタだったが
(実をいうと PHP も書き慣れているわけではないので、
配列の要素を削除する方法もマニュアルを調べながら書いた ^^;)、
うまく動いているようだ:
</p>
<pre class="terminal">
% curl -I http://www.gcd.org/blog/
HTTP/1.1 200 OK
Date: Thu, 28 Jan 2010 10:29:33 GMT
Server: Apache
Content-Type: text/html; charset=UTF-8

</pre>
<p>
めでたく、X-Pingback: フィールドが出力されなくなった。
</p>
<pre class="terminal">
(19:30:23) 仙石: 取り除きました。＞ X-Pingback:
(19:30:33) ひろせ: oo
(19:30:38) 仙石: functions.php に、
function remove_X_Pingback($headers) {
        unset($headers['X-Pingback']);
        return $headers;
}
add_filter('wp_headers', 'remove_X_Pingback');
(19:30:51) 仙石: を加えました。
</pre>
<p>
ありがとうございました。＞ ひろせ31さん
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/01/494/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>香港で買ったプリペイド SIM カードを日本で使ってみる</title>
		<link>http://www.gcd.org/blog/2010/01/185/</link>
		<comments>http://www.gcd.org/blog/2010/01/185/#comments</comments>
		<pubDate>Sun, 03 Jan 2010 22:07:39 +0000</pubDate>
		<dc:creator>hiroaki_sengoku</dc:creator>
				<category><![CDATA[香港]]></category>

		<guid isPermaLink="false">http://www.gcd.org/blog/2010/01/185/</guid>
		<description><![CDATA[あけましておめでとうございます。今年もよろしくお願いします。 香港 CSL の Power Prepaid SIM $88 を買った (HK$88 = 約1060円)。 この SIM カードを妻の携帯電話 Motorola ZN200 に入れて、 一週間の香港滞在中、 互いに連絡するときに大いに利用した。 が、通話料が一分間 HK$0.1 (約 1円) と安すぎるので、 HK$82.20 も残ってしまった (つまり HK$5.80 しか使ってない)。 有効期限は 2010年6月15日までの半年間。 期限内にチャージすればチャージした時点からさらに半年間有効。 SIM カードのパッケージの内側には、 海外 (香港でも 「於海外」 「Overseas」 って言うのか...) でのチャージ方法として A. 自動チャージ (香港での手続きが必要) B. バウチャー (香港で事前に購入が必要) C. PPS の三種類があると書いてある。 PPS (Payment by Phone Service [...]]]></description>
			<content:encoded><![CDATA[<p>
あけましておめでとうございます。今年もよろしくお願いします。
</p>
<p>
<a href="http://www.hkcsl.com/">香港 CSL</a> の
<a href="http://www.gcd.org/blog/2009/12/184/">Power Prepaid SIM $88 を買った</a> (HK$88 = 約1060円)。
この SIM カードを妻の携帯電話
<a href="http://www.gcd.org/blog/2008/12/166/">Motorola ZN200</a>
に入れて、
一週間の香港滞在中、
互いに連絡するときに大いに利用した。
が、通話料が一分間 HK$0.1 (約 1円) と安すぎるので、
HK$82.20 も残ってしまった (つまり HK$5.80 しか使ってない)。
</p>
<a href="http://www.gcd.org/sengoku/picts/CSL_PowerPrepaidSIM$88.jpg"><img src="http://www.gcd.org/sengoku/picts/CSL_PowerPrepaidSIM$88.jpg" width="390" height="268" alt="Power Prepaid SIM $88" /></a>
<p>
有効期限は 2010年6月15日までの半年間。
期限内にチャージすればチャージした時点からさらに半年間有効。
SIM カードのパッケージの内側には、
海外 (香港でも 「於海外」 「Overseas」 って言うのか...)
でのチャージ方法として
A. 自動チャージ (香港での手続きが必要)
B. バウチャー (香港で事前に購入が必要)
C. PPS
の三種類があると書いてある。
</p>
<a href="http://www.gcd.org/sengoku/picts/CSL_PowerPrepaidSIM$88_.jpg"><img src="http://www.gcd.org/sengoku/picts/CSL_PowerPrepaidSIM$88_.jpg" width="390" height="268" alt="Power Prepaid SIM $88 inside" /></a>
<p>
PPS (Payment by Phone Service ?) というと、
電話をかけて支払う方法だとばかり思っていたが、
インターネット経由でも可能であるようだ
(「by Phone Service」 と言いながらインターネットでも可能とはこれ如何?)。
<a href="https://ecs.hkcsl.com/nextg/pages/CreditCardInfoInputPage.jsp?language=en">Top up Your Account</a> ページで、
チャージ金額 (HK$50 ～ HK$300)、SIMカードの電話番号、クレジットカード番号
を入力することでチャージできそう。
したがって期限切れ直前にチャージすることにより次回 (たぶん年末に)
香港に行くときに使うこともできそうだが、
一年間使わずに寝かせておくよりはと思い、
<a href="http://page13.auctions.yahoo.co.jp/jp/auction/r61184677">Yahoo!オークションに出品してみた</a>。
</p>
<p>
この SIM カードは日本でも、
3G 対応の携帯電話なら使用できる
(<a href="http://www.gcd.org/blog/2008/12/166/">Motorola ZN200</a>
は GSM 専用機なので日本では使えない)
ので、
<a href="http://www.htc.com/jp/product.aspx?id=24006">HTC P3600</a> に入れて試しに少し使ってみた
(正確に言うと、
課金される実験は HK$1000 Prepaid SIM を使用したので、
Power Prepaid SIM $88 の残度数は減らしていない。
どちらの SIM も機能的には同じ)。
</p>
<p>
まず、
着信は普通に可能。
日本から 「010-852-6714-XXXX」 (「010」 は日本において国際電話をかけるときの国際プレフィックス、 「852」 は香港の国番号、 「6714-XXXX」 は、この SIM カードに割り当てられた香港内での電話番号) にダイヤルすれば、
この SIM カードを入れた電話機が日本にあっても着信する (Roaming Service)。
ただし 30秒ごとに HK$25 かかる
(<a href="http://one2free.hkcsl.com/jsp/prepaid_sim_card/useful_information/pdf/roamser.pdf">国によって HK$8 ～ HK$25</a>。
深&#x5733;とかなら HK$8)。
</p>
<p>
発信は、
中国以外だと直接発信ができないのでコールバックしてもらう方式。
つまり 「*108*」 に続けて相手先の電話番号を香港からかける形で押して、
最後に 「#」 を押した上で発信。
例えば東京都の固定電話 03-5771-1328 にかけるのであれば、
「*108*00181357711328#」 とダイヤル。
</p>
<p>
すると香港から電話がかかってくる (コールバック) ので、
出ると中国語で何やら言ったあと、
相手先 001 81 3 5771 1328
(001 は香港において国際電話をかけるときの国際プレフィックス、
81 は日本の国番号、
3 は東京の市外局番03) を呼び出してくれる。
相手先が出ればそのまま通話できる。
つまり日本国内宛に電話をかける場合も、
香港から日本への国際電話 2本使って通話することになる。
なお、
「*108*」 の代りに 「*108*1*」 にすると、
コールバックの時の発声を中国語のかわりに英語で言ってくれるらしい
(試すとまた課金されてしまうので未確認)。
</p>
<p>
コールバックに応えた時点で、
相手先につながるか否かにかかわらず課金される。
相手先が出てくれなくても 30秒ごとに HK$25 も課金されてしまうので注意が必要。
試しに私の PHS に前述のダイヤル方法 (*108*00181705013YYYY#) でかけてみた。
すると 「通知不可能」 で PHS が鳴った。
ちょっと鳴らしてみただけなのだが、
通話時間 (コールバックに応えてから切断するまで) としては 19秒もかかり、
HK$6.5 もかかった (香港滞在一週間で使った度数より大きい! *_*)。
数秒鳴らすだけで、
香港内同士 (local call HK$0.1/min)
なら 1時間分以上の通話に匹敵する課金なのだから恐れ入る。
</p>
<p>
というわけで通話の Roaming Service は高すぎて実用的ではない
(国際ローミングなんてどこもそんなもの?) が、
日本にいながら香港の番号での着発信の確認をできる意味はあるかも
(香港に行く前から電話番号を周囲に伝えておく場合など)。
</p>
<p>
一方 SMS (Short Message Service) は、
一通 HK$3 なので香港内同士 (一通 HK$0.8) に比べてさほど割高ではない
(そもそも local で一通 10円というのが高いと思うけど)。
しかも、
受信側は香港内外問わず無料でメッセージを受け取ることができる。
日本ではあまり一般的ではない SMS  だが、
日本以外だととても普及している。
SMS は通信キャリア・国を越えて利用できる共通基盤になっているのに、
日本の通信キャリアはいまだに<a href="http://itpro.nikkeibp.co.jp/article/COLUMN/20090928/337937/">事業者間接続でモメているようだ</a>。
よく知られているように、
twitter の文字数制限が 140文字なのは、
SMS の制限が 140オクテットであることを踏襲している。
</p>
<blockquote>
日本を除くほぼ全世界で共通のテキスト・メッセージサービスとして定着した。
現在の全世界のSMSの利用者は約24億人で、
世界中の携帯電話ユーザーの約74％がSMSを利用している。
<div align="right">「<a href="http://ja.wikipedia.org/wiki/%E3%82%B7%E3%83%A7%E3%83%BC%E3%83%88%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9">ショートメッセージサービス</a>」から引用</div>
</blockquote>
<p>
まず SMS の着信を試してみる。
Skype で SMS を 「+852-6714-XXXX」 に送ってみる
(&euro;0.051 = 約8円かかった) と、
携帯電話が日本にあっても着信する。
日本語表示が可能な携帯電話 (HTC P3600) なので、
日本語を含むメッセージも問題なく表示できた。
念のため残度数が減ってないことを確認。
</p>
<blockquote>
「*109#」 にダイヤルする (もちろん無料) と、
次のような残度数を知らせるメッセージが、
差出人 「179179」 から SMS で届くので残度数を確認できる。<br />
<br />
6714XXXX with $82.20 stored-value. The expiry date is 15/Jun/2010.
</blockquote>
<p>
次に SMS の発信を試してみる。
別の SIM カード (HK$1000 Prepaid SIM) を HTC P3600 に入れて、
HTC P3600 の標準アプリケーション 「メール」 を使って
SMS を 「+8526714XXXX」 宛に送ってみた。
宛先欄に 「+」 記号を含めて入力することが重要。
残度数を調べると、
送信に HK$3 かかったことが確認できた。
続いて SIM カードを元の Power Prepaid SIM $88
(電話番号が 6714XXXX) に戻すと、
先ほど別 SIM カードで送った SMS が着信した
(SIM カード入れ替えを行なったのは、
手元に 3G 対応携帯電話が一台しかないため)。
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gcd.org/blog/2010/01/185/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
