2011年12月24日土曜日

Solaris 11でmikutter用ruby環境を構築する

pkgsrcがうまくいかずぐぬぬしていたので、気をとり直してmikutterを動かしてみることにした。 ただ、ホストがXServerを動かせるような環境では無いので、sshでXをforwardして起動する。 また、リポジトリにはruby1.8.7しか無いため、ruby1.9.3はソースからビルドする。

pkg install gcc
pkg install system/header
pkg install library/desktop/gtk2
pkg install x11/session/xauth
pkg install system/font/truetype/ipafont
wget ftp://ftp.ruby-lang.org/pub/ruby/ruby-1.9.3-p0.tar.gz
tar xzf ruby-1.9.3-p0 && cd ruby-1.9.3-p0
./configure --prefix=/usr/ruby && make && make install
/usr/ruby/bin/gem install gtk2

だいたいこんな感じでmikutterを実行できるruby環境が出来上がる。 あとはいつも通りmikutterをcheckoutして実行すればよい。

Solaris 10(SPARC)の時はやたら苦労したのに、 今回はすんなり通ってしまいあっさり終わってしまった。

Solaris 11でpkgsrcを使う

poolがv33なZFSを試そうとOracle印のSolaris 11を入れてみた。 しかしパッケージが古かったり無かったりするものが多くてよろしくないので、 pkgsrcを導入した。その流れをさっとまとめておく。

pkg install git
pkg install gcc
pkg install system/header
zfs create -o mountpoint=legacy rpool/netbsd
zfs create -o mountpoint=/usr/pkg rpool/netbsd/pkg
zfs create -o mountpoint=/usr/pkgsrc rpool/netbsd/pkgsrc
git clone https://github.com/jsonn/pkgsrc /usr/pkgsrc
cd /usr/pkgsrc/bootstrap
./bootstrap.sh --prefix=/usr/pkg

bootstrapを開始して数分、以下のようなエラーが発生。

checking how to run the C preprocessor... cpp
configure: error: in `/usr/pkgsrc/bootstrap/work/wrk/devel/bmake/work/SunOS':
configure: error: C preprocessor "cpp" fails sanity check
See `config.log' for more details.
*** Error code 1

config.logを見たところ、/usr/gcc/binにPATHが通っていないので、 cppを見つけられずにコケている模様。 gcc自体には/usr/bin/gccにシンボリックリンクが張られていたので気が付かなかった。

