仙石浩明の日記

このエントリーを含むブックマーク 2010年8月4日

HYBRID W-ZERO3 の解約に続き、ウィルコムADSL も解約した。さよならウィルコム!

事業破綻の報道等で 「顧客 (サービス利用者) の保護」 という言葉をみかけることが多いが、 言語明瞭意味不明ワードの一つだと思う:

PHS の解約が予想以上に進み、 2月に 417万人だった契約者数は 6月末には 388万人に減った。 7月下旬に予定していた裁判所への再建計画提出も、 「環境が変わった」として 10月に延期していた。
管財人らは、顧客を守るためにも、再建には通信会社の協力が必要と判断。 XGPを引き受けるソフトバンクに、PHS事業への支援に加わるよう求めていた。

「顧客を守る」 などと書くと聞こえはよいが、 PHS サービス継続を望む顧客がどれだけいるかなんて、 「PHS の解約が予想以上に進」んでいることから明らかなはず。 現在 388万人(も) 契約者数が残っていると言ったって、 その大半は 2年縛りのせいで辞めるに辞められない人たちであって (私も 6月末時点だと契約者なのでこの 388万人に含まれている)、 現顧客にとって望ましいのはサービス継続じゃなくて 2年縛りを免除してあげることだと思う。

「顧客」 というと 「債権者」 というイメージが強いし、 この報道のように 「顧客を守る」 と書くとますます 「債権者たる顧客を守れ!」 というイメージが強調されるけど、 実状は 「辞めたいけど契約の縛りで払い続けざるを得ない債務者たる顧客」 と 「辞めていく顧客から少しでも多くの資金を回収したい債権者」 という (報道から受けるイメージとは真逆の) 構図であるわけで、 報道がいかに実状をゆがめたイメージを撒き散らしているかの一例だと思う。

私は丸 4年 (なのでちょうど解約可能なタイミングだった)、 ウィルコムの PHS サービスを利用してきた。 また自宅のバックアップ回線として 4年間近くウィルコムADSL を利用してきた。 なぜウィルコムだったかと言えば、 2006年当時まだ珍しかった 「スマートフォン」 W-ZERO3[es] を使いたかったから。 4年前ドコモショップで、 スマートフォンが使いたいから解約すると伝えたとき、 ドコモも近いうちにスマートフォンを出す予定 (hTc Z) と担当者が言っていたのを思い出す。

その後、 W-ZERO3[es], Advanced W-ZERO3[es], HYBRID W-ZERO3 と 3代続けて W-ZERO3 シリーズを使い続けてきたが、 HYBRID W-ZERO3 のあまりのデキの悪さ (そもそも電話の着信音 / バイブレーションが小さすぎて着信に気付けないのだから電話として失格) に幻滅し、 乗り換えを決意した次第。 輝かしい W-ZERO3 の歴史の最後の最後で欠陥品を出してしまうとは (私は使ったことがないが一つ前の WILLCOM 03 も失敗作だった?)、 いったいシャープに何が起こったのか?

iPhone を使いたいがためにソフトバンクに乗り換える人が圧倒的である昨今、 いまさら言うまでもないことだが、 (2006年ごろから?) 通信サービスは端末のオマケに成り下がったとつくづく思う。 いや、 端末ではなくコンテンツのほうがもっと大事だと言う人がいるかもしれないが、 そうなるのはもう少し先 (少なくともあと 3年くらい先)、 端末がコモディティ化した後の話だと思う。 少なくとも現時点では、 どんなに立派で魅力的なコンテンツでも、 それをユーザに届ける窓口である 「端末」 がヘタレであれば何の意味もない。

一般に解約するのは骨が折れる。 多くの場合、解約は電話のみの受付だったり、 解約通知書を郵送しなければいけなかったりと、いろいろ手間がかかる。 Web だけで契約が済んでしまう入会の時とは対照的。 しかも解約の方法は Web を丹念に見ていかないと見つけられなかったりする。 退会率を下げたいという事業者側の気持ちも (職業がら) もちろん分かるのだが、 解約方法を分かりにくくすればするほどサポートコストは増えるしユーザ満足度も下がるわけで、 結局事業者自身の首を絞めることにしかなってないと思う。

特に、 ウィルコムADSL の解約は大変だったので以下にメモ:

(続きを読む...)
Filed under: その他 — hiroaki_sengoku @ 08:07
このエントリーを含むブックマーク 2010年7月26日

Nexus One で Android 2.2 froyo のマルチタッチを試してみる

Android は 2.1-update1 以降でマルチタッチ (Multi-touch) をサポートしている。 ところがマルチタッチといっても、 ピンチイン/ピンチアウトなどのジェスチャをサポートしているだけのアプリが大半で、 複数のタッチを独立に扱えるアプリはいまだほとんどなく、 iPhone と比べるとその差が際立っている。

どうして Android にはマルチタッチを活用したアプリケーションが無いのだろう? と思ったので、 マルチタッチを試すテストアプリ MultiTouch.java (apk) を書いてみた:

MultiTouch

タッチした位置にタッチの強さに応じた大きさの円を表示するだけの単純なアプリ。 指を移動すれば円も追随する。 Android ではタッチID が順に割り振られるので、 ID が 0 のタッチを赤色の円で、 ID が 1 のタッチを緑色の円で描いている。

プログラム上は ID が 2 のタッチを青色の円で描くことになっているが、 残念ながら現行の Android で同時に扱えるタッチは 2箇所のみ (追記: Samsung Galaxy S は 5箇所のマルチタッチが可能らしい) なので、 3箇所にタッチしても三つ目の円が描かれることはない。 だから例えば iPhone のアプリにあるような鍵盤楽器アプリを作ろうと思っても、 三つ以上の音を同時に鳴らすことはできない。

とはいえ、 2箇所のタッチを独立に扱えれば、 いろいろ応用が効くだろうにと思いつつ、 このテストアプリをいじっていると...

(続きを読む...)
Filed under: プログラミングと開発環境 — hiroaki_sengoku @ 08:57
このエントリーを含むブックマーク 2010年7月17日

Nexus One の近接センサ/環境光センサは、どこにあるのか?調べてみた

Nexus One など最近のスマートフォンには、 加速度 (Accelerometer)、 環境光 (照度, Ambient Light)、 磁場 (磁界, Magnetic Field)、 方位 (電子コンパス, Orientation)、 近接 (Proximity) など、 様々なセンサがついている。 いろいろ応用できそうで夢がふくらむが、 携帯電話本来の使い方 (つまり通話すること) において、 使い勝手に直接影響する重要なセンサが近接センサ。

Nexus One や iPhone など全面タッチパネルの携帯電話だと、 (受話器として使うために) 耳に近づけたときタッチパネルが反応しては困る。 そこで近接センサを使って顔が接近してくることを感知し、 タッチパネルを無効にする (ついでにディスプレイをオフにして消費電力を抑える)。

私は Proximity なんて聞くと、 Proximity Warning System (接近警報システム) を思い浮かべてしまうくらいで、 携帯電話用の近接センサがどういうしくみか全く知らなかった。 今年1月の Nexus One の発表の時に近接センサのことを初めて知り、 その時はタッチパネル全体への接近を感知する (静電容量の変化を検知して?) のかと想像したが、 後述するように Nexus One の近接センサはタッチパネルの左上にしかなく、 タッチパネルの下方への接近は感知できないことが分かった。

Nexus One のどこに近接センサが搭載されているか、 センサの感応範囲がどれくらいなのか、 私には見当もつかなかったし、 google で検索してもその手の情報は見つからなかったので、 近接センサが感知した値を表示するだけの簡単なプログラムを書いてみた。

実は、 私にとって初めての android アプリ (^^;)。 しかも、 ここ数年 Java から遠ざかっていたので、 久々に書く Java プログラムだったりする。

お膳立ては Android SDK が全てやってくれるので、 わずか 74行のプログラム。 まず SensorManager#getSensorList メソッドで、 PROXIMITY タイプのセンサを取得し (sensor)、

	sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
	List<Sensor> sensors
	    = sensorManager.getSensorList(Sensor.TYPE_PROXIMITY);
	Sensor sensor = sensors.get(0);

この sensor の値が変化したときなどにセンサの値を受け取るリスナ (SensorEventListener) を、 SensorManager#registerListener メソッドで登録するだけ。

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) {
    }
}

近接センサの値が変化するとリスナの onSensorChanged メソッドが呼び出されるので、 新しいセンサ値を描画する (view.update):

	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 < values.length; i++) {
		canvas.drawText(" "+values[i], 0, height * (i + 1), paint);
	    }
	    getHolder().unlockCanvasAndPost(canvas);
	}

Nexus One のタッチパネル左上隅近く (黒枠部分) にセンサがあるらしく、 パネルまで 2cm ほどの距離に物体を近づけると反応する (センサの値が 9.0 から 0.0 へ変化する)。 また、 パネルと並行に物体を動かす場合、 センサの真上から 1cm ほど外れると反応しなくなる。