rm -rf ./work /usr/pkg/* /var/db/pkg
PATH=$PATH:/usr/gcc/4.5/bin ./bootstrap --prefix=/usr/pkg

これでbootstrapは通った。

nbpatchがコケる

bootstrapが完了したので、早速tmuxあたりをインストールしてみることにしたのだが、 早速コケてしまった。libtoolにpatchを当てる際にnbpatchがエラー終了している様子。

miku@iroha:/usr/pkgsrc/devel/libtool$ sudo bmake 
=> Bootstrap dependency digest>=20010302: found digest-20111104
===> Checking for vulnerabilities in libtool-2.2.6b
===> Installing dependencies for libtool-2.2.6b
=> Build dependency nbpatch-[0-9]*: found nbpatch-20100124
=> Full dependency libtool-base-2.2.6b{,nb[0-9]*}: NOT found
=> Verifying package-install for ../../devel/libtool-base
=> Bootstrap dependency digest>=20010302: found digest-20111104
=> Bootstrap dependency tnftp-[0-9]*: found tnftp-20070806
===> Checking for vulnerabilities in libtool-base-2.2.6bnb5
=> Checksum SHA1 OK for libtool-2.2.6b.tar.gz
=> Checksum RMD160 OK for libtool-2.2.6b.tar.gz
===> Installing dependencies for libtool-base-2.2.6bnb5
=> Build dependency nbpatch-[0-9]*: found nbpatch-20100124
===> Overriding tools for libtool-base-2.2.6bnb5
===> Extracting for libtool-base-2.2.6bnb5
===> Patching for libtool-base-2.2.6bnb5
=> Applying pkgsrc patches for libtool-base-2.2.6bnb5
patch: **** too many file arguments
Patch /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-aa failed
patch: **** too many file arguments
Patch /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-ab failed
patch: **** too many file arguments
Patch /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-ac failed
patch: **** too many file arguments
Patch /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-ad failed
ERROR: Patching failed due to modified or broken patch file(s):
ERROR:  /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-aa
ERROR:  /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-ab
ERROR:  /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-ac
ERROR:  /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-ad
*** Error code 1

Stop.
bmake: stopped in /usr/pkgsrc/devel/libtool-base
*** Error code 1

Stop.
bmake: stopped in /usr/pkgsrc/devel/libtool-base
*** Error code 1

Stop.
bmake: stopped in /usr/pkgsrc/devel/libtool

sudo bmake PATCH_DEBUG=1 PKG_VERBOSE=1 PKG_DEBUG=yes PKG_DEBUG_LEVEL=2した場合のエラー部分はこんな感じ。

+ patches=/usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-*
+ test -f /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-aa
+ test ! -f /usr/pkgsrc/devel/libtool-base/../libtool/distinfo
+ echo => Verifying /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-aa
=> Verifying /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-aa
+ /usr/bin/env DIGEST=/usr/pkg/bin/digest CAT=/usr/bin/cat ECHO=echo SED=/usr/gnu/bin/sed TEST=test /usr/pkg/bin/pdksh /usr/pkgsrc/mk/checksum/check
sum -p /usr/pkgsrc/devel/libtool-base/../libtool/distinfo /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-aa
+ > /dev/null 
+ 2>&1 
+ cksum_result=0
+ echo => Applying pkgsrc patch /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-aa
=> Applying pkgsrc patch /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-aa
+ fuzz_flags=
+ /usr/pkg/bin/nbpatch -v
+ > /dev/null 
+ 2>&1 
+ fuzz_flags=-F0
+ /usr/pkg/bin/nbpatch -F0 -d /usr/pkgsrc/devel/libtool-base/work/libtool-2.2.6b -E -p0 -V simple -z .orig
+ < /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-aa 
patch: **** too many file arguments
+ echo Patch /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-aa failed
Patch /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-aa failed
+ fail= /usr/pkgsrc/devel/libtool-base/../libtool/patches/patch-aa

他のパッケージでもnbpatchがエラーを吐いてコケてしまいまったくビルドが通らない。 さてどうしたらいいものか...

2011年12月21日水曜日

Debian/kFreeBSD(wheezy)に9.0のカーネルが登場

Debian流にFreeBSDが利用できることで期待のDebian/kFreeBSD。 そのテスト版に、そろそろ正式版リリース予定のFreeBSD 9.0のカーネル類が提供されたようなので、 早速試してみた。

まずはカーネル。直接wheezyをインストールしようと試したもののうまく行かず、 今回もsqueezeからのアップグレードでwheezyを入れた。 アップグレード方法はlinuxなdebianと同じく、sources.listを書き換えて更新するだけ。

FreeBSDといえばZFS。8.3以降はpoolのバージョンが28になるということで、 こちらもv28になっていることが確認できた。 zfsutilsをインストールすることで利用できるようになる。

FreeBSDといえばもうひとつ、jailがある。こちらも最近ようやくDebian/kFreeBSDに提供されて、 wheezy以降なら利用できる。debianなのでjail環境はdebootstrapで一発作成。

そして、BSDといえばpfも使える。まだpflogは使えない(?)ようだが、 とりあえずpfそのものを有効にすることはできる。

これ以外にも追加されつつある機能はいろいろとあるもよう。 ただ、testingだけあって安定性の面でやはりまだ難がある (この記事を書くにあたっても、1度ファイルシステムが壊れた) ようなので、常用するには難があるかもしれない。

2011年12月11日日曜日

ruby/dlでの構造体の扱いについて

ruby/dlでlibaoを使うでCのライブラリをCでラッパを書かずにrubyから利用できることは分かった。 ただ、構造体の扱いが少々面倒臭いことが分かったので、纏めておく。

構造体のメンバに構造体が存在する場合

構造体の定義はDL::Importer#structを用いて行うことができるが、 構造体のメンバに構造体が存在する場合、それを自動的に展開はしてくれない。

例えば、以下のような2つの構造体があったとする。

typedef struct info{
  int no;
  char *name;
} info;

typedef struct namelist{
  info first;
  info second;
  info third;
} namelist;

これをDL::Importer#structに渡す際には、以下のように書きたくなるが、 これではnamelist構造体のメンバであるinfoがうまく展開されない。

Info = struct(['int no', 'char *name'])
NameList = struct(['info first', 'info second', 'info third'])

以下のように手動で展開する必要がある。

NameList = struct(['int first_no', 'char *first_name',
  'int second_no', 'char *second_name', 
  'int third_no', 'char *thrd_name'])

関数の引数に構造体のポインタを渡す場合

関数の引数に構造体のポインタを渡す場合は、ruby/dl側でvoid *に自動的に変換するようなので、 そのまま渡して構わない模様。

# int check(namelist *list);
extern('int check(namelist *list)')

関数の引数に構造体そのものを渡す場合

aliasできる型が無いのでDL::Imprter#typealiasも利用できないし、どうしたらいいものか…と悩み続けて数週間。 入れ子の構造体同様に定義してしまえばよいということにようやく気がついた。

# int check(namelist list);
extern('int check(int first_no, char *, int , char *,  int, char *)')

あとはrubyから呼ぶ際にメンバの順番通りに引数を渡せば良い。

ここまでくるのにだいぶ長期間悩んでしまったが、まぁなんとかなったのでよしとしたいところ…orz。

2011年11月20日日曜日

Debian/kFreeBSDでjailを使う

squeezeリリースの際に話題になったDebian/kFreeBSD。いろいろと主要な 機能がsqueezeリリース時点では足りず残念だったのだが、wheezy(testing) は着々と開発が進んでいるようで、先日遂に wheezyの freebsd-utilsにjailが含まれた。そこで、今回はjailを用いて Debian/kFreeBSDの中にFreeBSDっぽいものを量産してみようと思う。

Debian/kFreeBSD wheezyをインストール

今回必要となるのはwheezy(testing)以降のDebian/kFreeBSDである。wheezyの インストール はdaily build のdebian-installerを用いて直接行うことも可能だが、あまりうまく行っ た試しが無い。そこで一旦squeezeをインストールし、その後wheezyにupgrade する。
root@kfbsd:/# sed -i.squeeze s/squeeze/wheezy/g /etc/apt/sources.list
root@kfbsd:/# aptitude update
root@kfbsd:/# aptitude full-upgrade

Debian/kFreeBSD wheezyをdebootstrap

jail用にwheezyを/var/jail/wheezyに用意する。素のFreeBSDならば/usr/src に入ってうんたらアーカイブを展開してかんたらとかあるのだが、今回は Debianなのでdebootstrapで一発である。
root@kfbsd:/# aptitude install debootstrap
root@kfbsd:/# debootstrap wheezy /var/jail/wheezy http://ftp.jaist.ac.jp/pub/Linux/Debian
もしi386向けのsqueezeを用意したいのならば、以下のようにdebootstrapを実 行する。
root@kfbsd:/# debootstrap --arch kfreebsd-i386 squeeze /var/jail/squeeze_i386 \
> http://ftp.jaist.ac.jp/pub/Linux/Debian

jailに入る

そして作成したjail環境に入る。
root@kfbsd:/# ifconfig em0 alias 10.0.0.3/24
root@kfbsd:/# /usr/sbin/jail -c path=/var/jail/wheezy host.hostname=negi \
> ip4.addr=10.0.0.3 command=/bin/bash
root@negi:/# hostname
negi
root@negi:/# ifconfig | grep inet
        inet 10.0.0.3 netmask 0xffffff00 broadcast 10.0.0.255
/devが必要な場合は、親からdevfsをmountする。
root@kfbsd:/# mount_devfs devfs /var/jail/wheezy

FreeBSDのjail環境をDebian/kFreeBSD内に作る

カーネルはFreeBSDなので、普通のFreeBSDのユーザーランドを持ってきてjail 環境とすることもできるらしい。今回は2011年11月18日時点で最新の FreeBSD9.0-RC2 amd64のユーザーランドを用意。カーネルとのバージョンの違 いは気にしないことにする(ぁ
root@kfbsd:/# wget http://ftp.jaist.ac.jp/pub/FreeBSD/releases/amd64/amd64/9.0-RC2/base.txz
root@kfbsd:/# mkdir /var/jail/freebsd
root@kfbsd:/# tar xpJf base.txz -C /var/jail/freebsd
root@kfbsd:/# mount_devfs devfs /var/jail/freebsd
root@kfbsd:/# /usr/sbin/jail -c path=/var/jail/freebsd host.hostname=fbsd \
> ip4.addr=10.0.0.3 command=/bin/sh
# uname -a
FreeBSD fbsd 8.2-1-amd64 #0 Mon Oct 17 18:49:25 UTC 2011  amd64
# ps aux
USER   PID  %CPU %MEM    VSZ    RSS  TT  STAT STARTED    TIME COMMAND
root 28266   0.0  0.2  14632   2348  p1  SJ    4:57AM 0:00.01 /bin/sh
root 28268   0.0  0.2  14324   1756  p1  R+J   4:57AM 0:00.00 ps aux
# clang -v
FreeBSD clang version 3.0 (branches/release_30 142614) 20111021
Target: x86_64-unknown-freebsd9.0
Thread model: posix

残念ながらまだjexecやjlsは追加されていないようだが、Debian/kFreeBSDで もjailが使えるということが分かっただけでもよしということにしておこう。

jail以外にも、インストーラがZFSに対応したり、NFS Server/Clientが 追加されていたり、experimentalにkfreebsd-image-10-amd64が出現して いたりと、いつの間にかいろいろ増えていることが多いDebian/kFreeBSD。 wheezyリリース時にはかなりいい感じになるのではないかと、期待が膨らむ。

2011年8月12日金曜日

Xperia X10(SO-01B)とAndroid 2.3

昨年の夏、使用していたP701iDの液晶がついに壊れ、それならばと思い切って乗り換えたXperia X10(SO-01B)。 使い始めて1年経ったこともあり、少し纏めてみようと思う。

Androidとは

Xperia X10のOS、Androidは、Googleが開発・公開しているOSである。 Androidでは、各メーカが独自にカスタマイズしたものを、 キャリア向けにカスタマイズし販売、ユーザが購入する、というのが主流である。 Windowsならば、Microsoftがリリースし、各メーカがカスタマイズしたものを「メーカ製PC」 として販売するのが主流。

WindowsとAndroidの大きく異なる点は、間に介入する存在が多い点と、更新の方法が異なる点。 このため、Android開発元のGoogleがOSを更新してもすぐには反映されないどころか、 まったく反映されないことも多々ある。

Androidの更新を保証

そこで、18ヶ月間のupdateを保証するプログラムというものが発表されたことがあった。

GoogleとOEM各社、Android端末のアップデート保証プログラムを発表
http://japanese.engadget.com/2011/05/10/google-oem-android/

その結果か、Xperia X10のグローバルモデルにAndroid 2.3の更新が登場。 散々無いと言われ続けていたが、Sony Ericssonは出来る子だったようだ。

Android 2.3 for Xperia™ X10 is here
http://www.sonyericsson.com/update/?lc=en&cc=gb&pid=xperiax10

まさかの事態

しかし、これに水を差すとんでもない奴が居た。キャリアである。

Xperia(SO-01B)のAndroid 2.3アップデート見送りの理由が明らかに
http://gigazine.net/news/20110622_xperia_so_01b_not_gingerbread_reason/

これによると、更新を見送る理由は、

  • Android 2.3モデルとして今年発表した「Xperia arc(SO-01C)」や「Xperia acro(SO-02C)」のようなパフォーマンスを発揮できない
  • バージョンアップの際にユーザーデータを保持できず、必ず本体が初期化されてしまう
  • データのバックアップアプリが無くなる
  • カメラ撮影時の笑顔認識機能「スマイルシャッター」が無くなる
  • 現行のAndroid 2.1版で提供している日本語フォント「モトヤフォント」が適用できなくなる
とのこと。

パフォーマンスを発揮できない? そりゃハードの性能が違うんだから当たり前だろう。 本体内蔵データが消える? Android Marketのアプリは仕方ないとしても、ユーザデータを本体に保存しているとか、 何のためにGoogleのOSを使っているのか。 そして最大の「わけがわからないよ」はフォント。 フォントを理由としてサポートする側がupdate見送る携帯端末用OSというのは聞いたことがない。

こうして更新が見送られるほんの少し前、キャリアであるドコモ側はこんな事を言っていたらしい。

「我々の使命は最新OSをお届けすることに加え、日本独自の機能を入れていくこと」、ドコモの山田社長がAndroidのバージョンアップについて言及 http://gigazine.net/news/20110516_docomo_android_version_up/

まったく、わけがわからないよ。

アップデート見送りの問題点

当然ながら、(一部の)SO-01Bユーザからは失望の声が上がり、グローバルモデルのファームウェアと OSをインストールする方も出てきている。だが、それはあくまで非公認のやり方であり、リスクは端末更新で データ消失するより遥かに大きい。 また、「2.3待ってんのなんてオタだけだよw そんなんめんどいし儲からないからキャリアがやらなくて当然じゃんw」 という意見も見受けられる。

だが、アップデートされるのは何も見た目の機能だけではない。セキュリティホールに対する更新も含めての更新なのである。つい最近も、Android 2.2以前にSSLにおける重大な脆弱性が判明したばかりだ。

JVN#43105011 Android における SSL 証明書の表示に関する脆弱性
http://jvn.jp/jp/JVN43105011/

PC向けのWindows等と違い、Androidはユーザ側がパッチを適用することはできない。root権限すら持てない。 既存の携帯電話、いわゆる「ガラケー」と違い、Androidはオープンでグローバルなものであり、 脆弱性もオープンかつグローバル。これまでの常識は通用しない。 Androidの更新を提供しないメーカ・キャリアは、OS更新を提供しないことに対するリスクを、 全てユーザ側に押し付けているということになる。

それでは不味いと一応思ったらしく、「ドコモ あんしんスキャン」なるウィルス対策ソフトウェアの無償提供を開始した。

スマートフォン向けウイルス対策サービス「ドコモ あんしんスキャン」を無料で提供開始
http://www.nttdocomo.co.jp/info/news_release/2011/06/22_01.html

しかし、こんなモノは気休めでしか無い。根本的に解決しなければならないものを、 小手先の工作で誤魔化しているようにしか見えない。OSの更新によるセキュリティホールへのパッチと ウィルス対策は両方必須、Windowsではよく言われていることである。古いAndroidを使い続けるのは、 ウィルス対策ソフトがあるからとMicrosoftによる更新サポートが切れたWindows 9xや2000を使い続けるようなものだ。

それに、"Android 2.3モデルとして今年発表した「Xperia arc(SO-01C)」や「Xperia acro(SO-02C)」 のようなパフォーマンスを発揮できない"などと言ったのだから、ウィルス対策ソフトを導入することによる 負荷増・パフォーマンスの低下も考慮していただきたい。

また、脆弱性情報に対して、Android 2.2以前を用いて製品を販売している国内企業からの反応が ほとんど無いというのも問題がある。("JVN#43105011"でググって出てくるのは1300件。そのうち殆どがtwitterやblog) 例えそのセキュリティホールが販売している端末に対して問題なかったとしても、「問題なかった」 という情報を公開しなければ、不安が募るだけだ。

何を言いたかったか

簡単に言うと、言い訳言ってないでさっさと公式で更新提供するか、 日本独自モデルなんて止めるか、更新打ち切った端末で2年縛りは止めるかしてね!ということだと思う。

いろいろ文句や愚痴を書いたが、AndroidやXperiaが嫌いな訳ではない。 サポートする筈のキャリア(ドコモ)が足を引っ張っている現状に怒っているのである。 update以外にも、Windowsのメーカ製PCを思い起こされる量、かつアンインストール不可なアプリケーションを 大量にプレインストールといった、Androidのメリットである自由さを損ねるようなやり方など、 キャリアのやり方に対する不満点は多く存在する。

ドコモスマートフォン for Businessには、「セキュリティも安心」と書いてある。 しかし、現状のような対応では、私は安心してドコモやその他国内キャリアのスマートフォンを利用することはできない。 情報が流出したり、壊されてからサポートされるのでは遅すぎるのである。

2011年8月9日火曜日

FreeBSD 9.0-BETA1が登場したらしい

我が家のサーバとして活躍中のFreeBSD。
その最新バージョン9のBETA1版が登場したらしいので、早速ダウンロードしてインストールしてみた。

CDから起動

CDを入れて起動。なにやらBoot Loaderの雰囲気も8.0から変わっている。

インストールするか、Shellに落ちるか、LiveCDとして起動するかを選択できるらしい。 今回はinstallを選択する。

パーティショニング

パーティショニングには、Guided、Manual、Shellの3つのメニューがある。 Guidedはディスク全体または選択したパーティションを自動で割り当てる。Manualは自分でパーティションサイズやファイルシステムを選び割り当てる。

試しにfreebsd-zfsにしてみたが…

やはりインストーラを用いたManual設定ではZFSはうまくいかないようである。 Shellから直接叩くしか無いのだろうか…?

仕方がないので/をUFSとしファイルを展開する。

設定

展開終了後、引き続きシステムの設定を行う。設定項目は以下の通り。 基本的にFreeBSD 8と同じように見えるが、細かい変更がある部分もある。 ユーザアカウント設定は、useraddコマンドを実行しただけのようだったり、 それならOpenBSDっぽく全部CUIでいいじゃないかと言いたくなるところもあるが、 おそらくそのうち改善されるのであろう。

  • rootパスワード
  • NIC選択
  • IPv4/DHCP設定
  • IPv6/アドレス割り当て設定
  • DNS設定
  • タイムゾーン設定
  • サービス設定(sshd、moused、ntpd、powerd)
  • ユーザアカウント設定
設定が終了したらexitしてrebootする。

HDDから起動

CDをejectしてインストールしたHDDから起動。 ZFS filesystem versionは5、ZFS storage pool versionは28となっている。

BETA1ということもあり、kernel panicしたりstack trace吐いたりとまだいろいろと不具合は残っているようだが、 RELEASEに期待したい。

www.FreeBSD.org
http://www.freebsd.org/

2011年7月31日日曜日

Rubyのio/consoleを試す

Rubyでコンソールアプリケーションを作成する際に、 標準のIOではうまくいかない問題がある。 例えば...

  • リアルタイム入力
  • パスワード入力時等に文字のエコーを停止
  • 端末のサイズを取得
等がある。これはcursesなどを用いれば解決できるが、パスワード入力のみで必要な場合等 cursesでは大げさすぎると感じる場合もある。

そこで、次のruby(1.9.3?)あたりに標準で付属されるらしい、 io/consoleというライブラリを使ってみる。

導入

今回は、rubyのリポジトリから最新開発版を入手し、ビルドする。RVMを用いてもよい。

miku@hatsune% svn co http://svn.ruby-lang.org/repos/ruby/trunk ruby19-svn
miku@hatsune% cd ruby19-svn
miku@hatsune% autoconf
miku@hatsune% ./configure --prefix=/usr/ruby19-svn --program-suffix=19-svn
miku@hatsune% make
miku@hatsune% sudo make install

lib/ruby/1.9.1/x86_64-linux/io/console.so がio/console用のライブラリらしい。

使ってみる

リファレンスなんて無いので、ソース(ext/io/console/console.c) を読みながらとりあえず叩いてみる。 IOクラスに追加されるメソッドは、以下のとおり...らしい。

  • IO.console #=> IO for console device
  • IO#raw
  • IO#raw!
  • IO#getch
  • IO#echo=
  • IO#echo?
  • IO#noecho
  • IO#winsize
  • IO#winsize=
  • IO#iflush
  • IO#oflush
  • IO#ioflush

実際にirbで叩いてみると分かるが、getchでは入力した瞬間にその入力した文字が返される。 また、winsizeではその瞬間のコンソールサイズが配列として返される。 その他のメソッドも、だいたい名前のままな動作をする模様。

irb(main):012:0> IO.console
=> #

irb(main):011:0> IO.console.getch
=> "m"

irb(main):013:0> IO.console.noecho{|io| p io.getch}
"a"
=> "a"

irb(main):014:0> IO.console.winsize
=> [55, 139]

改行の問題

io/consoleで入力を受け付けながら出力すると、 "\n"で正しく改行ができなくなることがある模様。 例えば以下のように書いた場合、LFのみが行われCRが行われず、 行は変わるものの開始位置がおかしくなる。

Thread.start(){
  loop {IO.console.getch}
}

IO.console.noecho{|io|
  loop{
   io.printf("%d\n", rand(1024))
  sleep 0.1
  }
}

これは、"\n"を"\r\n"に変えてやれば解決する。 今後修正されるかもしれないが、一応。

2011年6月30日木曜日

ruby/dlでlibaoを使う

Cなんて一行も書きたくないけれど、Cのダイナミックリンクライブラリをrubyから使いたい。 そんな人のためのライブラリ(?)がruby/dlである。 (Cは一行も書かなくてもよいものの、流石にCの知識がある程度無いと使えないが…)

利点と欠点は以下のとおり。

・利点

  • Cのダイナミックリンクライブラリをコンパイル作業無しで利用できる
  • すべてrubyで書ける
・欠点
  • 環境依存部分の吸収を自前でやらなければならない場合がある
  • 一部無理矢理な定義をしなければならない部分がある(入れ子の構造体、enum型など)

今回は、ruby1.9のdlライブラリを用いて、 マルチプラットフォームなサウンド出力ライブラリであるlibaoを叩き、 オーディオファイルを再生できるようにしてみる。

ヘッダファイルを読む

ruby/dlでlibaoを叩くには、まずlibaoにどのような関数や構造体などがあるのか調べる必要がある。 libaoでは、ほとんどが$PREFIX/include/ao/ao.hで宣言されているので、それを読む。 ao/ao.hで定義されていてruby/dlから叩くのに必要な部分は、以下のとおりである。

/* --- Constants ---*/
#define AO_TYPE_LIVE 1
#define AO_TYPE_FILE 2