Proximity Sensor  ←  鈴を近づけたことにより、
近接センサが反応して、
値が 0.0 になっている

赤外線型の近接センサ (赤外線を照射し、近接する物体からの反射光を測定するセンサ) なので、 凸面の物体など赤外線があさっての方向へ反射してしまって受光素子に正しく届かない場合や、 あるいは黒色の物体などあまり反射しない場合などでは、 より近づけないと反応しない。

例えば、 黒く細い丸棒などだと 1cm 以下に近づけないと反応しない。 逆に白い紙 (凹面〜平面) など、 効果的に赤外線を反射し、かつ受光素子に反射光が効率的に届くケースだと、 2cm より遠くても (8cm くらいでも) 反応する。

ちなみに、 Nexus One に搭載されている近接センサは、 Capella MicrosystemsCM3602 という、 環境光センサ付短距離近接センサ (Short Distance Proximity Sensor with Ambient Light Sensor) であるようだ。 名前の通り環境光も測定できる。 おそらく近接センサの受光素子をそのまま使って照度を測定しているのだろう。

前述したプログラムにおいて、 「Sensor.TYPE_PROXIMITY」 を 「Sensor.TYPE_LIGHT」 に置き換えれば、 環境光センサの値を読み取ることができる。

(続きを読む...)
Filed under: プログラミングと開発環境 — hiroaki_sengoku @ 08:55
このエントリーを含むブックマーク 2010年7月8日

Google カレンダーの過去の予定を自動的に削除する方法

ここ 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 3.5
2000-08 Palm m100 PalmOS 3.5.1
2002-09 Zaurus MI-E1 ZaurusOS
2002-12 Linux Zaurus SL-C700 Linux 2.4 Embedix
2003-06 Linux Zaurus SL-C750 Linux 2.4 Embedix
2006-07 W-ZERO3[es] Windows Mobile 5.0
2007-07 Advanced W-ZERO3[es] Windows Mobile 6 Classic
2008-12 HTC P3600 Windows Mobile 5.0
2010-01 HYBRID W-ZERO3 Windows Mobile 6.5 Professional
2010-04 Nexus One Android 2.2 froyo
2010-06 iPhone4 iOS 4

WorkPad 30J から HYBRID W-ZERO3 に至るまで、 全て PC とのデータ同期が基本だったし、 それぞれデータ移行ツールが用意されていたので移行は容易だった。 ところが Nexus One で同じような同期を行なうには、 データを PC ではなく Google Calendar へ置かなければならない。

もちろん、 Google Calendar は共有設定さえ行なわなければ他人に読まれることはないだろうし、 「don't be evil」 と言ってるくらいだから、 Google が勝手にユーザのデータを活用する可能性も無い (と信じたい)。

だからといって、 個人的なデータや会社の超機密事項 (議事メモにはそういった情報も含まれる) も洗いざらい Google に預けてしまう、 なんてことは小心な私にはとてもできない。 よく知られているように Google Calendar は 「限定公開 URL」 が漏れるだけで一巻の終わりであるわけで、 漏れることを前提でリスク評価すべき。

というわけで、 議事メモや日記を Google Calendar に置くことはハナからあきらめて、 Google Calendar には直近の予定だけを置くことにした。 万一漏れても、向こう一週間くらいの予定だけであれば、 致命的というほどでもない。

ところが驚いたことに Google Calendar には過去の予定を一括削除する機能がない。 手作業でいちいち消していかない限り、 データは残り続けるようだ。 当たり障りのない 「予定」 でも積み重なればいろいろ見えてくることがあるわけで、 長年にわたって溜った予定データは脅威となりうる。

どうしてこんな超基本機能が無いのだろうと思いつつ google で検索してみると、 見つかるのは 「どうやったら過去のデータを (一括) 消去できるのか?」 という質問のページばかり。 過去データを削除したい、 というニーズは確実にありそうなのに、 なぜ Google は実装しようとしないのか? そういえば gmail も過去のメールを溜め続けるのが基本だし、 Google Calendar に自動的に削除する機能がないのは意図的なのかもしれない。

無い機能は作ってしまえと、 Google Calendar API のドキュメントを眺めてみる。 API を叩くためのクライアントライブラリが用意されているようだ。 が、.NET とか Java とか Python とか PHP とか、 あまり気の進まない (^^;) 言語ばかりが並んでいる。 Perl は無いのかっと思って CPAN を検索したら、 Net::Google::Calendar があっさり見つかった。

マニュアル片手にテストプログラムを書いてみる:

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);
}

たったこれだけ。 最初に login($user, $pass) して、 get_events(%opts) で「予定」データを取り出して、 delete_entry($event) で削除。 get_events の引数で、 開始時刻が一週間以上過去の予定のみ取り出すよう指定している。

ちょっと書き足して、 ユーザID やパスワードを設定ファイルで設定できるようにしたスクリプト gcal_remove を作ってみた。

% gcal_remove -c /path/to/config.yaml -d 7

などと実行すると、 7日間以上前の予定を削除する。

設定ファイル config.yaml は以下のような感じ:

google_user: sengoku@gmail.com
google_passwd: xxxxxxxx
Filed under: プログラミングと開発環境 — hiroaki_sengoku @ 09:44
このエントリーを含むブックマーク 2010年6月17日

Linode から prgmr.com へ乗り換えてみた

今年 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 以上のプランは受付けていない) が、 逆に言えば Web インタフェースが不要な人 (含む私) にとっては prgmr.com のシンプルさがむしろ望ましく、 レスキュー用のブートイメージなど必要最低限のものは揃っているので、 不便と感じることはない。 また、 いったん申込みが完了してしまえば受付の一時停止は関係ないわけで、 むしろサーバリソースに見合ったユーザ数しか受付けない姿勢は好感が持てる。

Web から Signup すると、 以下のようなメールが届く。

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. 

OpenSSH の公開鍵くらい Web から登録させればいいのにと思いつつ、 「I need to know what distro you would like」 などと聞いてくるということは多少の融通は聞くのかと思い、 パーティションの切り方をついでにリクエストしてみた (6/4 15:46)。 つまり、 自分で作った Linux distro (配布していないから 「distribution」 と呼ぶのは正確性に欠けるのだが、 それなら何て呼んだらいいのだろう?) をインストールしたいから、 最小のパーティション (例えば 2GB) に ubuntu をインストールしておいてもらえるか? などとリクエストしてみた:

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 <sengoku@gcd.org>

すると、 4日後の 6/8 09:03 に返事が来た。 リクエストはあっさり無視されて、 ディスク一杯に ubuntu をインストールした状態の VPS が提供されただけ。 個別対応しないのであれば、 自動化したほうがいいような。

Your vps is setup now, your host server is whetstone.prgmr.com.
Login with your username and ssh key and follow the instructions
at http://book.xen.prgmr.com/mediawiki/index.php/Quickstart
For more information about repartitioning, see
http://book.xen.prgmr.com/mediawiki/index.php/Repartition
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

言われた通り ssh でログインしてみる:

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> 

「6. swap i386/amd64 bootloaders (pvgrub)」 が目を引くが、 PV-GRUB は DomainU 側で実行されるので、 PV-GRUB にて 32bit/64bit の両方のカーネルを立ち上げることができず、 PV-GRUB を実行する前にどちらか一方に決めておく必要がある、 ということのようだ。 デフォルトは (残念なことに) 32bit になっていて、 セットアップしてもらった ubuntu も 32bit 版だった。

「1」 を入力して VPS のコンソールを表示させてみる:

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:~# 

前掲のメールに書いてあった通り、 ユーザ 「root」 パスワード 「password」 でログインできた。 初めてログインしたのに 「Last login: Mon May 31 23:51:42 UTC 2010 on hvc0」 などと出ているが、 この ubuntu イメージを作ったとき試しに root でログインしたのが残ってしまったのだと思われる。 新規ユーザのセットアップのたびに、 5/31 に作ったイメージを使いまわしているのだろう。

ディスク 12GB のプランのはずなのに 1GB ほど足りないのは swap パーティションがあるから?と思い、 fdisk で確認してみる:

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:~# 

シリンダの最大値は 1566 なので、 72 (=1566-1494) シリンダが使われずに余っている。 試しにパーティションを割当ててみたら普通に使えた。

自前 distro 10GB, swap 1GB, ubuntu 1GB といった切り分けかたにするため、 レスキュー用のカーネル 「CentOS 5.5 rescue」 をブートしてみる:

    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.

二行目の 「CentOS 5.5 rescue (2.6.18-194.3.1.el5xen)」 を選択してブート。 root でログイン (パスワードは無し) して fdisk を使ってパーティションを切り直した。 せっかくセットアップしてもらった Ubuntu も、 3分と使わずにお別れ。

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 ~]# 

私は個人で管理している Linux マシン (自宅と職場と VPS 合わせて十数台, もちろん商用サービスを行なっているサーバではない) のディスクの内容を、 コマンド一発で同一に保てるようにしている (つまりマスタで変更/追加/削除したファイルを各マシンへコピー/各マシンで削除)。

もちろん、 各マシンの性能や 32bit/64bit などの CPU 種別、 ディスクの容量に応じて違う部分もあるわけで、 この同期コマンドは同期先の各マシンに合わせてコピーしないファイルのリストを生成した上で rsync を実行している。 なので、 新しくマシンを増やすときもマスタ上で同期コマンドを実行するだけで、 インストールが完了するはずだが...

(続きを読む...)
Filed under: システム構築・運用 — hiroaki_sengoku @ 09:44
このエントリーを含むブックマーク 2010年5月26日

サイボウズ オフィス8 のカレンダーを iCalendar 形式に変換するスクリプトを書いてみた

私の職場ではグループウェアとしてサイボウズを利用している。 自分のスケジュールを iCalendar 形式で取得してみたくなった (Nexus One を買うとそういう気分になる ^^;) ので、 google で検索してみたところ次の二つのスクリプトが見つかった:

あいにく職場のサイボウズはオフィス8 なので、 オフィス8 に未対応の前者は使えない。 後者はオフィス8 用だが、 「暫定版」 と書いてある通りいろいろバグがある。 さくっと修正してみて一応それっぽく動かすことはできたのだが、 使い続けていくとなると一から書き直したほうがいいような気がしてきた (わたし的にはこの手のものを PHP スクリプトでは書きたくない) ので、 perl で書き直してみた

この perl スクリプト cybozu8_ical を、

% cybozu8_ical --conf /path/to/config.yaml

などと実行すると、 オフィス8 の 「月予定」 (月間スケジュール) ページをアクセスして、 iCalendar 形式に変換する。 向こう一週間以内の予定については 「予定の詳細」 ページもアクセスして、 「メモ」 および 「設備」 も取得する。 「月予定」 の全ての予定について 「予定の詳細」をアクセスすると、 オフィス8 サーバに負荷をかけすぎる懸念があったので、 このような仕様にしてみた。

設定ファイル config.yaml は以下のような感じ:

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

「cybozu_url」 はオフィス8 の URL を指定する (もちろん intra.klab.org は KLab 社内LAN からしかアクセスできない)。 「userid」 と 「password」 はスケジュールを取得したいユーザの ID とパスワード。 「output_file」 に指定したファイルへ iCalendar 形式のスケジュールを出力する。 「output_file」 を省略すると標準出力へ書き出す。

なお 「--conf /path/to/config.yaml」 オプションを省略すると、 cybozu8_ical と同じディレクトリにある config.yaml が読み込まれる。

cybozu8_ical で生成した iCalendar 形式のファイルは、 とりあえず Google カレンダー および Thunderbird + Lightning で読み込めることは確認したが、 なにぶんまだ RFC 2445 を真面目に読んでいないので、 不具合などあったらご指摘頂けると有難い。

私はオフィス8 の API を知らないので、 「月予定」 「予定の詳細」 のページを取得してきて scrape しているだけ。 「繰り返し予定」 は個々の予定として扱っている。 iCalendar の UID (各予定固有のID) が同じままだと 2個目以降の予定が無視されてしまうので、 UID の末尾に通し番号をつけて互いに区別できるようにしている。

サイボウズのスケジュールは 「場所」 を登録できない。 その代わり 「施設」 として会議室を登録する。 ところが私の職場の場合 「施設」 には会議室だけでなく 「ビデオ会議システム」 などもあったりするので、 「施設」 に登録されているデータをそのまま iCalendar の LOCATION として使うわけにもいかない。 そこで 「施設」 に会議室の名称が登録されている場合のみ、 その会議室名を LOCATION として出力するようにしている。

iCalendar の LOCATION として出力すべき会議室名の一覧を、 cybozu8_ical スクリプトの始めの方で、

my @facility = (
	'20F 大会議室1', '20F 大会議室2', '20F 大会議室3',
	'20F 中会議室1', '20F 中会議室2',
	'20F 小会議室1', '20F 小会議室2',
	'20F 和室', '22F 社長室前MTGスペ-ス'
);

などと定義している。 ここに列挙されていない会議室等は 「施設」 に登録されていても単に無視される。

Filed under: プログラミングと開発環境 — hiroaki_sengoku @ 16:01
このエントリーを含むブックマーク 2010年5月3日

米国のプリペイド SIM カードで Nexus One を使ってみた

私は米国ではプリペイド SIM カードを購入して携帯電話を使っている。 AT&T の Pay As You Go $1 Mobile to Mobile Plan は、 携帯電話同士の通話が 1日 1ドルで使い放題になるので便利。 一方データ通信は、 米国だとホテルをはじめ多くの場所で無線LAN が使えるので、 今までは必要性をあまり感じなかったのだが、 今回のハワイ滞在で Nexus One を入手したら、 携帯電話でもデータ通信をしてみたくなってしまった。

日本でも HTC Desire が発売された今となっては 「いまさら」 感もあるが、 SIM ロック フリーな Nexus One を入手する意味はあると思い、 渡米前に Web サイトで Nexus One を購入し (日本からは、 このサイトへアクセスできないと書いてあるページを見かけるが、 トップページ以外は普通にアクセスできるような...?)、 配送先として滞在する予定のホテルを指定しておいた (配送先に日本の住所は指定できない)。 ただし米国のホテルはテロを警戒して、 非滞在者宛の荷物の受け取りを (事前連絡しても) 拒否するそうなので、 購入手続きは出発直前に行なった。
日本を発つ日 (フライトは晩の 20:50) の早朝 4:57 JST (12:57 PDT) に購入手続きを行なったら、 その翌日 0:02 JST (8:02 PDT) に発送され (FedEx overnight)、 ハワイ到着日の翌日昼間 13:30 HAST にホテルに配達された (日本時間でいうと購入日の 2日後の朝 8:30 JST)。 出発日の前日に購入手続きを行なっていれば到着日に受け取れたかもしれないが、 ホテルにチェックイン (12:00 HAST) する前に配達されるとマズイかも?

ホテルのフロントで Nexus One の小包を受け取って、 SIM を入れて電源 ON したら、 勝手に 3G (W-CDMA) でデータ通信を始めてしまった (当たり前 ^^;)。 Pay As You Go (AT&T のプリペイドSIM) は、 Data Package を追加購入せずにデータ通信を行なうと、 1KB あたり 1セントも課金されるので一瞬で (例えば、わずか 10MB で約1万円) パケ死してしまう。 慌てて無線LAN に切り替えたが既に 87セント課金されていた。 さらに、 位置情報を取得するために 1セント (基地局ID の取得?)、 無線LAN 経由だと google アカウントの同期が行なえなかった (ホテルの LAN の問題?) ので 3G でつなぎ直したら 29セントかかった。 この時点で残度数 $22.73 (SIM カード利用開始時は $25)。

Pay As You Go Online で、 「Login」 ボタンの上にある 「Forgot your password?」 をクリックすると 「Request Your Password」 ページに遷移するので、 AT&T プリペイド SIM の携帯電話番号を入力して 「Send Password」 ボタンを押す。 すると、

1111509064: S: Your account status has changed.
ATT Free Msg: Your password has been changed to 8181.

などといった SMS (ショートメッセージサービス) が送られてくる。 このメッセージ中に書いてある password を使ってログインし、 任意の password に設定し直せばよい。 いったんログインできるようになれば、 米国内外を問わず Web 上で度数の追加 (refill) や追加パッケージ (feature packages) の購入が可能。

追加パッケージには、 Unlimited Talk & 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) のいずれかを選べる。

Nexus One をいじっていたらすぐ 100MB くらいはいくだろうと思い 100MB を購入した。 実際、 google map をナビ代わりに使う (海外旅行ではすごく便利!) と、 20MB くらいはすぐ使ってしまう。 また google アカウントとの自動同期を行なうと、 何もしなくても一日 1MB くらいは使ってしまうようだ。 あっというまに 100MB を使い切りそうなので、 Data Package 100MB を追加購入することにした。

追加パッケージは、 Credit/Debit カードで支払うこともできるが、 SIM カードの残度数で払うこともできる。 つまり、 いったん度数を購入 (refill) してから、 その度数で追加パッケージを購入するということができる。 なんでそんな回りくどいことを?と思うかも知れないが、 refill だと SIM カードの有効期限を延長できるという違いがある。 例えば $100 refill すると有効期限が 1年後になる ($25〜$75 だと 90日後、$15 だと 30日後)。

有効期限は追加できないので、 例えば有効期限が 3ヶ月後の場合 (新規に SIM カードを購入した場合など) に、 $100 refill したからといって有効期限が 15ヶ月後 (=3ヶ月+1年) になるわけではなく、 refill した時点の 1年後が有効期限になる。 有効期限はもちろん短くはならない。 例えば $15 refill だと有効期限は 30日後だが、 refill 以前の有効期限が 3ヶ月後なら、 3ヶ月後のままで変わらない。
一方、 refill 以前の度数も新しい期限まで有効期限が延長される。 つまり有効期限に達する前に refill し続ければ、 度数を失効させずに済む。 ただし一枚の SIM カードに溜められる度数は $500 が上限。