#define AO_ENODRIVER   1
#define AO_ENOTFILE    2
#define AO_ENOTLIVE    3
#define AO_EBADOPTION  4
#define AO_EOPENDEVICE 5
#define AO_EOPENFILE   6
#define AO_EFILEEXISTS 7
#define AO_EBADFORMAT  8

#define AO_EFAIL       100

#define AO_FMT_LITTLE 1
#define AO_FMT_BIG    2
#define AO_FMT_NATIVE 4


/* --- Structures --- */
typedef struct ao_info {
int  type; /* live output or file output? */
char *name; /* full name of driver */
char *short_name; /* short name of driver */
        char *author; /* driver author */
char *comment; /* driver comment */
int  preferred_byte_format;
int  priority;
char **options;
int  option_count;
} ao_info;

typedef struct ao_functions ao_functions;
typedef struct ao_device ao_device;

typedef struct ao_sample_format {
int  bits; /* bits per sample */
int  rate; /* samples per second (in a single channel) */
int  channels; /* number of audio channels */
int  byte_format; /* Byte ordering in sample, see constants below */
        char *matrix; /* input channel location/ordering */
} ao_sample_format;

typedef struct ao_option {
char *key;
char *value;
struct ao_option *next;
} ao_option;


/* --- Functions --- */
/* library setup/teardown */
void ao_initialize(void);
void ao_shutdown(void);