というわけで、 最初に購入した Data Package 100MB の残りがわずかになった段階で、 $100 refill し、 その度数から $19.99 を使って Data Package 100MB を追加購入した。

Filed under: Hawaii — hiroaki_sengoku @ 18:04
このエントリーを含むブックマーク 2010年4月20日

Linux サーバ同士をシリアルケーブルでつなぐには寡黙な getty が必要

私の自宅 LAN (GCD) の対外ルータである 2台の Linux サーバ (senriasao) は、 双方のシリアルポート (つまり DSUB9ピンの RS-232C) 同士がつながっている。 いまさら RS-232C ? と思う人が大半だと思うが、 最後の命綱としての安心感は測り知れない。

つまりサーバをリモートから (ネットワーク経由で ssh ログインして) いじっていると、 ちょっとしたミスで操作不能に陥ってしまうことがある。 例えば NIC を down してしまった、 iptables で DROP する設定にしてしまった、 sshd を殺してしまった、 等々。 あるいは、 リモートから再起動を行なう場合、 起動スクリプトのほんの些細な設定ミスで、 リモートからは操作不能になってしまう。

こんなとき、 2台のサーバのシリアルポート同士を RS-232C クロスケーブル (シリアルケーブル) で接続しておけば、 生きている方のサーバからシリアルケーブル経由でログインできて、 リモートからは操作不能に陥ったサーバのネットワーク設定を修復したり、 sshd を起動し直したり、 あるいは再起動の場合であれば、 GRUB を操作することができる (ML115 などであれば BIOS 設定までいじれる)。

職場のサーバ群は、 もちろん IPMI を利用していて大抵の操作は IPMI でできてしまうのでシリアルケーブルの出番はあまりないのだが、 それでもやはり最後の命綱がある安心感は何ものにも代えがたい。 しかも数百円のケーブル一本で済むのだから、 掛けておくべき保険だろう。

シリアルケーブル経由でサーバにログインするには、 そのサーバの /dev/ttyS0 (シリアルポート COM1 の場合) で getty を動かしておく必要がある。 例えば GCD の場合 mgetty を使っているので、 /etc/inittab に

# Dialin lines
s0:25:respawn:/usr/sbin/mgetty ttyS0

などと設定している。 シリアルケーブルでつながった 2台の Linux サーバ senri と asao において、 asao から senri へシリアルケーブル経由でアクセスすると、 senri のシリアルコンソールが表示される:

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 # 

たとえ senri のネットワーク機能が全滅していたとしても、 mgetty は /sbin/init から直接起動されるので影響を受けない。 シリアルコンソールから root でログインして復旧作業を行なうことができる。 逆に、 asao のネットワーク機能が全滅したときは、 senri へ ssh ログインした上で asao へシリアルケーブル経由でアクセスすればよい。 ネットワーク機能が麻痺する可能性があるような危険な作業を senri と asao の両方で同時に行なったりしないようにすれば、 何が起きてもどちらかは生きていることが保証できる。

さて、 両サーバで mgetty が動いているときは以上で問題無いのだが、 どちらかのサーバを再起動するときなど、 mgetty 以外のものが動くときは状況が変わってくる。

(続きを読む...)
Filed under: システム構築・運用 — hiroaki_sengoku @ 07:48
このエントリーを含むブックマーク 2010年4月2日

「無知の無知」への 3ステップ

KLab(株)は、 2007年から新卒採用を行っています。
今年も例年通り、4月1日に入社式を行いました。

- o -

みなさん、入社おめでとうございます。 社長のお話が終わり、みなさんは挨拶が終わって、 緊張もほぐれてきたのではないかと思います。 ここからは各役員の挨拶なのですが、 なぜか私だけは挨拶だけでなく (長くならない範囲で ^^;) 話もしろということになったらしいので、 話を捻り出してみます。

「考えることが大事」ということは社長のお話にもありましたし、 みなさんも既によく知っていることだと思います。 ただ、 知っていることと実際に実行できることとの間には越えがたい壁があることも事実で、 分かっちゃいるけどついつい考えなくなってしまう、 あるいは考えているつもりが、 いつのまにか深く考えることがなくなってしまうものなのでしょう。 そもそも自分がどのくらい考えているかなんて、 なかなか意識するのは難しいですよね。

入社時にはみんな等しく、 これからは「考える習慣」を身につけよう、 と決意を新たにしたはずなのに、 3年で大きな差がついてしまうのが現実です。 考える習慣を身につけた人がどんどん成長するのに対し、 考えることを習慣化し損なった人は停滞したままなので、 その後も差は広がる一方。 10年 20年もたつととんでもない差になってしまいます。

なぜこんなにも差がついてしまうのでしょうか? ただやみくもに 「考えよう」 としても、 具体的に何をすればいいのかよく分かりませんよね? なので逆に何をしてはいけないかについて今日はお話ししたいと思います。 今日からみっちり 2ヶ月間、 新人研修でいろいろなことを学んでいくみなさんにとって、 「何をしてはいけないか」 を押えておくことはきっと役に立つと思います。

私は仕事柄、 面接をする機会が多いのですが、 お会いする人の中には考える習慣が全くない人もいます。 ご本人にはそういう自覚が全く無く、 人並みには考えていると思っているようなのですが、 私から見ると正に 「人生オワタ」 状態で、 なぜそうなってしまったのだろうかと考えているうちに、 ついついその人の身の上を根掘り葉掘り聞いて人生相談モードへ入ってしまうので、 そういうことを繰り返すうちに、 だんだん原因が見えてきたように感じています。

考える習慣を持たない人に共通する問題点として 「無知の知」 ならぬ 「無知の無知」 があります。 自分が分かってないことを分かってない、 自分の浅慮では到底及ばない領域があることが全く想像できない、 いわば 「井の中の蛙」 状態ですね。 「無知の無知」 状態になってしまうと、 今の自分の考え方で満足してしまい、 今の自分の枠を越えて考えてみようという発想がでてきません。 当然、 考える習慣もなくなってしまうわけです。 なぜこうなってしまうのか? 次の 3つのステップを経て 「無知の無知」 状態に至るケースが多いように思います:

  1. 後で調べればいいや
  2. 何が分からないのか分からない
  3. 分かったつもり

「質問する前に考えろ」 「それくらい Google 検索などを使って調べろ」 「ググれカス」 などと言われるようになったのはいつのころからでしょうか? 確かに何でもかんでも 「教えて君」 では困りますが、 「すぐ質問すること」 = 「悪いこと」 とする風潮はいかがなものかと思います。 こういう風潮が、 「聞くのは恥ずかしい」 「後で調べればいいや」 という意識を生んでいないでしょうか。

ちなみに私は全く正反対で、 「分からないと言える」 = 「カッコイイ」 と中学生の頃から思い込んでるので、 今でも堂々と 「分からない」 を連発しています。 もちろん、 「CTO がこんなことも知らないのか?」 と思われる (^^;) リスクも無いわけではないので、 TPO を考慮せざるを得ないこともあるのがちょっと残念です。

確かに本当に後で調べるのならまだいいのですが大抵は忘れてしまいます。 仮に忘れずに後で調べて言葉の意味を知ったとしても時間は遡れません。 その場で意味を聞いていれば相手と有益な議論を展開できたのかもしれないのに、 その機会は永久に失われてしまうわけです。

そして何より怖いのは、 知らない言葉が出てきても何とも思わなくなってしまうことです。 ふつう、 相手の話の中に意味が取りにくい言葉があれば気持ち悪く感じ、 なんとかその気持ち悪さを解消しようと思うものだと思うのですが、 「後で調べればいいや」 と思ってるとだんだんこの 「気持ち悪さ」 が無くなってきます。 この 「不感症」 が最初のステップです。

そして一つの話の中に知らない言葉がいくつも出てきても、 何とも思わない状態になると次のステップが見えてきます。 知らない言葉が複数出てくれば、 当然話全体が分からなくなるし、 そもそも話自体に興味が持てなくなります。 こうなってしまうと、 「何が分からないか分からない」 状態に陥ります。 いわゆる 「質問したくても何を質問したらいいのか分からない」 状態ですね。

質問しようとしない人に、 「もっと質問しようよ」 と言うと返ってくる言葉が、この 「何を質問すればいいのか分からない」 です。 話を興味を持って聞くということが無くなってしまっているために、 相手が話していない部分を聞き出そうという意欲が失われてしまっています。 もちろん、 人の興味は十人十色ですから、 ある話題に興味が持てないというのは普通でしょう。 問題なのは、 どんな話であっても興味が持てないことにあります。 しかも本人的にはそれが普通な状態なので、 他人の話を興味を持って聞くということがどんな体験だったのか、 もう覚えていなかったりします。 この 「無関心・無気力」 が第2 のステップとなります。