/* device setup/playback/teardown */
int   ao_append_global_option(const char *key,
                              const char *value);
int          ao_append_option(ao_option **options,
                              const char *key,
                              const char *value);
void          ao_free_options(ao_option *options);
ao_device*       ao_open_live(int driver_id,
                              ao_sample_format *format,
                              ao_option *option);
ao_device*       ao_open_file(int driver_id,
                              const char *filename,
                              int overwrite,
                              ao_sample_format *format,
                              ao_option *option);

int                   ao_play(ao_device *device,
                              char *output_samples,
                              uint_32 num_bytes);
int                  ao_close(ao_device *device);

/* driver information */
int              ao_driver_id(const char *short_name);
int      ao_default_driver_id(void);
ao_info       *ao_driver_info(int driver_id);
ao_info **ao_driver_info_list(int *driver_count);
char       *ao_file_extension(int driver_id);

/* miscellaneous */
int          ao_is_big_endian(void);

さっと眺めると、定数・構造体を定義した後、 関数を定義してあるシンプルな物であることが分かる。

rubyのAOモジュールを作成する

ao/ao.hの情報から、ruby/dlを用いてrubyからlibaoを叩くためのモジュール、 AOを作成する。 基本的な使用例は リファレンスにサッと書いてあるので、それを参考にする。

module AO
  # DL::Importerのインスタンスメソッドを
  # AOモジュールの特異メソッドとして追加する
  extend(DL::Importer)

  # libao.so(Windowsならlibao.dll)を
  # ライブラリパスから検索しロードする
  dlload('libao.so')


  # ao/ao.hで定義されていた定数を定義する
  # ao/ao.h
  AO_TYPE_LIVE   = 1
  AO_TYPE_FILE   = 2

  AO_ENODRIVER   = 1
  AO_ENOTFILE    = 2
  AO_ENOTLIVE    = 3
  AO_EBADOPTION  = 4
  AO_EOPENDEVICE = 5
  AO_EOPENFILE   = 6
  AO_EFILEEXISTS = 7
  AO_EBADFORMAT  = 8
  
  AO_EFAIL       = 100

  AO_FMT_LITTLE  = 1
  AO_FMT_BIG     = 2
  AO_FMT_NATIVE  = 4


  # ao/ao.hで定義されていた構造体を定義する
  AO_Info =
    struct(['int  type',
            'char *name',
            'char *short_name',
            'char *author',
            'char *comment',
            'int  preferrd_byte_format',
            'int  priority',
            'char **options',
            'int  option_count'])
  AO_Sample_Format =
    struct(['int  bits',
            'int  rate',
            'int  channels',
            'int  byte_format',
            'char *matrix'])
  AO_Option =
    struct(['char   *key',
            'char   *value',
            'struct ao_option *next'])


  # 空の構造体は、voidのaliasとして定義する
  typealias('ao_functions', 'void')
  typealias('ao_device', 'void')


  # 未定義の型uint_32を、
  # unsignd intのaliasとして定義する
  typealias('uint_32', 'unsigned int')


  # 関数を定義する
  # (仮引数は型のみ記入する)

  # /* --- Functions --- */
  # /* library setup/teardown */
  # void ao_initialize(void);
  extern('void ao_initialize(void)')

  # void ao_shutdown(void);
  extern('void ao_shutdown(void)')

  # /* device setup/playback/teardown */
  # int   ao_append_global_option(const char *key,
  #                               const char *value);
  extern('int ao_append_global_option(const char *, const char *)')

  # int          ao_append_option(ao_option **options,
  #                               const char *key,
  #                               const char *value);
  extern('int ao_append_option(ao_option **, const char *, const char *)')

  # void          ao_free_options(ao_option *options);
  extern('void ao_free_options(ao_option *)')

  # ao_device*       ao_open_live(int driver_id,
  #                               ao_sample_format *format,
  #                               ao_option *option);
  extern('ao_device * ao_open_live(int, ao_sample_format *, ao_option *)')

  # ao_device*       ao_open_file(int driver_id,
  #                               const char *filename,
  #                               int overwrite,
  #                               ao_sample_format *format,
  #                               ao_option *option);
  extern(' ao_device* ao_open_file(int, const char *, int, ao_sample_format *, ao_option *)')

  # int                   ao_play(ao_device *device,
  #                               char *output_samples,
  #                               uint_32 num_bytes);
  extern('int ao_play(ao_device *, char *, uint_32)')

  # int                  ao_close(ao_device *device);
  extern('int ao_close(ao_device *)')

  # /* driver information */
  # int              ao_driver_id(const char *short_name);
  extern('int ao_driver_id(const char *)')

  # int      ao_default_driver_id(void);
  extern('int ao_default_driver_id(void)')

  # ao_info       *ao_driver_info(int driver_id);
  extern('ao_info *ao_driver_info(int)')

  # ao_info **ao_driver_info_list(int *driver_count);
  extern('ao_info **ao_driver_info_list(int *)')

  # char       *ao_file_extension(int driver_id);
  # extern('char *ao_file_extension(int)')

  # /* miscellaneous */
  # int          ao_is_big_endian(void);
  extern('int ao_is_big_endian(void)')