この第2 のステップの怖いところは自覚症状があまり無いことにあります。 自分は 「無気力」 ではない、 と思う人が大半だとは思いますが、 どんな話を聞いても、 質問したいことが思い浮かばないようだと 「第2 のステップ」 がかなり進行中であるわけで要注意です。 なんとか気付いて危機感を持って欲しいですし、 私はことある毎に 「質問しろ〜」 と口を酸っぱくして言ってるのですが、 長年染み付いた習慣というのはなかなか克服できないものなのかも知れません。

第2 のステップは 「分からない」 ということ自体は自覚できているのでまだ救いがあるのですが、 この症状が進むと第3 のステップである 「分かったつもり」 に至ります。 「分からない」 という意識さえあれば何かのきっかけで 「分かろうとする意欲」 が芽生えるかもしれないのですが、 分かっていないこと自体が分からなくなると、 その可能性すら無くなってしまうので末期的です。

何を聞いても 「分かったつもり」 になってしまって、 表面的な理解だけで満足するようになってしまうと、 自分の考え方と相手の考え方が異なっていてもそれに気付かない、 ということが起り得ます。 つまり相手の話を自分の思考の枠内だけで理解してしまうわけです。 まさに 「無知の無知」 です。

と、 いうわけで今日からの研修では決して疑問点を残さないよう、 積極的に質問してください。 また、 「分かった」 と思っても本当に理解しているのか、 今一度自分を疑ってみてください。

例えば、 半年前に内定式で渋滞の話をしました。 忘れてしまった人は私のブログを参照してください。 社会人経験がまだ無いみなさんには、 あの話を完全に理解することはなかなか難しいのではないかと思いますが、 「分からない」 という感覚を持っていますか? そして分からないとすればどこが分からないのか考え、 何を質問すれば自分の理解を深められるか是非考えてみてください。

まだまだ自分は 「分かっていない」 と自分を疑うことこそが、 考えを深めていくために必要なのだと思います。 KLab への入社が、 みなさんの人生において飛躍のきっかけとなることを期待します。

Filed under: 仙石浩明CTO の日記,自己啓発 — hiroaki_sengoku @ 13:19
このエントリーを含むブックマーク 2010年3月8日

tinydns のゾーンを MyDNS へ反映させるスクリプトを書いてみた

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 を利用している。 せっかく海外にサーバ fremont.gcd.org を借りたのだから、 tinydns で管理しているゾーンも fremont.gcd.org で引けるようにしたいが、 fremont に複数 IP アドレスを付与するのはお金がかかる (月額 $1 追加) ので、 fremont で MyDNS と tinydns の両方を走らせるわけにもいかない。

私が借りてるレンタルサーバ (正確に言うと VPS) Linode は、 もともと DNS サービス (マスタ/スレーヴどちらでも、ドメインいくつでも) を無料で利用できるので、 DNS のためだけに 1IP 追加する気にはちょっとなれない。

もちろん、 tinydns を止めてしまって全てのゾーンを MyDNS へ移行してしまうという解決策も無くはないのだが、 MyDNS に全面的に依存してしまうのは恐い気もする。 できれば tinydns 側はいじらずに、 tinydns のゾーンデータを MyDNS 側へ反映させる仕掛けを作りたい。

というわけで、 tinydns のゾーンデータを読み込み、 MyDNS のゾーンデータの形式で MySQL へレコードを書込む perl スクリプト tinydns2mydns を書いてみた (CVSリポジトリ)。

なお MyDNS には、 mydnsimport という外部からゾーンデータを取り込むツールが付属していて、 tinydns のゾーンデータもインポートできるのだが、 以下の欠陥があってゾーン転送目的には使えない:

  1. ゾーンの全レコードをいったん削除した上でインポートを行なう実装になっている。
  2. SRV レコードをインポートできない。
(続きを読む...)
Filed under: プログラミングと開発環境 — hiroaki_sengoku @ 09:25
このエントリーを含むブックマーク 2010年3月1日

x86_64 Linux などの 64bit 環境で MD5 を使うときの注意点

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 int」 のままで、 この MD5 リファレンス実装を使ってしまうと、 32bit であるべき UINT4 型が 64bit になってしまい、 間違ったハッシュ値を算出してしまう。

16bit CPU が主流だった大昔なら 「int が 16bit なデータモデルを採用している環境」 が多かったのかもしれないが、 RFC 1321 が出た 1992年ごろは既に 32bit CPU が主流だったわけで、 UINT4 型を 「int」 と定義しておいてくれてもよかったのにと思う。 そうすれば、 「long が 64bit なデータモデルを採用している環境」 が多くなる昨今でも (int は 32bit のままなので) 問題を起こさずに済んだだろうに。

試しにテストプログラムを書いてみる:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#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(&context);
    while ((i=read(0, buf, BUFFER_LEN)) > 0) MD5Update(&context, buf, i);
    MD5Final(digest, &context);
    for (i=0; i < DIGEST_LEN; i++) printf("%02x", digest[i]);
    printf("\n");
    return 0;
}

32bit 環境 (i686 Linux) では正しく動く:

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

ところが、 64bit 環境 (x86_64 Linux) だと:

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

などと、 誤った MD5 の値が出力されてしまう。

MD5 の値を求めること自体が目的であれば、 誤ったハッシュ値が出力されればすぐ気付くのでいいのだが、 値そのものが目的であることは (当然ながら) あまりなくて、 普通はアプリケーションの中で MD5 を利用するので、 32bit 環境で使っていたアプリケーションを 64bit 環境でコンパイルし直して使おうとするとハマる。

(続きを読む...)
Filed under: プログラミングと開発環境 — hiroaki_sengoku @ 09:34
このエントリーを含むブックマーク 2010年2月25日

本ブログのサーバを海外レンタルサーバ Linode を使って冗長化・地域分散してみた

前回前々回に書いたように、 このブログ 「仙石浩明の日記」 (および 「仙石浩明CTO の日記」) は、 私の自宅にある PC サーバで動かしている。 2台の PC からなる冗長構成になっていて、 10年以上の安定稼働実績がある (両サーバ共に停止したことは皆無) が、 回線が (家庭用の) フレッツ光ネクストなので、 インターネットとの接続が切れてしまう可能性は皆無ではない。 実際、 Bフレッツからフレッツ光ネクストへ切り替えたときは 40分間ほど切れてしまった。 一応バックアップ回線として ADSL 回線も契約しているが、 こちらは固定 IP アドレスを割当てていないので WWW サービス用としては使いにくい。

短時間とはいえブログが見えないようなことがあると、 たまたまそのタイミングで (検索エンジン等で見つけて) 訪れた人が、 ブログが既に閉鎖してしまったものと勘違いしてしまうかもしれない。 ここは一つ自宅以外の場所にもサーバを立てて、 回線断の影響を受けないようにしたいところ。 海外のサーバなら地域分散もできてなおよい。

道楽で運営している自宅サーバにそこまで可用性を求めるのは、 やりすぎの感もあるが、 近頃流行りの VPS (Virtual Private Server, 仮想専有サーバ) は、 月額 $20 (初期費用無し) 程度で利用できるらしい。 $20 なら試しに使ってみるのも悪くない。

サービス
プラン名
メモリ
ディスク
帯域
追加額/単位
月額
年額
データセンタの
場所
ServerAxis Xen
VS000.5G-0025.0GP
512MB
25GB
0GB
$.05/GB
$15
$180
Chicago, IL
Linode
Xen 360
360MB
16GB
200GB
$10/100GB
$19.95
$215.46
Fremont, CA
Dallas, TX ...
VPSLink
Xen VPS Link-3
256MB
10GB
300GB
$.50/GB
$19.95
$201.12
Seattle, WA
New York, NY
slicehost
Xen VPS 256 slice
256MB
10GB
150GB
$.30/GB
$20
$216
Dallas, TX
St. Louis, MO

VPSLink 以外は最安のプランで比較した。 VPSLink には、 メモリ 64MB で月額 $7.95 の Link-1 プラン、 メモリ 128MB で月額 $13.95 の Link-2 プランもあるが、 256MB 未満のメモリではできることも限られてしまうので比較対象から外した。

月額基本料だけを比較すると、 ServerAxis が格安に見えるが、 これは帯域課金が含まれていないためで、 例えば 100GB (送受信の合計バイト数) 使うと $5 加算されて $20 と他社並になる。

データセンタの場所 (というかネットワーク上での位置) によって遅延時間が変わってくるので、 どこのデータセンタを使っているかも重要。 例えば同じ Linode でもデータセンタによって、 日本からアクセスしたときの RTT (Round Trip Time, 往復にかかる時間, ミリ秒) が倍以上変わってくる。 それぞれ 10回ずつ ping を打って、 RTT の最小/平均/最大を比較してみる:

データセンタの場所ホスト名 最小平均最大
Fremont, Californiafremont1.linode.com 121.341132.299146.939
Dallas, Texasdallas1.linode.com 171.049173.152174.905
Atlanta, Georgiaatlanta1.linode.com 183.184185.938190.174
Newark, New Jerseynewark1.linode.com 200.629203.053204.755
London, Englandlondon1.linode.com 269.324273.499276.822

米国西海岸だと RTT は 130msec 程度で済むが、 東海岸は 200msec を超えてしまい、 英国はもっと遠い。 西海岸にデータセンタがあるのは Linode と VPSLink だが、 Linode がデータセンタの情報を詳細に公開しているのに対し、 VPSLink は、 Spry Hosting のシアトルのデータセンタを使用している、 ということ以上の情報を公開していない (しかもこのページは 2006年8月の内容のまま) のが少し気になる。

サポートしている Linux ディストリビューションは、 各社だいたい共通で、 CentOS, Debian, Gentoo, Ubuntu の各バージョンが利用できるが、 VPSLink Ubuntu Plan のページに Ubuntu 9.10 Karmic が書かれていない点も、 ちょっと気になった (ちゃんとポリシーを持って 9.10 をサポートしないのならいいのだが、 どうもそうではなく単に運用の手を抜いているだけのような気配がする)。 Web 界隈での評判も Linode のほうがよさげだったので、 Linode を使ってみることに決めた。

サポートしているディストリビューションのバージョンが古くてもアップグレードできるのだろうが、 帯域で課金されるので最初から新しいバージョンをインストールできる方が好ましい。

サービス CentOSDebianGentooUbuntu Fedoraその他
ServerAxis 5.45.02008.09.10- Mandriva SUSE
Linode 5.35.02008.09.1011 Arch SUSE Slack
VPSLink 55O9.0411 Arch Slack
slicehost 5.45.02008.09.1012 Arch RedHat

おそらく各社ともカーネルも入れ替えることができるのだと思うが、 Linode 以外は使っていないので未確認。 Linode の場合は pv_ops (paravirtualization) を有効にしておけば任意のカーネルを利用できる。 したがって (帯域課金さえ気にしなければ ;-) 任意のディストリビューションの利用 (あるいは完全なカスタマイズ) が可能 (私はまだそこまではやっていない)。

なお、VPS サービスは探すといろいろあるようだ。 前述した 4社と比べると公開している情報が不十分 (特に Virtuozzo/OpenVZ な VPS は実際のパフォーマンスを予想しにくく、 実地に使ってみないとなんとも...) なので私は比較検討対象から外したが、 ダメ元で使ってみるのも面白いかもしれない:

サービス
プラン名
メモリ
ディスク
帯域
追加額/単位
月額
年額
データセンタの
場所
SplitServ
Xen 1024
1024MB
15GB
200GBs
$8/400GB
$14.95
$149.50
Los Angeles, CA
Kansas City, MO
WebKeepers
Virtuozzo ライト
512MB
50GB
?1280円
11760円
?
PhotonVPS
Xen VPS WARP 1
512MB
35GB
500GB
?
$16.95
$203.40
Los Angeles, CA
KnownHost
Virtuozzo VS2
512MB
30GB
750GB
$40/100GB
$35
$400
Los Angeles, CA
Dallas, TX
BurstNet
VPS PACKAGE #1
512MB
20GB
1000GB
$25/200GB
$5.95
$71.40
Scranton, PA
VPS NOC
OpenVZ Bronze
512 MB
20 GB
400 GB
?
$12.95
$155.40
Kansas City, MO
CoreNetworks
CoreMR
512MB
12GB
500GB
?
$19.95
$239.40
?
prgmr.com
Xen VPS
512MB
12GB
80GB
?
$12
$115.20
San Jose, CA
HostCadet
VPS Micro
512MB
10GB
400GB
?
$9.99
$119.88
Colorado Springs, CO
NY NOC OpenVZ
Super VPS #1
512MB
10GB
1000GB
?
$10.00
$120
Chicago, IL
North Bergen, NJ
ARP Networks
KVM/QEMU
512MB
10GB
100GB
?
$15
$180
Los Angeles, CA
VirtuallyDedicated
Xen VX384
384MB
24GB
300GB
?
$10.99
$131.88
Chicago, IL
Scranton, PA
GrokThis.net
Xen VPS
320MB
16GB
160GB
?
$20
$200
Philadelphia, PA
DMEHosting
OpenVZ VPS1
256MB
25GB
1000GB
$10/200GB
$5.95
$71.40
Denver, CO
Chicago, IL
Quantact
OpenVZ VS1
256MB
15GB
300GB
$1/GB
$14.99
$179.88
Santa Rosa, CA
RackspaceCloud
Xen CloudServers
256MB
10GB
0GB
$.08-$.22/GB
$10.95
$131.40
Dallas, TX
XENnode
XEN 256
256MB
10GB
200GB
?
$14.99
$143.99
Dallas, TX
Datarealm Xen
PowerVPS
256MB
10GB
1Mbps
Unmetered
$19.95
$215.52
?
HostVirtual Xen
XV0 Server
256MB
10GB
250GB
?
$14.95
$179.40
San Jose, CA
RackUnlimited
Xen RVPS-Starter
256MB
10GB
100GB
?
$8
$96
Asheville, NC
QuickWeb
Xen Lite
256MB
5GB
240GB
?
$17.95
$215.40
San Jose, CA
QuickVPS
Xen プラン1
256MB
10GB
50GB
?
2300円
21600円
日本国内
QuillHost
OpenVZ Standard
256MB
8GB
150GB
?
$9.99
$119.88
Washington, DC
Charlotte, NC

と、いうわけで、 Linode 360 を契約してみた。

(続きを読む...)
Filed under: システム構築・運用 — hiroaki_sengoku @ 08:58
このエントリーを含むブックマーク 2010年2月10日

CTO日記も livedoorブログから WordPress へ引越しました (URL は変更なし)

「仙石浩明の日記」 に続いて、 「仙石浩明CTO の日記」 も先週末に livedoorブログから自宅サーバへ引っ越した (つまりネームサーバの設定を変更して切替。有料プランの解約はこれから)。 もともと両ブログは相互にリンクを張って密接に連係していたので、 引越を機会に両者を統合した。

統合といっても両ブログは微妙(?)に読者層が異なると思われるし、 何よりページの体裁が大きく変わってしまっては読者の方々を戸惑わせてしまうので、 CTO日記を 「仙石浩明の日記」 の一カテゴリという位置付けにして、 かつページの体裁は WordPress のテーマを切り替えることによって、 どちらのブログもあまり大きな変化がないようにしている。

「仙石浩明CTO の日記」 http://sengoku.blog.klab.org/ をアクセスすると、 次のような PHP スクリプトを走らせた上で、 WordPress を呼び出す (末尾の require 文):

<?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");
...(中略)...
}
?>

つまり http://sengoku.blog.klab.org/ へのアクセスは、 パス名に 「/category/cto/」 を追加することによって、 CTO日記カテゴリへのアクセスに変換する。

ページの体裁については、 「wp-content/themes/sengoku_cto/」 ディレクトリが、 CTO日記のテーマフォルダで、 二つの PHP 定数 TEMPLATEPATH と STYLESHEETPATH をこのディレクトリへ設定することによって、 テーマの切り替えを行なっている。

テーマフォルダの中にあるテーマ関数ファイル 「functions.php」 は、 WordPress の初期化中に読み込まれるので、 ここに PHP スクリプトを書いておくことによって WordPress の挙動を変更することができる。 例えばブログのタイトルを 「仙石浩明CTO の日記」 に変更するには、 以下のスクリプトを functions.php に追加しておけばよい:

function option_blogname_cto() {
    return '仙石浩明CTO の日記';
}
add_filter('pre_option_blogname', 'option_blogname_cto');

つまり、 pre_option_blogname フックに、 option_blogname_cto フィルタを登録する。

WordPress では、 ブログのタイトルなど各種オプションの設定値 (DB に格納している) を、 get_option($setting) 関数を呼び出すことで参照している。 例えばタイトルは get_option('blogname') を呼び出すことで得られ、 URL は get_option('home') で得られる。

get_option($setting) 関数は wp-includes/functions.php で定義されていて、 以下のようにフィルタフック pre_option_* が定義されている:

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_option_設定名」 というフックに登録されたフィルタが値を返すなら、 get_option はオプションの設定値ではなくフィルタが返した値を返すようになる。 前述の例なら、 「pre_option_blogname」 フックに登録された 「option_blogname_cto」 フィルタが 「仙石浩明CTO の日記」 という値を返すので、 get_option('blogname') も 「仙石浩明CTO の日記」 という値を返すようになり、 結果としてブログのタイトルを変更できる、というわけ。

ただし、 前述したように CTO日記は 「仙石浩明の日記」 の一カテゴリという位置付けなので、 ブログのタイトルを変更しただけだと、 ブログ 「仙石浩明CTO の日記」 の 「仙石浩明CTO の日記」 カテゴリということで、 ページのタイトル等が 「仙石浩明CTO の日記 » 仙石浩明CTO の日記」 という冗長なものになってしまう。 そこで、 以下のようなスクリプトを 「functions.php」 に追加して、 タイトルとカテゴリ名が同じときはカテゴリ名が表示されないようにする:

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');