end

比較してみると分かるが、ao/ao.hで定義されているものをruby/dlに合わせ 機械的に定義し直しているだけである。

実際に使用する

作成したAOモジュールを実際に使用してみる。この時、関数の戻り値等はC言語で使用する際と同様に 適切に処理しなければならない。またerrnoの値を取得するには、 DL::CFunc.last_errorを用いる。

puts 'initialize'
AO.ao_initialize

puts 'setup default driver id'
drv = AO.ao_default_driver_id
if drv < 0
  puts 'usable audio output device is not found.'
  exit 1
end

puts 'setup sample format structure'
format = AO::AO_Sample_Format.malloc
format.bits     = 16
format.rate     = 44100
format.channels = 2
format.byte_format = AO::AO_FMT_LITTLE
format.matrix   = nil

puts 'open live device'
dev = AO.ao_open_live(drv, format, nil)
unless dev
  printf("errno: %d\n", DL::CFunc.last_error)
  exit DL::CFunc.last_error
end

puts 'play'
ARGV.each{|argv|
  if File.file?(argv)
    File.open(argv){|f|
      while buf = f.read(4096)
        if AO.ao_play(dev, buf, buf.size) == 0
          puts 'ao_play() failure.'
          exit 1
        end
      end
    }
  end
}

puts 'close'
if AO.ao_close(dev) == 0
  puts 'ao_close() failure.'
end

puts 'shutdown'
AO.ao_shutdown

あとは、作成したスクリプトを、引数に16bit・44.1KHz・2ch・Little Endianな音声の RAWファイルへのパスを付けて実行すれば、それを再生してくれる筈である。 WAVファイルはヘッダがあるため先頭に少々ノイズが入るが、再生することはできる。

参考文献

・xiph.org - libao
http://www.xiph.org/ao/

・Ruby 1.9.2 Reference Manual - dl
http://rurema.clear-code.com/1.9.2/library/dl.html

2011年5月24日火曜日

FreeBSD 8.2とZFSでiSCSI targetを作る

SolarisのZFSではiSCSI targetを楽に作成できるらしいが、FreeBSDのZFSでは未だにiSCSI targetがサポートされていない…らしい。そこで、netbsd-iscsi-targetを利用し、FreeBSDのZFSでも似たようなことをできるようにしてみる。

iSCSI target用ツールをビルド

portsに入っているのは少々古いようなので、NetBSDからソースで持ってくる。
# cd /usr/miku/src
# fetch http://ftp.jaist.ac.jp/pub/NetBSD/packages/distfiles/netbsd-iscsi-20110222.tar.gz
# tar xzf netbsd-iscsi-20110222.tar.gz
# ./configure --prefix=/usr/miku
# make
おそらくfuseが入っておらずinitiatorのビルドに失敗するので、libiscsiとtargetのみを手動でインストールする。
# cd src/lib
# make install
# cd ../target
# make install
もしソース持ってきて野良ビルドするのが嫌であれば、pkgsrcを導入しnet/netbsd-iscsi-targetを入れるという手もある。

ZVOLを作成

iSCSI targetで利用するボリュームを作成する。 ZVOLはZFS上に置かれ、ZFS同様にリサイズや圧縮が可能で、かつ普通のHDDと同じようにパーティションを作成したりファイルシステムを作成できる。
プールksysに10GBのボリュームiscsivol0を作成するコマンドは以下のとおり。
# zfs create -V 10G ksys/iscsivol0
ここで作成したZVOLは、/dev/zvol/ksys/iscsivol0に置かれる。

iSCSI targetを設定

iSCSIの設定ファイルは/etc/iscsiに置かれるので、最初にmkdir /etc/iscsiしておく。その中にファイルtargetsを作成し、以下のように設定を書く。
extent0 /dev/zvol/ksys/iscsivol0 0 10G
target0 rw extent0 10.39.39.0/24
使用するボリュームの範囲をextent0で指定し、公開されるtarget0のオプションでそれを指定すればよい模様。 複数公開する場合は、extent1・target1、extent2・target2...と追加していく。

iSCSI targetの起動

iSCSI targetの起動は、rootでiscsi-targetを実行するだけ。
# iscsi-target
Reading configuration from `/etc/iscsi/targets'
target0:rw:10.39.39.0/24
        extent0:/dev/zvol/ksys/iscsivol0:0:10737418240
DISK: 1 logical unit (20971520 blocks, 512 bytes/block), type iscsi fs
DISK: LUN 0: 10240 MB disk storage for "target0"
TARGET: iSCSI Qualified Name (IQN) is iqn.1994-04.org.netbsd.iscsi-target

接続テスト

Debian側にopen-iscsiによるiSCSI initiatorを用意し、iSCSIを実際に利用してみる。Debianにはaptitude install open-iscsiでインストールできる。

まずiSCSI targetのリストを受け取る。
# iscsiadm -m discovery -t sendtargets -p 10.39.39.40
10.39.39.40:3260,1 iqn.1994-04.org.netbsd.iscsi-target:target0
そしてiSCSIにログインする。
# iscsiadm -m node -T iqn.1994-04.org.netbsd.iscsi-target:target0 -l
Logging in to [iface: default, target: iqn.1994-04.org.netbsd.iscsi-target:target0, portal: 10.39.39.40,3260]
Login to [iface: default, target: iqn.1994-04.org.netbsd.iscsi-target:target0, portal: 10.39.39.40,3260]: successful
また、ログアウトは以下のように行なう。
# iscsiadm -m node -T iqn.1994-04.org.netbsd.iscsi-target:target0 -u
Logging out of session [sid: 1, target: iqn.1994-04.org.netbsd.iscsi-target:target0, portal: 10.39.39.40,3260]
Logout of [sid: 1, target: iqn.1994-04.org.netbsd.iscsi-target:target0, portal: 10.39.39.40,3260]: successful
あとはいつも通りfdiskするなりmkfsするなりして使うだけ。

余談

本当は、以下のようにzfsのオプションでiscsiの共有を指定できればよいのだが、このとおり怒られてしまう。
# zfs set shareiscsi=on ksys/iscsivol0
property 'shareiscsi' not supported on FreeBSD: permission denied
そこで、ZVOL作成時にオプションとして付けたところ、
# zfs create -V 10G -o shareiscsi=on ksys/iscsivol2
# % zfs get shareiscsi ksys/iscsivol2
NAME            PROPERTY    VALUE       SOURCE
ksys/iscsivol2  shareiscsi  on          local
ONにできてしまった。これは一体…

2011年4月29日金曜日

ruby 1.9.*でURL短縮サービスを作る

rubyでは、URL短縮サービスをWebサーバごと、しかも簡単に作ってしまうことができる。 その上、必要なライブラリはruby1.9.*標準添付ライブラリのみ。

ここでは、以下の機能を実装してみる。
  • /regのクエリにURLを渡しGETすると、元のURLとJSONで短縮したURLを返す
  • 短縮したURLはgdbmのデータベースに保存し、サーバ再起動後も参照できるものにする
  • 短縮にはMD5ハッシュを用いる(このままでは長いがとりあえず)

ソースコードは以下のとおり。
#!/usr/bin/env ruby1.9.2
#-*- coding: utf-8 -*-

require 'webrick'
require 'uri'
require 'gdbm'
require 'json'
require 'digest/md5'

# このサーバのURLを指定
BASE_URL = 'http://localhost:3939/'
# 短縮URLのデータベースファイル
db = GDBM.new('url.db')

# Webサーバを作成
sv = WEBrick::HTTPServer.new(:Port => URI.parse(BASE_URL).port)
trap(:INT){sv.stop}

# 短縮されたURLを受け取り、転送するServletを/にmount
sv.mount_proc('/'){|req, res|
  digest = req.path.gsub(/^\//, '')
  if digest =~ /^[0-9a-z]+$/i &&
      url = db[digest]
    res.set_redirect(WEBrick::HTTPStatus::Found, url)
  else
    res.body = 'unknown digest'
  end
}

# URLを短縮してJSONで返すServletを/regにmount
sv.mount_proc('/reg'){|req, res|
  url    = req.query['url']
  if url =~ URI.regexp && 
      URI.parse(url).scheme =~ /^(https?|ftp)$/
    digest = Digest::MD5.hexdigest(url)
    db[digest] = url
    res.body   = JSON.dump({'digest'=> digest,
                             'turl' => BASE_URL + digest,
                             'url'  => url})
  else
    res.body = 'bad url'
  end
}

# サーバを開始
sv.start
これだけでOK。
http://localhost:3939/add?url=http://hachune.net/http://hachune.net/を短縮URLサービスに登録し、返されたhttp://localhost:3939/fd9f9871bca66e8b49a919138982c9c1にアクセスすると、http://hachune.net/に転送される。
Apacheの用意も新規ライブラリの導入も要らず、とってもお手軽。
あとは、MD5のままだとどう見ても長すぎるので、可能な限り衝突が起きにくくかつ文字列の長さが短くなる関数を作る必要があるが、まぁその辺は適当に。

2011年4月21日木曜日

OpenBSDで6to4なIPv6ルータを作る

IPv4アドレスがついに枯渇! …ということで、自宅のOpenBSD/macppcなルータでIPv6接続ができるようにしてみた。 今回は、固定IPv4アドレスが既にあるので、6to4を用いてグローバルIPv6アドレスを設定する。

固定IPv4アドレスを割り当て

最初に、IPv4アドレスをルータに割り当てる。 いつも通り、/etc/hostname.pppoe0に設定を書く。 当然ながら、ユーザ名とパスワードは自分の契約しているプロバイダのものに置き換える。 ここで用いられるインターフェースは、gem0がWAN側ネットワークに接続されている物理インターフェース、 pppoe0がPPPoE接続で用いられるインターフェースである。
# /etc/hostname.pppoe0

inet 0.0.0.0 255.255.255.255 NONE \
pppoedev gem0 authproto chap \
authname 'username' authkey 'passwd' up
dest 0.0.0.1
!/sbin/route add default -ifp pppoe0 0.0.0.1

6to4のトンネルを作成

次に、6to4のトンネルインターフェースを設定する。 OpenBSDのトンネルインターフェースはgifであるので、/etc/hostname.gif0に設定を記入する。
6to4トンネルの宛先は、192.88.99.1で固定の模様。

6to4では、「2002:IPv4アドレスを16進表記したもの::/48」を、 このルータのIPv6ネットワークとして利用することができる。 このルータのグローバルIPv4アドレスは、180.131.124.186なので、 2002:b483:7cba::/48がそれに当たる。
10進→16進変換は、printfコマンドでprintf "%x %x %x %x\n" 180 131 124 186のようにすれば簡単。
# /etc/hostname.gif0

up giftunnel 180.131.124.186 192.88.99.1
inet6 2002:b483:7cba::1 64
!route add -inet6 default 2002:b483:7cba::1

IPv6フォワーディングを有効化

このルータだけがIPv6アドレスを持っていてもあまり面白くないので、 IPv4同様、IPv6フォワーディングを有効化する。 /etc/sysctl.confの以下の部分のコメントアウトを外すだけでOK。
# /etc/sysctl.conf

net.inet6.ip6.forwarding=1

内部インターフェースへのIPv6アドレスの割り当て

内部ネットワークを2002:b483:7cba:39::/64とし、 内部向けインターフェースaxe0に、2002:b483:7cba:39::1を割り当てる。
# /etc/hostname.axe0

inet alias 10.39.39.1 255.255.255.0
inet6      2002:b483:7cba:39::1 64

router advertisement daemonを有効化

IPv6では、DHCPを用いずに動的にIPv6アドレスを割り当てることができる。 OpenBSDでは、rtadvdにより自動割当を行う。 有効化の設定は、/etc/rc.conf.localに、rtadvd_flagsを追加し、 引数に自動割当を行うインターフェース名を記入する。
このrtadvdにより、axe0以下のIPv6対応コンピュータには、axe0のネットワーク 2002:b483:7cba:39::/64の中から自動的にアドレスが割り当てられる。
# /etc/rc.conf.local

rtadvd_flags="axe0"

pfにIPv6のルールを追加

IPv6でも当然ながらフィルタリングは必要なので、pfを設定する。 最低限許可が必要なのは、6to4トンネルのパケットと、自動割り当てに必要なパケットである。 これ以外は必要に応じて追加する。
# /etc/pf.conf

# Pass 6to4 tunnel
pass  out on $ext_if proto ipv6 \
      from pppoe0 to 192.88.99.1
block in log (all) inet6

# Pass Router Solicitations (RS)
pass in  log (all) on axe0 \
     inet6 proto icmp6 \
     icmp6-type routersol

# Pass Router Advertisement (RA)
pass out log (all) on axe0 \
     inet6 proto icmp6 \
     icmp6-type routeradv
正しくグローバルIPv6アドレスが割り当てられているか確認したいときは、ping6 ipv6.google.comするなりlynxで開くなりなんなりと。

debianやubuntuでmikutterビルド環境を作る

あまりに何度もmikutterビルド環境を構築しているので、そろそろ纏めてみる。

ベースシステムを構築

/var/rootにビルド用のubuntu nattyをdebootstrapにて用意する。 この中で全ての作業を行うことになるので、バグがあったり操作ミスしたりしてもシステム全体に被害は及びにくくなる。また、それぞれ違うディストリビューションやバージョンを用いることもできる(debian sidやubuntu lucidなど)。
ホストがamd64な環境ならば、debootstrapにオプション--arch i386を追加することで、i386な環境を構築することも可能。
# aptitude install debootstrap
# mkdir /var/root
# cd /var/root
# debootstrap natty ./natty

基本システムをmikutterビルド用にセットアップ

新たに作ったubuntu nattyの中にchrootする。 chroot後は、必要なファイルやパッケージを、通常のシステム同様にapt等を用いて準備する。
# chroot /var/root/natty /bin/bash
# apt-get update
# apt-get install \
gcc make  pkg-config subversion libnotify-bin wget \
libgtk2.0-dev libsqlite3-dev libssl-dev zlib1g-dev
もしsysやprocが見つけられないことが原因でエラーになるようであれば、 chrootから一度抜けだしてマウントしておく。
必要以上にマウントすると、なんらかのミスでとんでもないことになる可能性もあるので注意。
# mount -t proc proc ./natty/proc
# mount -o bind /dev ./natty/dev
# mount -t devpts devpts ./natty/dev/pts
# mount -t sysfs sysfs ./natty/sys

Rubyをビルド

そしてchroot内でruby1.9.2をビルドし、必要なrubyパッケージをgemで追加する。
# cd /usr/src
# wget ftp://ftp.ruby-lang.org/pub/ruby/ruby-1.9.2-p180.tar.gz
# tar xzf ruby-1.9.2-p180.tar.gz
# cd ruby-1.9.2-p180
# ./configure --prefix=/usr/ruby192 --program-suffix=192
# make && make install
# /usr/ruby192/bin/gem192 install ruby-hmac gtk2 sqlite3

mikutterをcheckout

最後にmikutterをcheckoutすれば、mikutterビルド環境のできあがり。
# cd /usr/src
# svn co svn://mikutter.hachune.net/mikutter/trunk/ mikutter
あとはsshのX転送やvnc等などを利用して、mikutterが動くかどうか試す。
おそらく日本語がまともに表示されずガッカリすることになるので、
# apt-get install ttf-vlgothic
などとしておくとよいかもしれない。

2011年4月20日水曜日

心機一転

数年間書き続けていたBlogの整理がそろそろつかなくなったので、新しく作りなおしてみることにする。 hachune.*のサーバ構築や管理、プログラミング関係の話題が多くなりそうな予感。