single_cat_title フックは、 wp-includes/general-template.php で定義されていて、 ページのタイトルなどに表示されるカテゴリ名を変更することができる。

以上で、 「仙石浩明の日記」 の一カテゴリを 「仙石浩明CTO の日記」 の体裁で見せることができるようになる。 しかし、 元が 「仙石浩明の日記」 であるだけに、 リンク先が全て 「仙石浩明の日記」 のページになってしまう。 例えば、 「仙石浩明CTO の日記」 のトップページの一番下に、 「古い投稿 »」 というリンクがあるが、 このリンク先が http://www.gcd.org/blog/page/2/ になってしまい、 たどると 「仙石浩明の日記」 のトップページの 2ページ目へ遷移してしまう。

また、 本文中 (あるいはサイドバー) に現れるリンクも、 DB のデータは 「仙石浩明の日記」 のパーマリンクを用いているので、 たとえそれが 「仙石浩明CTO の日記」 カテゴリに含まれていても、 そのリンクをたどると 「仙石浩明の日記」 の記事として表示されてしまう。

そこで、 遷移先も 「仙石浩明CTO の日記」 として表示したいリンクを、 フィルタで書き換えることにした。 つまり DB のデータは 「仙石浩明の日記」 へのリンクのままで、 ブラウザに送信する前に都度書き換える。

対象となるリンクは、 記事本文中だけでなく、 前述したページナビ 「古い投稿」 「新しい投稿」 や、 サイドバー (「人気記事」 や 「最近の投稿」) にも現れる。 ページ丸ごと (つまり HTTP レスポンス丸ごと) HTML を書き換えられるフックがあるとよかったのだが、 残念ながらそういうフックは定義されていないようだ。 以下のフックそれぞれについてリンクを書き換えればよさげ:

フィルタフック フィルタが変更できる対象, 第2引数, ...
the_content 記事本文 HTML
the_category 記事の末尾に表示されるカテゴリーリストの HTML,
$separator, $parents
get_pagenum_link ページ末尾に表示されるページナビ 「古い投稿」 「新しい投稿」 の URL
post_link 記事の URL (パーマリンク), $post, $leavename
widget_text サイドバーに表示されるテキストウィジェットの HTML, $instance
wp_list_categories サイドバーに表示されるカテゴリーのリストの HTML
category_feed_link カテゴリーの RSSフィードの URL, $feed

書き換え対象のリンクを決めるために、 まず CTO日記カテゴリに属す記事の ID を取得する:

function setup_cto_id() {
    global $wpdb;
    global $is_cto_id;
    $result = $wpdb->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;
    }
}

あるカテゴリに属す記事ID のデータを取得する関数など、 WordPress に含まれているんじゃないかと探してみたのだが、 見つからなかったので DB に問合わせて取得するようにしてみた。 毎回 DB アクセスが発生してしまうが、 キャッシュとかはアクセス数が増えてから考える (^^;)。 「term_taxonomy_id=17」 が CTO日記のカテゴリ (決め打ち ^^;)。 CTO日記カテゴリに属す記事は、 配列 $is_cto_id[記事ID] に 1 を代入しておく。

次に URL を書き換えるスクリプト:

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;
}

顔文字を画像に変換して表示するフィルタ wptexturize (wp-includes/formatting.php で定義されている) を参考にさせてもらった。 preg_replace_callback() を使って書き換え対象リンクを探し、 replace_URL_cto で記事ID がCTO日記カテゴリに属す ($is_cto_id[$matches[2]] が TRUE) 場合のみ書き換える。

最後に、 この書き換えフィルタ replace_URL_cto を前述したフィルタフックに追加:

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');

wp_list_categories にフィルタを追加すると Category Order プラグインと衝突するので、 優先順位を下げて Category Order プラグインの後で実行されるようにしている。

また、 the_category, post_link, widget_text, category_feed_link 各フックは、 2つ以上の引数を持つが、 第1引数 (書き換え対象の HTML あるいは URL) のみ使用するので引数の数 (add_filter の第3引数) を省略している。

Filed under: システム構築・運用,プログラミングと開発環境 — hiroaki_sengoku @ 07:46
このエントリーを含むブックマーク 2010年1月28日

本ブログを livedoorブログから WordPress へ引越しました (URL は変更なし)

このブログ 「仙石浩明の日記」 は、 今まで livedoorブログの有料プラン (月額 315円) を利用していた。 比較的自由にデザインをカスタマイズできること、 URL のドメインを自由に設定できることから、 2006年3月9日にこのブログを開設して以来、 今まで 4年近く使い続けてきた。 しかし自前サーバでない不便さ、隔靴掻痒感はいかんともしがたく、 乗り換えを検討したことは何度もあって、 そのたびに不便さと乗り換えの面倒くささを天秤にかけて踏み切れずにいた。

例えば、 HTML文法エラーを修正できない問題点を見つけたときや、 ブログのサイドバーにある 「livedoor Reader」 「RSS」 「livedoor Blog」などのバナーを非表示にしていたら、 ライブドアから警告メールが来て、 やむなくバナー表示を復活させたときなど。 なかでも、 昨年ブログ管理画面 (有料プラン) がリニューアルされたときは、 とても大きなフラストレーションを感じた。 ライブドアはよかれと思ってリニューアルしているのだろうが、 JavaScript を多用した新管理画面は、 私にとっては使いにくいことこのうえないし、 生ログ取得スクリプトも使えなくなった。

そして、 今年に入って 「ブログ共通ヘッダー」 (最上端に表示される livedoor Blogのロゴ, 所属カテゴリ, テキスト広告) が勝手に表示されるようになったのが最後の背中のひと押しとなった。 これを非表示にしたければ PRO プラン (月額 315円) ではなく ADVANCE プラン (月額 840円) に変更しろという趣旨 (要は値上げ) なのだと思うが、 何の連絡もなく画面の一番目立つ最上端に、 醜悪なヘッダー (ADVANCE プランへの移行を促すためにわざと醜悪にしている? *_*) を勝手に挿入するデリカシーの無さに驚き呆れ、 自前サーバへの引越を決めた。

common header

ブラウザ画面最上端 ↑ の 「ブログ共通ヘッダー」 (所属カテゴリ 「IT > プログラミング 」 と 「受験にまつわるエピソードを教えてください」 という広告? 表示) は、 ライブドアのサーバが HTML 中に自動挿入する以下の JavaScript (settings/header.js) によって body 要素へ追加 (appendChild) されている。

(function () {
    var hd = document.createElement('div');
    var str = '';
    str += '<div id="header2" style="z-index:10001"><table cellspacing="0" class="blog-common-header" id="header">';
	...(中略)...
    hd.innerHTML = str;
    document.body.appendChild(hd);
	...(後略)...

ユーザがカスタマイズ可能な範囲の外であるため、 この JavaScript の実行そのものを止めることは無理っぽいが、 追加された子要素を削除する JavaScript を実行して消すことは可能。 例えば以下のようなコードを HTML ファイルのどこかに入れておけばよい。

<script type="text/javascript" src="http://www.gcd.org/sengoku/docs/livedoor.js"></script>

livedoor.js は、 body 要素の子要素 「header2」 を取り除く (removeChild) だけの JavaScript:

(function () {
    var element = document.getElementById("header2");
    element.removeChild(element.childNodes.item(0));
})();

正月早々 「ブログ共通ヘッダー」 が勝手に表示されるようになったことに気付き、 直ちにこの対策を行なって非表示にしたのだが、 以前バナーを非表示にしたときライブドアから警告メールが来たことを考えれば、 今回も警告メールが来るのは時間の問題だろうと、 引越を急いだ次第。

私はブログを書くときも HTML で書いていて、 あまり CMS 的な機能は必要としないので、 トラックバックとコメントを受付けるだけの簡単な Web アプリを書こうと思っていたのだが、 試しにインストールしてみた WordPress が思いの外シンプルな作りで、 ちょっといじっているうちに簡単にカスタマイズできてしまったので、 これを使うことにした。

livedoorブログでエクスポートしたデータを (Movable Type / TypePad のデータとして) WordPress でインポートしたら、 (pre 要素なのに) 行頭の空白文字が削除されてしまって往生したが、 大した量でもなかったので手作業で修正してしまった。 もう少し量が多かったら真面目に PHP コードを追ったのだが...

こんなに簡単にできるなら、 もっと早く WordPress へ移行すればよかった。 既存のテーマを見よう見まねでいじっただけなのだが、 以前のデザインをほとんどそのまま踏襲することができた (むしろ余計な div 要素を排除できたので、よりシンプルになった)。

livedoorブログと WordPress では URL (パーマリンク, permalink) の形式が異なる。 例えば、 あるエントリの URL は、 livedoorブログ (旧URL) と WordPress (新URL) で次のようになる:

旧: livedoorブログ新: WordPress
ベースURL http://blog.gcd.org http://www.gcd.org/blog
エントリ /archives/51168556.html /2008/04/154/
月別表示 /archives/2008-04.html /2008/04/
カテゴリ /archives/cat_50026701.html /category/enlighten/
RSS /index.rdf /feed/rdf/

一見して、 WordPress のパーマリンクの方が分かりやすい。 正確に言えば、 WordPress のパーマリンクの形式は任意に設定できて、 livedoorブログ (Movable Type ベース) の形式に合わせることも可能だが、 エントリID (上記の例だと 51168556 と 154) が異なるので形式を合わせること自体にはあまり意味はない。

livedoorブログを利用していた時の URL がそのまま使えるように、 旧URL を新URL へ変換する以下の PHP スクリプトを書いた (switch 文で URL ごとにずらずら case を並べただけ)。

<?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();
}
?>

http://blog.gcd.org/* にアクセスすると、 ↑ この PHP スクリプトが実行される。

これに加え、 逆方向の変換、 つまり新URL から旧URL への変換も必要。 例えば 「はてなブックマーク」 へ頂いたコメント (例えば前述したエントリに対するコメント一覧) は旧URL に紐づいているので、 各エントリに旧URL を登録しておく必要がある。 WordPress には カスタムフィールド という機能があって、ひとつのエントリに複数の 「キー」 と、 各キーにその値を登録することができる。 そこで沢山の 「ブックマーク」 を頂いたエントリには、 「hatena_b」 というキーで旧URL を登録しておくことにした。 すると、以下のような PHP スクリプトでブックマーク数 を表示できる。

<?php
  $hatena_b = get_post_meta($post->ID, "hatena_b", true);
  if ($hatena_b)
    echo ' <a href="http://b.hatena.ne.jp/entry/' . $hatena_b
       . '"><img style="border:0;" src="http://b.hatena.ne.jp/entry/image/'
       . $hatena_b . '" alt="" /></a>';
?>

最後に、 ネームサーバの設定変更を行なって、 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 時間〜数時間ごとに、ぱらぱらアクセスがあるという状況が続いている。 旧レコードを保持しているキャッシュネームサーバが残っていて、 そのネームサーバを使ってるブラウザからのアクセスのみが、 ライブドア側へ届いているといった感じ。

(続きを読む...)
Filed under: システム構築・運用 — hiroaki_sengoku @ 08:26
このエントリーを含むブックマーク 2010年1月4日

香港で買ったプリペイド SIM カードを日本で使ってみる

あけましておめでとうございます。今年もよろしくお願いします。

香港 CSLPower Prepaid SIM $88 を買った (HK$88 = 約1060円)。 この SIM カードを妻の携帯電話 Motorola ZN200 に入れて、 一週間の香港滞在中、 互いに連絡するときに大いに利用した。 が、通話料が一分間 HK$0.1 (約 1円) と安すぎるので、 HK$82.20 も残ってしまった (つまり HK$5.80 しか使ってない)。

Power Prepaid SIM $88

有効期限は 2010年6月15日までの半年間。 期限内にチャージすればチャージした時点からさらに半年間有効。 SIM カードのパッケージの内側には、 海外 (香港でも 「於海外」 「Overseas」 って言うのか...) でのチャージ方法として A. 自動チャージ (香港での手続きが必要) B. バウチャー (香港で事前に購入が必要) C. PPS の三種類があると書いてある。

Power Prepaid SIM $88 inside

PPS (Payment by Phone Service ?) というと、 電話をかけて支払う方法だとばかり思っていたが、 インターネット経由でも可能であるようだ (「by Phone Service」 と言いながらインターネットでも可能とはこれ如何?)。 Top up Your Account ページで、 チャージ金額 (HK$50 ~ HK$300)、SIMカードの電話番号、クレジットカード番号 を入力することでチャージできそう。 したがって期限切れ直前にチャージすることにより次回 (たぶん年末に) 香港に行くときに使うこともできそうだが、 一年間使わずに寝かせておくよりはと思い、 Yahoo!オークションに出品してみた

この SIM カードは日本でも、 3G 対応の携帯電話なら使用できる (Motorola ZN200 は GSM 専用機なので日本では使えない) ので、 HTC P3600 に入れて試しに少し使ってみた (正確に言うと、 課金される実験は HK$1000 Prepaid SIM を使用したので、 Power Prepaid SIM $88 の残度数は減らしていない。 どちらの SIM も機能的には同じ)。

まず、 着信は普通に可能。 日本から 「010-852-6714-XXXX」 (「010」 は日本において国際電話をかけるときの国際プレフィックス、 「852」 は香港の国番号、 「6714-XXXX」 は、この SIM カードに割り当てられた香港内での電話番号) にダイヤルすれば、 この SIM カードを入れた電話機が日本にあっても着信する (Roaming Service)。 ただし 30秒ごとに HK$25 かかる (国によって HK$8 ~ HK$25。 深圳とかなら HK$8)。

発信は、 中国以外だと直接発信ができないのでコールバックしてもらう方式。 つまり 「*108*」 に続けて相手先の電話番号を香港からかける形で押して、 最後に 「#」 を押した上で発信。 例えば東京都の固定電話 03-5771-1328 にかけるのであれば、 「*108*00181357711328#」 とダイヤル。

すると香港から電話がかかってくる (コールバック) ので、 出ると中国語で何やら言ったあと、 相手先 001 81 3 5771 1328 (001 は香港において国際電話をかけるときの国際プレフィックス、 81 は日本の国番号、 3 は東京の市外局番03) を呼び出してくれる。 相手先が出ればそのまま通話できる。 つまり日本国内宛に電話をかける場合も、 香港から日本への国際電話 2本使って通話することになる。 なお、 「*108*」 の代りに 「*108*1*」 にすると、 コールバックの時の発声を中国語のかわりに英語で言ってくれるらしい (試すとまた課金されてしまうので未確認)。

コールバックに応えた時点で、 相手先につながるか否かにかかわらず課金される。 相手先が出てくれなくても 30秒ごとに HK$25 も課金されてしまうので注意が必要。 試しに私の PHS に前述のダイヤル方法 (*108*00181705013YYYY#) でかけてみた。 すると 「通知不可能」 で PHS が鳴った。 ちょっと鳴らしてみただけなのだが、 通話時間 (コールバックに応えてから切断するまで) としては 19秒もかかり、 HK$6.5 もかかった (香港滞在一週間で使った度数より大きい! *_*)。 数秒鳴らすだけで、 香港内同士 (local call HK$0.1/min) なら 1時間分以上の通話に匹敵する課金なのだから恐れ入る。

というわけで通話の Roaming Service は高すぎて実用的ではない (国際ローミングなんてどこもそんなもの?) が、 日本にいながら香港の番号での着発信の確認をできる意味はあるかも (香港に行く前から電話番号を周囲に伝えておく場合など)。

一方 SMS (Short Message Service) は、 一通 HK$3 なので香港内同士 (一通 HK$0.8) に比べてさほど割高ではない (そもそも local で一通 10円というのが高いと思うけど)。 しかも、 受信側は香港内外問わず無料でメッセージを受け取ることができる。 日本ではあまり一般的ではない SMS だが、 日本以外だととても普及している。 SMS は通信キャリア・国を越えて利用できる共通基盤になっているのに、 日本の通信キャリアはいまだに事業者間接続でモメているようだ。 よく知られているように、 twitter の文字数制限が 140文字なのは、 SMS の制限が 140オクテットであることを踏襲している。

日本を除くほぼ全世界で共通のテキスト・メッセージサービスとして定着した。 現在の全世界のSMSの利用者は約24億人で、 世界中の携帯電話ユーザーの約74%がSMSを利用している。

まず SMS の着信を試してみる。 Skype で SMS を 「+852-6714-XXXX」 に送ってみる (€0.051 = 約8円かかった) と、 携帯電話が日本にあっても着信する。 日本語表示が可能な携帯電話 (HTC P3600) なので、 日本語を含むメッセージも問題なく表示できた。 念のため残度数が減ってないことを確認。

「*109#」 にダイヤルする (もちろん無料) と、 次のような残度数を知らせるメッセージが、 差出人 「179179」 から SMS で届くので残度数を確認できる。

6714XXXX with $82.20 stored-value. The expiry date is 15/Jun/2010.

次に SMS の発信を試してみる。 別の SIM カード (HK$1000 Prepaid SIM) を HTC P3600 に入れて、 HTC P3600 の標準アプリケーション 「メール」 を使って SMS を 「+8526714XXXX」 宛に送ってみた。 宛先欄に 「+」 記号を含めて入力することが重要。 残度数を調べると、 送信に HK$3 かかったことが確認できた。 続いて SIM カードを元の Power Prepaid SIM $88 (電話番号が 6714XXXX) に戻すと、 先ほど別 SIM カードで送った SMS が着信した (SIM カード入れ替えを行なったのは、 手元に 3G 対応携帯電話が一台しかないため)。

Filed under: 香港 — hiroaki_sengoku @ 07:07
Older Posts »