2003年05月01日(木)

ゾーン形式BCDのゾーンとは何か

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - ゾーン形式BCDのゾーンとは何か

BCDにはゾーン形式とパック形式がある。ゾーン形式は4ビットのBCDの上位にゾーンと呼ばれる謎のビットを付け足し1バイトにしたものである。ゾーンには、ASCII系なら00112、EBCDIC系なら11112を使う。そうするとあら不思議、BCDの0と文字の0が同じ表現になるのである。それはそれとして、その部分をどうしてゾーンと呼ぶのかが気になるのだ。

調べてみるとそれはパンチカードに由来するようだ。カードには0から9の印があり、そのどれかに穴を空けることによって数を表す。この部分をdigit rowsと呼ぶ。カードの上部、0の上の部分には何も書かれていない部分があり、実はそこをzone rowsと呼ぶのだ。なんでそう呼ぶのかは奥が深すぎて分からない。パンチカードシステムの生みの親であるホレリスさん周りを調べれば分かるかも。ともかく、このゾーンの部分を併用して文字を表すのである。そしてこれを素直にデータに置き換えたのがゾーン形式のBCDというわけだったのだ。

2003年05月06日(火)

リンクを表現するGUI部品

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - リンクを表現するGUI部品

別途定める何かの作者の情報として、作者のサイトのURLを表示するというのは珍しいことではないと思う。そしてURLがあるならば、簡便な方法をもってしてブラウザで開きたいと思うのもそれほど変わったことではないだろう。問題はそこだ。HTMLのレンダラでもない普通のウインドウに表示されるURLで、簡便な方法でブラウザで開くことができるということを示すにはどうしたら良いだろうか。青い字に下線でURLを表示するというのがベターな方法だろうが、何となくその方法はとりたくない感じ。コンテキストメニューでというのも、そこにコンテキストメニューがあることが分からないと無力っぽい。

2003年06月07日(土)

if文のネストのピラミッド

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - if文のネストのピラミッド

下手糞プログラム大全。大全というほど量は無いが、if文のネストのピラミッドは見たことあるなあ。途中の処理が全部成功しないとできない処理で、途中のステップごとに適切なエラーメッセージをあれするとかいう場合、if文ピラミッドが一番簡単に書ける構造だったりするのがC言語の悲しさ。ピラミッドにしないように書こうとすると逆に分かりにくくなったり。やはり例外機構はいいよねえ。

2003年07月03日(木)

blogツールのフロントエンドアプリ

カテゴリ: Web, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - blogツールのフロントエンドアプリ

.Mac上でblogを運営するためのアプリケーションであるiBlogというソフトウェアがある。MacOS Xのアプリケーションで必要なHTMLを生成して、それを.Mac上に転送することでblogを実現するという仕組みである。そういう仕組みはどうでもよくて、私が感心したのは、よくある装飾付きテキストを編集するUIのようにBとかIとかいうツールボタンをあれするだけで、それが反映されたHTMLが生成される点である。そうそうこれこれって感じ。物理マークアップなのはあれだが、とりあえずCocoaっぽいあれでできるという実例があるわけである。

そこでという訳ではないが、鳩丸.NETのようなものにMacOS X用フロントエンドを付けたら大変よさげなものができるのではないかと思う私。幸いにして新生鳩丸掲示板♯記事128にて鳩丸.NETのソースが公開されている。……えーと、インサイド鳩丸.NETとか希望(謎)。

2003年07月05日(土)

サポート掲示板にアクセスログを組み込む

カテゴリ: サイトメンテ, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - サポート掲示板にアクセスログを組み込む

サポート掲示板にアクセスログを組み込もうと思ったら、思いもよらず苦労した。トップページと日記のアクセスログのシステムを統合した時に、妙に凝ったりして簡単に追加できるような仕組みにしてあるので追加自体は何も問題無い。が、長らく止めていた過去のアクセスログの概略を作ってログ自体はメーで私に送り付けるというシステムを再始動させようとしたらなぜか動かなかったのだ。動かない原因は統合時のバグなのだが、いくら修正したやつをアップロードしてもいっこうに改善されないのだ。

原因は分かってしまえば簡単。2ヶ所に同じモジュールが入っていて、検索順位が低い方の場所のモジュールを必至に書き換えていたので、何も変化がなかったというわけだ。こんなややこしいことするなよ>過去の私。というか場所を移動したとかで形骸化したファイルはさっさと消すなり名前を変えるなりしておけという教訓だろうか。

2003年07月11日(金)

XML-RPC

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - XML-RPC

ひょんなことからXML-RPC仕様を見たり。認証とかセッションとかそういうものは何も無く、ただ単に関数呼び出しの引数と戻り値をXMLで表しただけのもののようだ。それにしてもなんか冗長な要素が多いような。<value><int>128</int></value>となるのは、value要素に直接テキストを記述した場合に文字列になることから、最初は型情報が無かった名残なのだろう。でもparam要素とかarray要素の子のdata要素とかはあっても邪魔なだけなような気が。

2003年07月13日(日)

機械イプシロンを理解した

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 機械イプシロンを理解した

ようやく文章をでっち上げられる程度には機械イプシロンが理解できたので、これでようやく基礎コンが書ける。

2003年07月23日(水)

半角カナ

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 半角カナ

半角カナ使用者保護運動はもはやその役目を終えた感を個人的に勝手に持っていて放置プレイ状態だったのだが、「えび日記: 2003年7月23日: 検定で GO!」で紹介されている設問を見ると、そうでも無いのかもしれない。ちなみに私は3だと思いました。確かJISでは半角カナの領域は予約領域としたのでもう使っちゃ駄目とかになっていたはず。

2003年09月02日(火)

変数=箱モデル

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 変数=箱モデル

ruby-listでRubyの変数を説明するのに「変数=箱」というのはどうよ? という話題でそれなりに盛り上がっている(スレッドの大元は[ruby-list:38183])。変数を箱、つまり何か実体のあるものが入る容器とすると、そこに入る何かについて説明しなければならない。そこそこ知っている人は参照値とか謎の値を思い浮かべて納得してしまうのだが、そのような値はRubyレベルでは存在しないので、箱モデルは余計にややこしくしているだけという気がする。

それはともかく、目からうろこだったのが[ruby-list:38324]。そうか、1つの物は必ず1つの箱にしか入らないというのは私たちが勝手にそう思っているだけで、別にそうでない世界があってもいいんだし。

あと、箱モデルでRubyの変数モデルをきちんと説明するには「箱の中身
は参照」と考える必要があるという議論になっていますが、久野先生な
んかは「二つの箱に同時に同じオブジェクトが入っていると考えても、
別にいいんじゃないか」とおっしゃっておられました。

2003年09月19日(金)

疑りぶかいあなたのためのオブジェクト指向再入門

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 疑りぶかいあなたのためのオブジェクト指向再入門

なにげに話題になっている「疑りぶかいあなたのためのオブジェクト指向再入門」。なんかクラスベースのオブジェクト指向言語に縛られてしまっているのが残念な感じ。カプセル化と同じように、クラスはオブジェクト指向に必須なものではないのだ。大抵のオブジェクト指向言語ではクラスは必須であり、それを理解しないと動くコードは書けない。ただでさえ手続き指向と考え方が逆なのに、その上クラスシステム理解しないと駄目というのがオブジェクト指向を理解する妨げになっているのではないかと思う。

2003年09月21日(日)

人の身長などにたとえてド=モルガンの法則を表す

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 人の身長などにたとえてド=モルガンの法則を表す

人の身長などにたとえてド=モルガンの法則を表すというのはどうやるのだろう。いや、そういう質問のメールが来たので。そんな喩え方はまったく想像できないので、むしろ訊き返してしまったのだが、分からないということで納得してしまったらしく、結局教えてもらえなかった。うわー、すごく気になるんですけど。

2003年09月22日(月)

続・人の身長などにたとえてド=モルガンの法則を表す

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 続・人の身長などにたとえてド=モルガンの法則を表す

RTCで昨日のド=モルガンの法則のあれを訊いてみたら、即座にフィンローダさんが答えてくれた。さすがはフィン様。要するに範囲指定で表すのだ。身長が160cm〜170cmの人を表すには、「160cmより小さいか170cmより小さいのではない」と「160cmより小さくなく、かつ170cmより小さくない」の2通りがあるという訳だ。なるほどー。

2003年09月25日(木)

コンボボックス?

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - コンボボックス?

昨日、RTCで書きかけのGUI部品図鑑のコンボボックスを見せたときにZnZさんからいくつかのスクリーンショットを貰ったのだが、GTK1とやらのをあらためて見てみると、これはコンボボックスというかドロップダウンリストではなくて、ポップアップメニューではないだろうか。ドロップダウンしていないし、リストボックスでもないような気がする。

2003年09月26日(金)

B-TRONの実身仮身システム

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - B-TRONの実身仮身システム

ネットワーク型ファイルシステムという最大の特徴が取り上げられなくて、文書にファイルへのリンクが埋め込めるという点ばかり取り上げられるのは何故だろう。

確かにその方法を標準化している点は偉いが、ファイルへのリンクを埋め込むだけならMacOSのエイリアスレコードを使っても同じことができたりする。現にメモパッド系のユーティリティで、ファイルやディレクトリをドロップするとアイコンが埋め込まれ、それをダブルクリックするとファイルが開かれるという機能を持ったものが存在する。

2003年10月09日(木)

パスワードはCGIスクリプトに直接書かない

カテゴリ: セキュリティ, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - パスワードはCGIスクリプトに直接書かない

IPA ISEC セキュア・プログラミング講座

基本的なことばかりだが、「2-2. スクリプトに埋め込まれたDBパスワード」はちょっと目からうろこだった。曰く、スクリプトにDBパスワードを書いてはいけないと。ならどこに書くのん? というと、ライブラリやモジュールなど、Webから見えないところにあるファイルに書けと。こうすればうっかりスクリプトのコードがWebに晒されてしまったとしても、パスワードは見ることができない。なるほど。「人に見られてはいけないものは、そもそもWebから見えるところに置いてはいけない」という原則は、コードの一片にでも適用すべきものということなんだねえ。

2003年10月13日(月)

JavaScriptで音を鳴らす方法

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - JavaScriptで音を鳴らす方法

JavaScriptでBGSOUND要素のsrc属性にURLをセットすることにより任意のタイミングで音が鳴らせるらしい。これと同じことをOBJECT要素を使って、というかStrictなHTMLで実現できないだろうか。

2003年10月24日(金)

ARMのTrustZone

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - ARMのTrustZone

ZDNN:ARMの最新セキュリティと省電力技術に迫る

TrustZoneって要は68000のスーパーバイザーモードと同じようなもののような気がする。68000のアドレス空間はユーザー空間とスーパーバイザー空間に分かれていて、スーパーバイザー空間に割り付けられている装置はユーザーモードで実行されているコードからはアクセスすることができない。この機能を今風に表現すれば、まあセキュリティ機能なのかもしれない。

ARMのサイトにあるTrustZone™ Technologyによると、monitor mode というのがなんでもありのスーパーバイザーモードに当たり、そこで非moitor mode時にアクセスできるアドレス空間を切り替えるという感じらしい。権限階層によってアクセスできるアドレス空間がかわるのではないという辺りがミソ?

2003年10月27日(月)

FTPで転送したファイルのサイズが0になる

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - FTPで転送したファイルのサイズが0になる

NetFinderで日記をアップロードしようとしたら、なぜか転送したファイルのサイズが0になるという謎の現象が発生して難儀した。容量がいっぱいでquotaの制限に引っ掛かっているというわけでもないようだ。さては先月分をサービス停止予告通知が来るほど滞納したから今月は早めに止めたとか? とか妄想を膨らましてしまったが、原因が分かった。ネットワークの設定のパッシブモードのチェックが外れていた。なるほど。制御用の接続からの指令によりファイルが更新されるけど、データ用の接続ができないので中身が空になるのか。勉強になります。

Cocoaのコントローラクラス

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - Cocoaのコントローラクラス

MacOS XのCocoaでは従来からMVCが充実していたが、10.3では汎用的なコントローラクラスが追加された。そんなNSControllerクラスの子クラスにNSUserDefaultControllerというのがある。環境設定ウインドウを作ると、必ずウインドウ上に乗っているコントロールとNSUserDefaultsの間を取り持つためのコントローラが必要になる。が、これはモデルが決まっているので、アクセッサを書いているような感じで決まり切ったコードの羅列になる。この決まり切ったことをやってくれるのがNSUserDefaultControllerというわけだ。なのでInterfaceBuilderで結び付きを指定してやれば、さくーりと環境設定ウインドウが作れてしまうのだ。ひゃっぽー。

2003年10月28日(火)

Key Value Observer

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - Key Value Observer

NSControllerがどうやってモデルとビューと通信を行っているのか非常に謎だったのだが、なんとNSObjectに非形式プロトコルとして存在していたとは。モデルはともかくとして、ビューの方はNSViewか、もしくはNSControllかNSCellの辺りだと思っていた。それがよもやNSObjectとは。つまり何でもビュー役になれるという、かなり汎用的な作りになっている。

データ受け渡しのインターフェースがKey Value Coding、データの変更通知がKey Value Observerになっているらしい。Key Value Codingは以前からあるのは知っているが、Key Value Observerなんてあったっけ。

2003年11月21日(金)

日記用XML DTD

カテゴリ: Web, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 日記用XML DTD

ふと思ったのだが、*MLとか名付けられている勢いの、日記用のXML DTDは無いのだろうか。世間では日記やブログが流行りまくっているわけで、これだけ流行っているのだからそういうものがあっても不思議ではないと思うのだが。いえ、決して自分で考えるのが面倒とかそういうことではなく(謎)。

2003年12月08日(月)

整数演算オーバーフローの脆弱性

カテゴリ: セキュリティ, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 整数演算オーバーフローの脆弱性

RTCでrsyncの脆弱性の話でちょびっと盛り上がる。malloc()で確保するメモリサイズはchar単位で指定するので、int配列を確保する場合は配列の長さにsizeof(int)を乗じることになる。配列の長さ自体はsize_tに収まるとしても、その整数倍が収まるとは限らない。オーバーフローするとまず間違いなく元の数よりも小さくなるので、ここに脆弱性を生むポイントがある。たとえ配列の添え字で範囲チェックをしたとしても、実際に確保された領域はそれよりも狭いので、バッファオーバーフローが発生する。

で、Javaなら大丈夫という話になったのだが、実際のところどうなのだろう。確かにJavaなら不意なアクセスは行いようが無いが、そのかわりに例外が投げられる。キャッチされない例外の末路はプロセス終了であり、もしそれが何かのサーバプロセスであればDoS攻撃が可能ということになる。たとえJavaプログラマであれど、配列の範囲を超えてアクセスしたから何々するというコードを全ての配列アクセスに対して書くことはないと思うので、JavaであってもDoS攻撃は可能だと思われる。

2003年12月15日(月)

Perl中毒?

カテゴリ: Java, Perl, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - Perl中毒?

久しぶりにJavaのコードを書いていて、文字列リテラルを書くときに先行して指が勝手に '' を打っているのはPerlの書きすぎとか思った。「変数名の頭に$はいらないんだっけ」と心配になるのはObjecttive-Cの時に体験したのでもうあんしん(謎)。

2003年12月21日(日)

ピュアRubyなDBMS

カテゴリ: Ruby, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - ピュアRubyなDBMS

Pure Rubyなデータベースを探してみる。望みとしては、別途常駐プロセスを必要とせず、CGIプログラムからさくっと利用できる感じのものが欲しい。できればPerlのDBD::AnyDataみたいに、DBIインターフェースから使えるようなものだと、本物のDBMSに置き換えることもできるのでナイスなのだが、そこまでは高望みしない。ということでRAAで検索した結果、以下の3つがそれっぽい。

ruby_rdo
ソースコードを覗いたらメソッドの定義の中身が無くて「これはインターフェースクラスですか?」という勢いだったのだが、とりあえず作ったけど実装はまだよということらしい。RAAのステータスに書いてあったjust started, not yet to releaseというのはそういうことでしたか。というかそんなものをRAAに登録するなと言いたい。
rdodb
ドキュメントもサンプルコードも無いし、はっきり言ってよく分からない。doc/rdodb.en.rdには一言TODOと書いてあるだけという放置プレイさ。まあ気持ちは分からなくもないが、ソースコードがドキュメントというのはご遠慮したい(謎)。
fsbd
ドキュメントもしっかりしていてナイスなのだが、私が求めているものがそれなのかちょっと疑問。

ということでいっそ自分で作ってしまおうかとか思ったり。機能的にはNewtonのsoup相当で十分なので、それを参考にして作ろうかなあ。

2004年02月14日(土)

真も日ブに自動リロード機能を追加した

カテゴリ: サイトメンテ, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 真も日ブに自動リロード機能を追加した

なんとなくサポート掲示板で要望のあった新も日ブの自動リロード機能を作ってみたりする。最初はもっとちゃんとインジケータとかも付けようと思っていたのだが、面倒くさくなったので(早っ)停止/始動ボタンだけになった。最初、なぜか更新されなかった。よく考えてみれば当たり前で、新も日ブが毎時ちょっきりにチェックを開始するので、毎時ちょうどにリロードしてもまだ更新されていないので中身が変わらないというわけだ。とはいえ、毎時ちょうどからどれくらい待てば新も日ブの更新が完了しているかというのは分からない。タイムアウトが続発すればかなりの時間がかかるだろう。

ZnZさんから、更新時刻を見て更新されていなかったら別途定める一定期間待って再度リロードするという案が出た。つまり、今の時刻の「時」と更新時刻の「時」が異なっていれば再度リロードするというわけだ。普通に表示したときは必ず今の時刻の「時」と更新時刻の「時」が同じになる(新も日ブがコケていなければ)ので、いきなりリロードの嵐になることは無い。

では早速と思ってdocument.lastModifiedでごにょごにょやろうとしたら、このプロパティで取得できるのはDateオブジェクトではなく文字列という罠。しかもこの文字列、環境によって形式が変化し、あまつさえ自らが作成した時刻文字列なのにDateクラスでパースできないブラウザがあるという。ダメじゃん。表示することにしか使えません……

2004年02月28日(土)

Python

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - Python

Pythonの本を読んでみる。なんとなく非オブジェクトな部分がごちゃまぜになっているオブジェクト指向言語というイメージがあったのだが、実際は全てがオブジェクトの完全なオブジェクト指向言語であった。ところどころ手続きにオブジェクトを食わせるというオブジェクト指向っぽくない部分があるのは、オブジェクト指向言語ではなかった頃の名残なのだろうか。演算子など、とにかくオブジェクトに働きかけることはすべて特殊な名前のメソッド呼び出しとして処理されるというのが非常に分かりやすい。関数オブジェクトの関数呼び出しも__call__というメソッド呼び出しになるというのがなんだか凄い。

Pythonのクラスはちょっと変わっていて、クラスの実体は関数オブジェクトで、その関数オブジェクトがコンストラクタとなるようになっている。JavaScript? とか思ってしまった。

2004年03月03日(水)

インスタンス変数という言葉の解釈

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - インスタンス変数という言葉の解釈

[RbJML:12913] Re: インスタンス 変数より。

> > Dim dog, cat As Animal
> > dog = New Animal
> > cat = New Animal
> > このときのdogやcatにあたるのが、インスタンス変数じゃないですか?

そうです、インスタンス化した時のローカル変数を、インスタンス変数って
呼ぶ人がいるみたいです。

ううむ。とするとクラス変数はクラスが入っている変数という事になるのだろうか。と思ったら、そう呼ぶ人達は一般的な意味の方のインスタンス変数のことをクラス変数と呼ぶらしい。となると一般的な意味の方のクラス変数の呼び名がなくなってしまうのだが、どうしているのだろう。というかREALbasicにはクラス変数にあたるものが無いのかも。それにしても「インスタンス変数」という言葉だけでここまで盛り上がれるものとは。

2004年04月14日(水)

int ÷ int = Rational?

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - int ÷ int = Rational?

[ruby-list:39453] Re: int/int in Ruby2?からのスレッド。おおむね int ÷ int = int というのは数学的に不自然だからデフォルトが int ÷ int = Rational になったらなーという話。

int ÷ int = int じゃないと何か落ち着かないのは、実数演算なんて滅多に使わないので無駄という貧乏性ゆえの気持ちなのだろうか。でも、Perlだとすべて実数演算なので、そういうものだと分かっていれば大丈夫なのかもしれない。もっとも、いまさら / 演算子の挙動を変えるのは、たとえバージョンが1から2に変わるということであっても非互換過ぎる感じがするので、採用されない感じがする。せめてはじめから整数除算演算子と実数除算演算子があれば。/ 演算子を整数除算にしても実数除算にしても、面倒くさい問題が発生しそうだ。

2004年04月21日(水)

TCPのRSTを投げかける攻撃

カテゴリ: セキュリティ, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - TCPのRSTを投げかける攻撃

スラッシュドット ジャパン | TCPに容易にDoS撃を可能とする脆弱性

もはやこんなところにも気を遣わなければならない時代になったんだなあ。というか、ただひたすらパケットを投げつけると、うん十万分の一の確率で成功するかもしれないというつまらない攻撃をやるような人がいそうな時代になったというか。攻撃成功の確率が上がるのは何か新しい手法が編み出されたとかではなくて、現状を鑑みたら考えているものとは違ったということのようだ。

TCPでは輻輳制御の為にシーケンス番号とウインドウサイズという情報をやりとりしている。シーケンス番号は簡単に言うとストリーム上のバイト位置で、「この位置のデータです」とか「この位置まで受け取りました」という様に使用する。シーケンス番号の初期値は個々の接続ことに決められ、接続開始時に「シーケンス番号の初期値はxxです」というようなやり取りが行われる。様々な都合上、この初期シーケンス番号は接続ごとにバラバラの値が使われる。

ウインドウサイズは受信バッファのサイズで、受信完了のシーケンス番号とウインドウサイズによって「今受け取れるのはストリーム上のここからここまでです」という情報を刻々と相手とやり取りしている。この範囲外のパケットが来た場合、「期待しているのはこのシーケンス番号からのデータです」と答えてパケットを突っ返すようになっている。

RSTはTCPのパケットに付くフラグのひとつで、接続のリセットを示す。プロセスがTCPで接続中に異常終了してしまった場合、OSは接続の相手にこの接続がもはや有効ではないことを通知しなければならない。このような時に使用するのがRSTフラグで、切断というより断線に近い形態の切り方である。

やり取りされるシーケンス番号はストリームを隙間なく埋めなければならないので、受取り可能なシーケンス番号は接続の両端のホストしか知りえないはずである。たとえ知らなくても総当たりで行けば、いつかは有効なシーケンス番号にヒットするが、シーケンス番号は32ビット符号無し整数なので、攻撃成功の確率は1/232となる。

が、IPはパケットが到達する順序も、到達するかどうかも保証していないので、RSTが来る以前のパケットが失われているかもしれない。続き番号のシーケンス番号を期待しているとうまくRSTによって切れない可能性があるので、ウインドウサイズ内なら有効なRSTとしているのだと思われる。現在よく使われるウインドウサイズは割と大きめで、たとえばWindows XPでは初期ウインドウサイズは64KBである(スケーリングオプションを使用しない場合の上限)。つまり無作為に選んだシーケンス番号のRSTパケットがヒットする確率がシーケンス番号ちょっきりより65536倍上高いということになるわけだ。

2004年07月10日(土)

FPROGソーシャルネットワーク

カテゴリ: Web, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - FPROGソーシャルネットワーク

ソーシャルネットワークというのはある意味出会い系な訳で、プログラマーの出会い系フォーラムであるFPROGはソーシャルネットワーク化するのが良いかもなどと思ってみたりした。システム的には会員同士の関係情報がちょっと面倒なだけの、単なる会員限定の掲示板やらの集合体なので、気合いがあれば作るのは難しくはない。というか私が思うくらいだから既に誰かがオープンソースで作っていたりしそうだがどうなのだろう。

2004年07月15日(木)

スワップファイルからパスワードが漏れる

カテゴリ: Cocoa, Mac, セキュリティ, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - スワップファイルからパスワードが漏れる

スラッシュドット ジャパン | Mac OS X: スワップファイルからパスワードが漏れる?」より。

パスワードを保持していたメモリ領域がスワップアウトされてしまうと、暗号化されていないパスワードがHDDに書き込まれてしまうという話。スワップファイルをgrepすると、というのはさほど重要なことではない。スワップファイルはrootでなければ読めないし、rootであればprocfsなどからプロセスのメモリ空間をいくらでも覗くことができるので、スワップファイルをあさるという間接的な方法を取る必要は無い。問題は生パスワードがHDDに意図せず書き込まれてしまうことだ。

ファイルは、たとえその存在が消去されたとしても、その存在を復活させたり、または内容の一部を取り出すことが可能である。また、磁気記憶装置の記録内容はかなりしつこく消えないので、なにかのデータで上書きされたとしても、パスワードを取り出されてしまうかもしれない。そんなところに生パスワードが書き込まれてしまうのはいただけない。

このような問題をなるべく避ける為の、プログラムでパスワードを扱う際の定石というものがある。

  • パスワードを書き込むメモリ領域を物理メモリに固定する
  • 使い終わったら速やかにゼロクリアする

言われてみればごもっともで、実施するのは難しくはない感じがするが、ではCocoaでこれをどうやるのかと言われるとかなり困る。まずメモリ領域を物理メモリに固定する方法がFoundation KitにもApp Kitにも無い。CarbonにはHoldMemory()というAPIがあるが、これをCocoaとまぜこぜで使う方法が思い浮かばない。使い終わったらゼロクリアの方はもっと致命的で、文字列オブジェクトを破棄する前にゼロクリアしたらオブジェクトが壊れ、破棄した後にクリアしようとしたら無効なメモリアクセスが発生するかもしれず、二進も三進も行かない。自前でSecureStringクラスなどというものを作れば、自分で生成して自分で破棄する分には何とかなるが、ライブラリが返してくる文字列に対しては無力である。

この辺りの問題はCocoaだけでなく、メモリ確保と破棄というプリミティブな操作をプログラマが明示的に行わない処理系全てに当てはまるのではないだろうか。Ruby、Perl、Java、C#はともかく、C++も例外ではないと思う。そういえば一応セキュア度アップに邁進しているMicrosoftの、.NET Frameworkではその辺りはどうなっているのだろう。

2004年08月22日(日)

オープンソースなソーシャルネットワークシステム

カテゴリ: Web, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - オープンソースなソーシャルネットワークシステム

あるだろうとは思っていたが、やっぱりあったオープンソースなSNS。

OpenSNSのPerlを除いて(物はまだないけど)、全てPHPなのはやっぱり作りやすいからなのだろうか。PHPというとHTMLの中にコードが埋まっているというものを想像するのだが、wawawaのソースを見てみたら、全くHTMLが記述されてなくて驚いた。どうやら動的にHTMLを組み立てて出力しているようだ。もはやHTML埋め込みという部分は形骸化していますか。

2004年11月07日(日)

オブジェクト指向言語の種類

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - オブジェクト指向言語の種類

はてなダイアリー - sumim's smalltalking-tos - オブジェクト指向の概念の発明者は誰ですか?

オブジェクト指向言語には複数のタイプがあるという、なかなか興味深い話。以前、どこぞの掲示板で、メッセージ送信を関数呼び出しにたとえて説明していたことに対してあれやこれやと盛り上がっていて、私もさすがにそのたとえはどうかなと思っていた。その時に思ったのだが、どう考えてもC++のメンバ関数呼び出しとメッセージ送信には共通点が無い。同じオブジェクト指向言語なのに、こんな基本的な部分でこんなに差があるのはどうしてなのか。答えは簡単。同じじゃなかったのだ。

そういう点から見ると、Objective-Cはかなり気持ち悪い言語ではないだろうか。C言語に“メッセージ送信”の仕組みを導入して、“抽象データ型のスーパーセット”を実現しているという、なにか相容れない2つを無理やり混ぜ合わせた感がある。埋め込みSQLみたいな違和感はこの為かもしれない。

2004年11月24日(水)

Pascal形式

カテゴリ: C#, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - Pascal形式

先日の命名規則のあれで、.Net的には「UpperCamel」ではなく「Pascal形式」と呼ばれているとばけらさんに指摘された。確かに下記のページにそのように書かれている。

NET Framework 一般情報リファレンス - 名前付けのガイドライン - 大文字の使用スタイル

UpperCamelという呼び方はかなりマイナーなようだ。実は「A.R.N [ Top > 書庫 > C# & ASP.NET コーディング標準 ]」というところを参考にしていて、そういうものを作っちゃうくらいだから用語もあっているのだろうと思ったら、そういうわけでもなかったようだ。それにしてもUpperCamelというのは何処由来の言葉なのだろう。

2004年11月30日(火)

JavaScriptによるDoS攻撃

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - JavaScriptによるDoS攻撃

ITmedia エンタープライズ:全主要ブラウザに影響する脆弱性発見

JavaScriptでArrayクラスのsort()メソッドを使うことにより、ブラウザを異常終了させられるという話。私ははじめ、sort()に渡す比較関数の中で、現在ソート中の配列の長さを変えるようなことを行うと落ちるというような話だと思っていた。組み込みクラスの実装はネイティブコードであることが多い。そこでは言語の保護機能は働かないので、想定外のことがあると実行環境ごとあっさり落ちる。そこで危険なのが、コールバックなど、ネイティブコードの途中で任意のコードが実行される状況である。

実際は、配列の要素が配列という、マトリョーシカのようなひたすら入れ子の配列をソートするというものだった。Windows XP SP2ではスタックオーバーフローというメッセージボックスが出るので、他の環境でもスタックオーバーフローが起きているのだろう。そして再帰的な構造を持つ攻撃コードもそれを狙っているようだ。

だがしかし、どうしてスタックオーバーフローが起るのかが理解できない。ソートといえばクイックソートで、これは再帰的なアルゴリズムだが、通常はループに展開したものを使用する。何よりもまず、普通のソートルーチンは入れ子になった配列の中までソートしようとはしないので、マトリョーシカ攻撃は無意味である。とすると、デフォルトの比較関数のせいだろうか。大小関係を決定する為に、入れ子関係を深く潜行して行くことでスタックオーバーフローすると。

と思ったら、そんな気の利いたものは存在せず、比較関数として何も指定しない場合は、要素を文字列化して比較するらしい。それならソートしなくてもtoString()メソッドを呼んで文字列化するだけで良いのではないか。試してみたらそれでも落ちた。うーん、迂遠な。

2005年01月16日(日)

Unicodeを使わない理由

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - Unicodeを使わない理由

日本人の開発したCGIやフリーソフトは、なぜいつまでたってもEUCやShift-JISに固執し、Unicodeをベースに作成しないのでしょうか。 (以下略)という、ものすごく長い題名のはてなの質問の話。

一番の理由は、Unicodeの文字列をちゃんと扱うのは割と面倒なことだろう。そして、Unicodeを使っただけで国際化できるというわけでもないので、気軽にUnicodeにしておこうという感じにはならないのだ。

面倒くささの源は合成用文字である。ひとつの文字が複数のUnicodeスカラ値で構成されている可能性があり、要するにUnicodeは可変長の文字コードなのだ。さらにややこしいことに、文字によっては合成済みのものがUnicodeスカラ値として存在するので、同じ文字の並びであっても、Unicodeスカラ値の並びとしては異なるものになる可能性がある。そのため、比較の際には合成、合成済みのどちらかに統一することが必要になる。これを正規化という。合成用文字かどうかはコード範囲で決まっているという訳ではなく、ひとつひとつのUnicodeスカラ値に対して合成用文字かどうかのデータベースが必要になるため、言語やライブラリのサポートが無ければ事実上Unicodeは使えないといって良いだろう。

ここまでは開発側の都合だか、実はエンドユーザー側にも、今までとは異なる状況で文字化けが発生するという影響が出る。具体的には円マーク(¥)とチルダ(~)、波ダッシュ(〜)などである。円マークとチルダはコードポイントが同じなら字形が異なっても良いとしてきた今までのツケ、波ダッシュはWindowsが全角チルダとして扱う為であるが、これをエンドユーザーに説明するのはかなり面倒くさい。

結局、国際化する利点のあるCGIプログラムというのはあまり無いし、さまざまな面倒くささを考えると、「今回はご遠慮させていただきます」ということになるのである。

2005年01月22日(土)

アスペクト指向

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - アスペクト指向

なにやらアスペクト指向というのが流行っているようなので、「AspectJによるアスペクト指向プログラミング入門」という本を読んでみた。

アスペクト指向はオブジェクト指向を補佐するもの。アスペクト指向の例として必ず出てくるロギング処理では、記録の仕組みはクラスとして纏められるが、実際に記録を行う処理は、ロギングの対象となるクラス郡の該当メソッドに、人間が頑張って埋め込んで行かなければならない。しかもあちこちに散らばっているので、記録する情報を変えようと思ったら大変なことになる。

平たくいうと、その埋め込み作業を機械にやらせるがアスペクト指向である。埋め込み位置と埋め込むコードを一ヶ所に記述するというのがポイントとなる。そのため、同じなんとか指向であるオブジェクト指向は、ツールなどのサポートが無くとも実戦が可能だが、アスペクト指向はツールのサポートが必須となる。埋め込める位置はツールによって異なるが、AspectJではメソッドの先頭・末尾、メソッド呼び出しの前後、フィールド参照の前後など、割といろいろなところを指定することができる。

アスペクト指向の問題は、実際にどこに何が埋め込まれたかということを把握するのが難しいことだろう。AspectJではいきなりクラスファイルが生成される様になっている。インタプリタ型の言語ではそういうものすら残らない為、変なことになった時の状況把握が大変そうである。AspectJではEclipsと連携して、埋め込み結果を見ることができる機能があるようだ。アスペクト指向を活用するにはIDEも必須かもしれない。

2005年06月11日(土)

xp_cmdshell

カテゴリ: Windows, セキュリティ, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - xp_cmdshell

データベースシステムにはストアドプロシージャというものがある。SQLでは行えない複雑な処理やDBMSに依存するような処理を行う為に、別途定める言語で記述した処理をデータベースに登録し、クライアントから呼び出すことができるという機能だ。多くのプログラミング言語では、言語環境の外にある機能を使えるようにするために拡張ライブラリという仕組みが用意されている。ストアードプロシージャでもそのような仕組みが用意されている場合があり、それを拡張ストアードプロシージャという。

Microsoft SQL Serverでは、恐ろしいことに、任意のコマンドを実行することができるxp_cmdshellという拡張ストアードプロシージャが標準で用意されている。これが何を意味するかというと、SQLインジェクションの脆弱性があると、もれなくOSコマンドインジェクションの脆弱性もあるということだ。ちなみにxpはWindows XPではなくextended procedureの略らしい。

これで色々納得がいった。カカクコムの不正アクセスの話で、攻撃方法はSQLインジェクションらしいという噂が出ていたが、それでどうしてサイトが改竄できるのかがどうにも理解できなかった。任意のコマンドが実行できるならそれも可能だろう。

2005年07月01日(金)

ruby-1.8.2標準ライブラリのXMLRPCに任意のコマンド実行の脆弱性

カテゴリ: Ruby, セキュリティ, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - ruby-1.8.2標準ライブラリのXMLRPCに任意のコマンド実行の脆弱性

オブジェクト指向言語Ruby - XMLRPC.iPIMethodsの脆弱性について

指定したオブジェクトが持つメソッドを自動的に公開するサービスがあって、それがうっかり祖先のメソッドまで公開してしまうようになっていたため、Objectクラスが持つsystem()なども公開され、その結果、任意のコマンドをリモートから実行できてしまうということらしい。

詳細は公開されていないが、原因はパッチを見れば一目瞭然である。オブジェクトが持つメソッドの一覧を取得するのにModule#public_instance_methodsを使用している。このメソッドは祖先が持つメソッドも含めるかどうかのフラグを引数として与えるようになっているが、この引数はデフォルト値を持っているので省略可能である。XMLRPCでは省略して使っていたが、1.8より前のデフォルト引数は偽であり、祖先が持つメソッドは含まれないようになっていたため、問題は無かった。が、1.8以降からデフォルトが真に変わるという恐ろしいことが起こり、脆弱になってしまったというわけだ。

この件はデフォルト値に頼ると痛い目に会うという教訓を見事に体現しているだろう。デフォルト値を暗黙のうちに想定して使っているような場合は、想定している値を明示的に指定するべきである。デフォルト値に頼っていいのは、どれに転んでも別にこだわらないときだけだろう。

こういう問題があるのでデフォルト値というのはそう簡単に変えられるものではないと思うのだが、これはなんで変更されたのだろう。いちおう理由があると思うのだが。

2005年08月31日(水)

(?{})

カテゴリ: Perl, セキュリティ, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - (?{})

Perlの正規表現では、(?{ コード })という記述を用いることで、パターンマッチ中に任意のコードを実行することができる。

$ perl -e '"aaa" =~ /(?{print "OK"})/'
OK

検索CGIなどでは、検索条件として正規表現が使えるものがある。Perlで実装されている場合、まず間違いなく入力された正規表現をそのままm//に渡しているだろう。正規表現中に任意のコードを含ませることができるということは、つまりそれって脆弱性? と思ったらPerl自身がちゃんと考慮していた。さすがはPerl。PHPとは違いますな。

$ perl -e '$re = "(?{print ¥"OK¥"})"; "aaa" =~ $re;'
Eval-group not allowed at runtime, use re 'eval' in regex m/(?{print "OK"})/ at
-e line 1.

エラーメッセージにある通り、パターンマッチ内に直接書かれていないものは許可されないのである。どうしても使用したい場合はプラグマuse re 'eval'を指定する必要がある。実際にはその上でtaintモードを有効にし、さらに汚染除去した文字列を使用する必要がある。

Perlではqr//という記法でもってあらかじめ正規表現オブジェクトを生成する方法もある。ここに例のあれを記述したらどうなるのか。

$ perl -e '$re = qr/(?{print ¥"OK¥"})/; "aaa" =~ $re;'
panic: top_env

panicって一体……

2006年05月21日(日)

特異メソッドとメタクラス

カテゴリ: Ruby, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 特異メソッドとメタクラス

Rubyには特異メソッドというものがある。任意のインスタンスに独自のメソッドを定義できるというものだ。特異メソッドは英語というかソースコード上ではsingleton methodと呼ばれている。一体何がどうシングルトンなのだろうか。答えはその実装にある。

特異メソッドは一見インスタンスに直接メソッドを定義しているように見えるが、実はそうではない。特異メソッドを定義しようとすると、そのインスタンス専用のクラス(特異クラス)がクラス階層に挿入され、そこにメソッドの定義が追加される。特異クラスの存在はRubyレベルでは無視されるようになっているので、あたかもインスタンスに直接メソッドが定義できるように見えるのである。

このとき、特異クラスのインスタンスは、特異メソッドを定義したインスタンスただひとつだけであり、そしてただひとつだけでなければならない。そうしなければメソッドの定義がインスタンス固有にならないからだ。この性質はつまるところシングルトンである。したがって特異メソッドというのは、インスタンスをシングルトン化し、そのクラスにメソッドを定義するということになるという訳だ。

この特異メソッドという機能は、どちらかというとクラスメソッドの定義方法の副産物的なものだと思われる。特異メソッドを使用する場面のは、ほとんどの場合クラスメソッドの定義だけである。

クラスベースのオブジェクト指向言語では、全てのオブジェクトは必ず何かのクラスのインスタンスである必要がある。クラスもオブジェクトという言語の場合、クラス自身も何かのクラスのインスタンスである必要があり、このクラス自身が所属するクラスをメタクラスという。インスタンスの持つメソッドの定義はクラスが持つので、クラスメソッド、つまりクラスオブジェクトが持つメソッドの定義はメタクラスが持つことになる。したがってメタクラスはクラスごとに異なるものが必要になり、そしてクラスオブジェクトはメタクラスの唯一のインスタンスとなるわけである。

つまりクラスメソッドの定義とは、メタクラスという特異クラスにメソッドを定義することにほかならない。そしてメタクラスの存在も、言語的には無視された方が都合が良い。勝手にインスタンスを作られたり、継承されたりするのは好ましくないからだ。ということで、これを実現する為の仕組みが特異メソッドというわけである。

なんか、ここまで理解できないとたかだか1機能の存在も説明できないというのは、さすがは言語オタク用言語Rubyという感じ。

2006年06月26日(月)

緊急モーションセンサーの使い方

カテゴリ: Mac, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 緊急モーションセンサーの使い方

緊急モーションセンサー(SMS:Sudden Motion Sensor)は、Macを叩いてデスクトップを切り替えるなどの誤った使い方をされることが多いが、これは簡単に使えるからなのかもしれない。調べてみたら意外に簡単に使えるようだ。ちなみにsuddenはサドンデスのサドンで、「突然に」「不意に」という意味。

実は使い方というか、利用する為のソースコードが「SMSUTILS - GNUE(鵺)」で公開されている。SMSから情報を取得している部分はsmsutils.cにある。それによると以下の様にIOKitを使ってデバイスにアクセスするだけで使うことができる。

  1. IOServiceGetMatchingServices()で該当するデバイスを探す。機種ごとにデバイスが異なる(SMCMotionSensor / IOI2CMotionSensor / PMUMotionSensor)というのがポイント。
  2. IOServiceOpen()でデバイスと通信する為のポートを開く。
  3. IOConnectMethodStructureIStructureO()でデバイスから値を読み取る。どのデバイスからもx、y、zの3つの値が得られるが、デバイスごとに入出力に使用する構造体の仕様が異なるというのがポイント

こういうデバイスをあれしてそれするプログラムでは、デバイスの名前と仕様がポイントとなる訳だが、そういう情報はやっぱりdarwinのソースコードから得ているのだろうか。マシンに搭載されているデバイスの名前はIORegistryExplorerとかから調べられるので、そこからブラックボックス的にハックという線も無くは無いが、どうにも考えづらい。

それにしてもzの値が謎だ。姿勢を表すだけならxとyだけで十分で、かつ別にZ軸周りの回転が検出できる訳でもZ軸方向の加速度が取れる訳でも無いようなので、おそらくzはジンバルロック状態に対する冗長性確保の為に設けられているのだと思うのだが、どういう値が入るのだろう。

2006年06月27日(火)

もののけpingサーバ計画

カテゴリ: サイトメンテ, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - もののけpingサーバ計画

いい加減、真も日ブもRSSが読めないとか仕様が古くさいシステムという感じが否めないものになってきており、何とかすべき感もいい感じで高まってきている。今時分の更新チェックシステムというと、能動的なRSSアグリゲータか、受動的なpingサーバとなるわけだが、どちらもブログ関連の技術である。私の描いていた次世代真も日ブの姿としては「いっそ更新を通知してもらう方が確実でかつリアルタイム」という感じだったので、後者のpingサーバが近い感じになる。ブログ関連技術で過度の幻想を抱くシステムといえばMovableTypeで、そこではpingという言葉も使われている。とということはMovableTypeを使用してさくっとpingサーバを作れたりとか?

実はpingにはtrackback pingupdate pingの2種類があったりするのだった。trackback pingはごく普通にトラックバックと呼ばれているあれである。話題を逆追跡出来るように、あるエントリーからリンクしている事をリンク先に通知するものだ。update pingの方は、単にあるブログが更新されたことを通知する為のものだ。実はpingサーバが受け取るものは後者のupdate pingなのであり、これを受け取る機能はMovableTypeにはないのだった。

update pingのプロトコルは意外にしょぼくて、基本的には更新されたサイトのURLと更新日しか送られてこない。それにしては世のpingサーバがもうちょっとましな情報を提供しているのはどういうことかと、こういう仕組みになっている。

  1. update pingを受信する
  2. 受信したURLにアクセスし、リソースを取得する
  3. そのリソースにRSSの在りかが記載されていれば、そこから更新情報を取得する
  4. なければ、個々のpingサーバ独自の手法で更新情報をひねり出す

Perlによるpingサーバの実装は「ここギコ!: Update Pingを受け付けるサーバの実装に最低限のコードがあるので実装するのは簡単なのだが、spam除けとかを考慮した、ちゃんとしたpingサーバとして使用できるようにするのは面倒だろう。というかどう考えても主要部分はMovableTypeに頼れないので、MovableTypeを利用しようという意味は無いのだった。

2006年07月17日(月)

Subversionブロック図

カテゴリ: Subversion, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - Subversionブロック図

やっぱりMacOS X用のSubversionのGUIクライアントアプリケーションが欲しいということで、もう少し頑張ってみようと思う。ついでにその経緯をPagesを使って紙面にまとめる事で、せっかく買ったPagesを活用してみようという計画。ちなみに今までクライアントアプリケーションを作ろうと奮闘した結果は以下の通り。

  • Subversionの機能はライブラリ化されている
  • 標準のCLIクライアントもそのライブラリを使用して作られたひとつの実装例
  • ライブラリのドキュメントはソースコードからdoxygenで生成されたリファレンスマニュアルだけ
  • クライアントアプリケーションの開発をサポートしてくれるようなドキュメントは無い

この結果からすると、要するに標準のCLIクライアントのソースコードを見て参考にしろという事なのだと思う。で、詳細はdoxygenで生成されたリファレンスマニュアルか、ソースコードを見ろということなのだろう。とはいえ、クライアントアプリケーションを作るにあたって、どのライブラリを理解していかなければならないのかということが分からないと、いささかやりづらい。ということで、まずは各ライブラリがどのレイヤーに位置して何と関連しているかという、ありがちなブロック図をひねり出してみた。

[Subversionブロック図]

この図の元ネタはいくつかあるが、どれも微妙だったので理解の為に自分で描き直した。こうしてみると、Subversionは3層+1で構成されている事が分かる。クライアントは異本的にlibsvn_clientだけを相手にしていれば、基本的にはそれより下の層については何も考えなくて良い。事実、標準のCLIクライアントは、ほぼlibsvn_clientのラッパーという感じになっているようだ。が、逆に言えば、標準のCLIクライアントが持っている機能以上のものを実装しようとする場合は、libsvn_wcとlibsvn_raのお世話になるしかないということにもなる。

クライアントアプリを作るならとりあえずlibsvn_clientを理解すべしということが分かったのは心強いが、謎はまだまだある。たとえばコミットするにはどのファイルをコミットするのかというのを指定しなければならないが、その指定方法はlibsvn_clientのソースコードを見ただけでは分からない。また、リポジトリの操作には認証が必要になる場合が多いが、どうやって認証情報を渡しているのかは、Subversionでもかなりの謎の部類に入りそうだ。

ちなみに図では最下層となるリポジトリインターフェースの下には、本当はファイルシステムインターフェースが存在する。libsvn_fsがそれにあたる。この両者の違いはリポジトリを操作した際の各種フック処理が起動されるか否かである。ファイルシステムインターフェースを直接使用することはほぼ無いと思われるので、図ではあえて省略している。

2006年08月06日(日)

svnコマンドmain()関数

カテゴリ: Subversion, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - svnコマンドmain()関数

クライアントアプリを作る為にSubversionのCLIクライアントを解析してみよう第1回。まずはmain()関数を調べて、クライアントアプリ実行に先だって必要な処理やコンテキストの生成について調べてみたい。コンテキスト的なものがあるはずという予想は、認証に関しての処理がサブコマンドの処理内に存在しない事による。認証が必要かどうかはサーバ等にアクセスしてみない限り分からないが、libsvn_clientが独自に処理してしまうと、任意の認証インターフェースを実装する事が困難になる。それができるとドキュメントで明言されている以上、認証処理用のコールバックの情報を保持したコンテキスト状のものをlibsvn_clientに渡しているはずに違いないという予測が立てられる。

main()関数内の処理の大半はコマンドライン引数に関する処理で分かりづらいが、やっている事を大ざっぱにまとめるとこんな感じになるようだ。

  1. トップレベルのAPRメモリプール生成
  2. svnライブラリ系の初期化
  3. コマンドライン引数の解析および関連処理
  4. svn client_content生成
  5. svn client_contentのauth_baton設定
  6. サブコマンド処理呼び出し
  7. APRメモリプール破棄

これだけ見ると簡単な感じがするのだが、ここから先が大変なのだ。なにしろAPRもSubversionも基本的にはAPIリファレンスしかない。client_contentに何が保持できるか、auth_batonに何を設定しなければならないのかは個々のAPIの説明を見ても分からない。前途多難な予感。

2006年09月01日(金)

超高難度のエラーメッセージ

カテゴリ: Windows, トラブルシューティング, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 超高難度のエラーメッセージ

ExcelにはHLookupという関数がある。これは1行目に列名が入っている表に対して列名と行番号を使用して表引きを行うものである。引数として列名、表のセル範囲、行番号を渡すと、該当するセルの値が取得できる。ワークシート上で使える関数はVBAからも使用できるようになっていて、HLookup関数は下記のように使用する。

value = WorksheetFunction.HLookup("Foo", Range("A1:D4"), 2)

の、はずなのだが、何度やっても下記のランタイムエラーが発生してしまう。

実行時エラー '1004': WorksheetFunctionクラスの HLookup プロパティを取得できません。

はじめは何かを書き間違っていたのだと思っていたが、ヘルプ等から何度コピペしてもエラーになってしまう。よくよく調べてみると、何度かの呼び出しは成功していて、途中でこのエラーになってしまうことが分かった。書き間違いではないことは分かったが、余計に謎な状態に。もしかしてExcelのバグ?

実は単に該当する列名が見つからなかった場合にこのエラーが発生するのだった。これなら状況によって発生したりしなかったりすることに納得できる。きっとワークシート関数内でのエラーは全部これになってしまうのだろう。とりあえずこのエラーが出た場合は「いやそもそもプロパティじゃないし」と心の中でツッコミを入れるようにしている。

2006年09月19日(火)

ディスクとネットワークの利用状況の取得方法

カテゴリ: Mac, MacFace, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - ディスクとネットワークの利用状況の取得方法

密かに総合システム状態表示ツールを目指しているMacFaceに、ディスクアクセスのグラフとネットワークアクセスのグラフを表示する機能を付けたいとは思っていたが、肝心の取得方法が全く分からなかった。デバイス関連といえばI/O Kitかioctl()なのだが、何も取っ掛かりが無い状態でドキュメントを探すのは難しい。

先日買ったMAC OS X InternalsのIOKitの部分を読んでいたら、EthernetのMACアドレスを取得する例があるのを見つけた。それによると、IOKitフレームワークのEthernet用のドライバのヘッダファイルに、MACアドレスを取得する為のI/O Registryのキーが定義されているようだ。ということは、他のヘッダファイルを見れば、ディスクやネットワークの利用状況を取得するための何かが定義されているかもしれない。

まずディスクアクセスの方は、storage/IOBlockStorageDriver.hにkIOBlockStorageDriverStatisticsKeyというそれっぽい定数が定義されていた。このキーを使用して取得できるものはディクショナリになっていて、下記のキーが含まれているらしい。なかなか良さそうだ。

  • kIOBlockStorageDriverStatisticsKey
  • kIOBlockStorageDriverStatisticsBytesReadKey
  • kIOBlockStorageDriverStatisticsBytesWrittenKey
  • kIOBlockStorageDriverStatisticsReadErrorsKey
  • kIOBlockStorageDriverStatisticsWriteErrorsKey
  • kIOBlockStorageDriverStatisticsLatentWriteTimeKey
  • kIOBlockStorageDriverStatisticsReadsKey
  • kIOBlockStorageDriverStatisticsWritesKey
  • kIOBlockStorageDriverStatisticsReadRetriesKey
  • kIOBlockStorageDriverStatisticsWriteRetriesKey
  • kIOBlockStorageDriverStatisticsTotalReadTimeKey
  • kIOBlockStorageDriverStatisticsTotalWriteTimeKey

ネットワークアクセスの方はnetwork/IONetworkStats.hというそれっぽい名前のヘッダファイルがあり、その中にそれっぽい構造体の定義があった。

typedef struct {
        UInt32  inputPackets;
        UInt32  inputErrors;
        UInt32  outputPackets;
        UInt32  outputErrors;
        UInt32  collisions;
} IONetworkStats;

こちらは構造体なのでディスクの方とは取得の方法が異なるようだ。kIONetworkStatsKeyというキーを使用して何らかの方法でIONetworkData型のデータを取得すると、そのデータの中に上記の構造体のデータが含まれているという仕組みらしい。

取得する為の何かが分かったので、これで具体的な取得方法を見つける事ができる——かもしれない。

2006年09月24日(日)

ディスクドライブの統計情報の取得方法

カテゴリ: Mac, MacFace, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - ディスクドライブの統計情報の取得方法

MacOS Xでのディスクドライブの統計情報は、I/O Registryに登録されているIOBlockStorageDriverClassに属するサービスオブジェクトのプロパティから取得する事ができる。取得する為の処理は以下のコードのようになる。なお、CFDictionaryRefをNSDictionaryにキャストすれば幾分扱いが楽になるが、Cocoaは使わないという方針から、あえてそのまま扱っている。

#include <stdio.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <CoreFoundation/CoreFoundation.h>

int main(int argc, const char *argv[])
{
    kern_return_t kr;
    CFMutableDictionaryRef matchDict;
    io_iterator_t iterator;
    io_object_t service;
    CFDictionaryRef statistics;
    CFNumberRef statNumber;
    SInt64 value;

    // I/O RegistryからBlockStrageDriverにマッチするサービスオブジェクトを探す
    matchDict = IOServiceMatching(kIOBlockStorageDriverClass);
    kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchDict, &iterator);

    // 全てのサービスオブジェクトを処理する
    while (service = IOIteratorNext(iterator)) {
        // エントリのパスを表示する
        io_string_t path;
        IORegistryEntryGetPath(service, kIOServicePlane, path);
        puts("----");
        printf("Location: %s\n", path);

        // エントリから統計情報の入ったディクショナリを取得する
        statistics = IORegistryEntryCreateCFProperty(service, CFSTR(kIOBlockStorageDriverStatisticsKey), kCFAllocatorDefault, kNilOptions);

        // ディクショナリから値を取り出す
        if (statistics) {
            statNumber = CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey));
            if (statNumber) {
                CFNumberGetValue(statNumber, kCFNumberSInt64Type, &value);
                printf("Bytes Read: %lld\n", value);
            }

            statNumber = CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey));
            if (statNumber) {
                CFNumberGetValue(statNumber, kCFNumberSInt64Type, &value);
                printf("Bytes Write: %lld\n", value);
            }

            CFRelease(statistics);
        }
        
        IOObjectRelease(service);
    }

    IOObjectRelease(iterator);

    return 0;
}

IOServiceMatching()ではIOBlockStorageDriverClassを継承しているクラスもマッチするマッチングディクショナリが作られる。そのためIOCDBlockStorageDriverやIODVDBlockStorageDriverのサービスオブジェクトもマッチする。それが嫌な場合はIOServiceNameMatching()を使用してIOBlockStorageDriverClassのみがマッチするものを作成する必要がある。ちなみにIOServiceGetMatchingServices()がマッッチングディクショナリを開放するので自分で開放する必要は無い。

物理的なディスクドライブごとにIOBlockStorageDriverClassのサービスオブジェクトが存在するので、ループで処理する必要がある。オブジェクトの開放は、I/O KitのAPIで取得したものではIOObjectRelease()を、Core FundationのAPIで取得したものはCFRelease()を使う必要がある。

ネットワークの統計情報の取得方法

カテゴリ: Mac, MacFace, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - ネットワークの統計情報の取得方法

MacOS Xでのネットワークの統計情報は、I/O Registryに登録されているIONetworkInterfaceClassに属するサービスオブジェクトから取得できるのだが、ディスクドライブの場合とは異なり、プロパティから取得する事はできない。ネットワークデバイス専用の方法でデバイスとの接続を作り、それを使って統計情報のデータを読み出すということを行う。

#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IONetworkStats.h>
#include <IOKit/network/IONetworkLib.h>

int main(int argc, const char *argv[])
{
    kern_return_t kr;
    CFMutableDictionaryRef matchDict;
    io_iterator_t iterator;
    io_registry_entry_t entry;
    IOReturn ir;
    io_connect_t con;
    IONDHandle dataHandle;
    IONetworkStats stats;
    UInt32 size;

   // I/O RegistryからIONetworkInterfaceClassにマッチするサービスオブジェクトを探す
    matchDict = IOServiceMatching(kIONetworkInterfaceClass);
    kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchDict, &iterator);

    // 全てのサービスオブジェクトを処理する
    while (entry = IOIteratorNext(iterator)) {
        // エントリのパスを表示する
        io_string_t path;
        IORegistryEntryGetPath(entry, kIOServicePlane, path);
        puts("----");
        printf("Location: %s\n", path);

        // ネットワークデバイスへの接続を開く
        ir = IONetworkOpen(entry, &con);
        IOObjectRelease(entry);

        // ネットワークデバイスからkIONetworkStatsKeyに対応するデータのデータハンドルを取得する
        ir = IONetworkGetDataHandle(con, kIONetworkStatsKey, &dataHandle);
        // データハンドルからデータを取り出す
        size = sizeof(stats);
        ir = IONetworkReadData(con, dataHandle, (UInt8*)&stats, &size);

        printf("inputPackets: %-ld\n", stats.inputPackets);
        printf("inputErrors: %-ld\n", stats.inputErrors);
        printf("outputPackets: %-ld\n", stats.outputPackets);
        printf("outputErrors: %-ld\n", stats.outputErrors);
        printf("collisions: %-ld\n", stats.collisions);

        IOObjectRelease(dataHandle);
        ir = IONetworkClose(con);
    }

    IOObjectRelease(iterator);

    return 0;
}

2007年07月21日(土)

ターゲットOSの違うユニバーサルバイナリの作り方

カテゴリ: Cocoa, MacFace, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - ターゲットOSの違うユニバーサルバイナリの作り方

Xcodeを使って普通にユニバーサルバイナリを作ると、PPC版もintel版もMacOS X 10.4をターゲットとしてビルドされる。とはいえ諸事情によりPPC版だけMacOS X 10.2でも使えるようにしたいということもある。こういった場合、別にビルドしたバイナリをlipoコマンドでくっつけるといったトリッキーなことをやるしかないと思っていたのだが、実はXcode自体がそういうことに対応していたということに今更気付いたのだった。

ターゲットOSの異なるバイナリをビルドする場合は、ビルド設定のMACOSX_DEPLOYMENT_TARGETとGCC_VERSIONなど、ターゲットごとに個別の値を設定する必要のある項目というのがある。Xcodeには、これらの項目についてアーキテクチャごとにバリアントが定義できる機能が備わっている。たとえば以下のように設定すれば、PPC版がv10.2以降、intel版がv10.4以降で動作するバイナリがビルドできる。

ARCHS = ppc i386
GCC_VERSION_i386 = 4.0
GCC_VERSION_ppc = 3.3
MACOSX_DEPLOYMENT_TARGET_i386 = 10.4
MACOSX_DEPLOYMENT_TARGET_ppc = 10.2

気をつけなければいけないのは、プロジェクト自体のターゲットを「Mac OS X 10.4 (Universal)」にしないとユニバーサルバイナリとしてビルドされないことと、アーキテクチャ名付きのバリアントよりも無しのバリアントの方が優先されるのでアーキテクチャ名無しの設定項目を削除しておく必要があることである。あと、楽をしようとしてDefaultのビルド設定にアーキテクチャ付きのバリアントを設定しても無駄なので注意すべし。

2007年09月02日(日)

Rubyの不思議挙動

カテゴリ: Ruby, サイトメンテ, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - Rubyの不思議挙動

さくらインターネットのサーバメンテナンスの際にRubyのバージョンが変わったようで(アナウンスには無い)、もののけアンテナが下記のエラーを吐いて動かなくなっていた。

/usr/local/bin/ruby: invalid option - (-h will show valid options) 

antenna.rbではshebangで#!/usr/local/bin/ruby -Ks -I/home/rryu/lib/rubyのようにオプションを指定していたのだが、どうやら複数のオプションを指定できなくなった為のようだ。この変なエラーメッセージからするとRubyのバグっぽい感じがしないでもないが。

いろいろ悩んだ末、#!/usr/local/bin/ruby -KsI/home/rryu/lib/rubyのようにして解決。

と思ったら、起動すると [BUG] Bus Errorで死ぬようになった。/usr/local/bin/ruby antenna1.rb としてスクリプトを直接Rubyに渡して起動すると問題無く動く。インクルードディレクトリの指定も生きているので、shebangのオプション指定が無視された結果、うまく動いているように見えるという訳では無いようだ。謎。

# ./antenna.rb mononoke-diary.conf 
/home/rryu/lib/ruby/configfile.rb:80: warning: parenthesize argument(s) for future version 
Warning:./antenna.rb:59: parsearg is deprecated after Ruby 1.8.1; use optparse instead 
get http://homepage3.nifty.com/h_sasaki/F/diary/diary.html 
/usr/local/lib/ruby/1.8/net/http.rb:560: [BUG] Bus Error 
ruby 1.8.5 (2006-08-25) [i386-freebsd6] 

Abort

2007年09月18日(火)

二種類のキャスト

カテゴリ: C#, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 二種類のキャスト

C#のプログラムをコンパイルしたら、こんなエラーが出た。partDefはHashtableで、partDef[KeyPartPosX]の中味はlong型、そしてその値は0である。

x = (int)(partDef[KeyPartPosX]);

指定されたキャストは有効ではありません。

せっかく表示されているトラブルシューティングのヒントから原因を考えてみよう。

数字からキャストするとき、値は無限大より小さい数でなければなりません。
値は0なので、この条件には引っかからないはずだ。そもそも文字列からの変換では無い(数字からというのはそう言う意味だと思う)ので、この項目は関係なさそうだ。
元の型が目的の型と互換性があることを確認します。
long型はint型と互換性があるはず。

----やっぱり解らない。こういう場合は同じようなコードを実際に実行してみたりすると解決の糸口が掴める場合が多い。ということで、例外が出てデバッガで停止している状態で、イミディエイトウインドウで下記のコードを実行してみた。すると、かなり具体的なエラーメッセージが表示された。

? (int)partDef[KeyPartPosX]
'partDef[KeyPartPosX]' を 'int' としてアンボックスすることはできません。

つまり、「(int)」はObjectからのアンボクシングのキャストになるので、実体がlongなObjectをintにアンボックスできないというエラーだったというわけだ。Visual Studioが出すトラブルシューティングのヒントに惑わされていた様な気がする。というかイミディエイトウインドウで出せるのなら、普通にそのメッセージを出して欲しい。

ということで、まずlongにアンボックスしてからintに変換しなくてはならないので、こう書く必要がある。

x = (int)((long)partDef[KeyPartPosX]);

なんかキャストで誤魔化しまくっているコードみたいでヤだなあ。

2007年09月19日(水)

sysctlとvmstatとvm_stat

カテゴリ: Cocoa, MacFace, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - sysctlとvmstatとvm_stat

Machカーネルは、仮想記憶を前提としつつも物理メモリの管理機能しか持っていない。あるページを外部ストレージにページアウトするには退避先のストレージにアクセスできる必要があり、そのレベルの処理はもはやカーネルだけではできないためである。そのため、MacOS Xでは、BSDレイヤに仮想記憶のページャが実装されている。

ということはつまり、仮想記憶関係の情報はBSDレイヤから得られる公算が高い。欲しい情報は、とりあえず物理メモリが不足していそうだと判断できる基準である。「4.4BSDの設計と実装」のメモリ管理関連のページを見ていると、「P.204 5.12.1 ページングパラメータ」にそれっぽい感じの記述が見つかった。

ページアウトデーモンは、空きメモリページ数を、いくつかのパラメータと比較することで、必要とされるメモリ量を判断する。第1のパラメータ、free_targetは、ページアウトデーモンを停止する基準値(ページ数)である。利用できるメモリ量がこの基準値を超えると、ページアウトデーモンによってページアウトされなくなる。free_targetは、通常、ユーザーメモリの7パーセントに設定される。又、耐えられると考えうる、空きメモリ量の最低限度を指定するfree_minという興味深い制限値がある。この値は通常ユーザーメモリの5パーセントに設定されている。空きメモリの量がfree_minよりも少なくなると、ページアウトデーモンが起動される。

つまり、物理メモリ消費量がfree_min以上でかつページアウトが発生している場合は、確実に物理メモリが足りない状態であるといえるだろう。また、一度free_minを越えた後にfree_min付近を彷徨っている場合も不足状態だと言えるだろう。free_minとして5%をハードコードしても良いが、それは取得できる手段がなかった際の最終手段として、まずは取得手段を探してみよう。

Spotlightで検索したところ、free_minはsys/vmmeter.hで定義されているstruct vmmeterのメンバのv_free_minがそれに該当するようだ。#ifdef __APPLE_API_OBSOLETEで括られている点が引っかかるが、とりあえず気にしないことにする。ではstruct vmmeterはどうやって得るのかをググったところ、sysctlシステムコールで取得できるようだ。sysctlシステムコールには、そのラッパーなsysctlコマンドがあり、簡単に値を取得する事が出来るようになっている。

sysctlコマンドにvm.vmmeterを指定した際に得られるものはstruct vmmeterではなく実際はstruct vmtotalの様なのだが、とりあえず試してみるとこのような結果が表示された。

$ sysctl vm.vmmeter
Use vmstat or systat to view vm.vmmeter information

つまりvmstatコマンドで取得しろと。同じような物を2つ作りたくないという気持ちは分からないでもない。でも、MacOS X上にはvmstatコマンドもsystatコマンドも見つからないのだ。

その後に調べたところ、MacOS Xではvmstat相当のコマンドはvm_statとして存在していることが判明した。なぜこんな微妙な違いをつけるのだろう。しかも結局vm_statコマンドでも欲しかった情報が得られなくて残念な思いをするのだった。

$ vm_stat
Mach Virtual Memory Statistics: (page size of 4096 bytes)
Pages free:                    10359.
Pages active:                 285436.
Pages inactive:               144691.
Pages wired down:              83802.
"Translation faults":       16851829.
Pages copy-on-write:          110949.
Pages zero filled:          12805714.
Pages reactivated:            483794.
Pageins:                      299740.
Pageouts:                      28632.
Object cache: 19449 hits of 39140 lookups (49% hit rate)

2007年09月23日(日)

__APPLE_API_OBSOLETEの謎(解決編)

カテゴリ: Cocoa, MacFace, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - __APPLE_API_OBSOLETEの謎(解決編)

vm_statコマンドでは目的の情報が取得できない様なので、試しにsysctlシステムコールを使ってstruct vmtotalを取得してみた。値の出力は面倒なので、デバッガで変数の中味を覗くことにする。

#include <sys/sysctl.h>
#include <sys/vmmeter.h>

int main(int argc, const char *argv[])
{
	int mib[2];
	size_t len;
	struct vmtotal vmt;

	mib[0] = CTL_VM;
	mib[1] = VM_METER;
 
	len = sizeof(vmt);
	sysctl(mib, 2, &vmt, &len, NULL, 0);

    return 0;
}

なんと構造体のメンバの半分以上が0である。つまりこのシステムコールでは有効な値は取れないということなのだろうか。__APPLE_API_OBSOLETEで括られていたのは、このことを意味していたのかもしれない。つまり、MacOS XではページャはBSDレイヤにあるが、仮想記憶関連の情報はすべてMachカーネルが管理しているということなのだろう。

sysctl vm.swapusage

カテゴリ: Cocoa, MacFace, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - sysctl vm.swapusage

いまのところ、sysctl系で使えそうなのはvm.swapusageくらいだろうか。スワップファイルの合計サイズと、その内の使用中のバイト数が得られる。ページアウトが1回も発生していない場合は使用中のバイト数が0になっているので、使用中のサイズはページアウトされたページのサイズということで間違いないようなのだが、ページアウトの回数とページサイズを掛けたものよりもサイズが大きいのはなぜなのだろう。管理情報のオーバーヘッドがあるとか、パディングがあるとかなのだろうか。

$ sysctl vm.swapusage
vm.swapusage: total = 128.00M  used = 54.38M  free = 73.62M  
$ vm_stat
Mach Virtual Memory Statistics: (page size of 4096 bytes)
Pages free:                     7976.
Pages active:                 259543.
Pages inactive:               190597.
Pages wired down:              66172.
"Translation faults":       21048898.
Pages copy-on-write:          251369.
Pages zero filled:          12855048.
Pages reactivated:            350326.
Pageins:                      135369.
Pageouts:                       7934.
Object cache: 45779 hits of 72957 lookups (62% hit rate)

2007年10月08日(月)

MySQLが対応しているのは3バイトまでのUTF-8

カテゴリ: トラブルシューティング, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - MySQLが対応しているのは3バイトまでのUTF-8

WIndows Vistaの登場によって、Unicodeの基本多言語面以外の文字が使われた場合の対応が注目されている。UTF-16はサロゲートペアと合成文字の存在を考慮しなければ1文字16ビットの固定長文字コードとして扱えるので、それらは無視されがちな存在である。たとえばVista以前のWindowsはサロゲートペアに対応していない。UTF-8は最初から可変長エンコーディングなので、そういった問題とは無関係だと感じるが、実はそうでは無かった。

MySQL 5.1 リファレンスマニュアル :: 9 キャラクタセットサポート :: 9.7 Unicodeのサポート より。

現在、MySQLではUTF-8に対して4バイトシーケンスのサポートは含みません。

UTF-8は最大4バイトになるが、基本多言語面の文字のみを表す場合は必ず3バイト以内に収まる。基本多言語面以外の文字がまだ定義されていなかった頃は、今ほど贅沢にはリソースを使えなかった。そのため絶対に使われないものに対応して空間効率を悪くすることはできなかったのだろう。

この4バイトUTF-8問題は実はMySQLだけでは無い。PostgreSQL 8.1のリリースノートAdd support for four-byte UTF8 characters (John Hansen)という記述がある。つまり以前のPostgreSQLでは4バイトのUTF-8をサポートしていなかった訳だ。

2007年10月22日(月)

MacFace現状分析

カテゴリ: Cocoa, MacFace, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - MacFace現状分析

MacFaceを改良したいと思っているのだが、いつもどこから直すべきなのか考えるだけで時間が無くなってしまうので、とりあえず現状の構造を図にしてみた。

MacFaceモデル図その1

もともと習作だったつもりのものだっただけに、その構造はあまりよろしくない。初めはコントローラはAppControllerしか無かったのでまあ良かったのだが、今となってはそれが足かせになってしまっている。まずはビューに対するコントローラをAppControllerから分離して行く所からだろうか。DockIconに対するコントローラもパターン表示コントローラとして抽象化しておけば、後の展開が楽になると思われる。

ちなみにCustomDrawViewというのはdrawRect:の呼び出しをdelegate先に委譲するという、描画の為だけにNSViewを継承したクラスを作るのが面倒くさいという理由から生み出されたダメなクラスである。今は反省している。さらにPrefsWindowとPatternWindowの関係もかなり微妙で、環境設定ウインドウの「パターン一覧」ボタンのアクション先がPatternWindowのmakeKeyAndOrderFront:になっているという、コントローラ無視の関係になっている。

2007年11月02日(金)

Generatorを作らなくてもQuickLookできる

カテゴリ: Mac, MacFace, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - Generatorを作らなくてもQuickLookできる

bundle形式のドキュメントファイルの場合、そのファイルパッケージ内にサムネイルファイルを含ませておけば、何もせずともQuickLook出来るようになる。具体的にはQuickLookというフォルダを作って、その中にThumbnail.*(*は拡張子)という名前の画像ファイルを置けば良い。

MacFaceでQuickLookできるようにしようかと思ったのだが、実は何もしなくてもいいのかもしれない。

2007年11月03日(土)

QuickLook

カテゴリ: Cocoa, Mac, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - QuickLook

Leopardの機能であるQuickLookは、実はdocument readerと呼ばれているコンポーネントがサポートしている決まった形式のデータしかプレビューすることが出来ない。データ形式ごとのプレビュー機能はプラグイン形式になっており、QuickLookUI.frameworkに含まれている。

  • Web.qldisplay
  • Generic.qldisplay
  • Image.qldisplay
  • Movie.qldisplay
  • Music.qldisplay
  • NSImage.qldisplay
  • PDF.qldisplay
  • QC.qldisplay
  • Text.qldisplay

display bundleと呼ばれているこれらはユーザーが追加することは出来ない。その代わりにこれらのデータ形式のいずれかにデータを変換する仕組みを追加することが出来る。それがgenerator bundleである。generator bundleは要求を受け取ったら、対象のファイルを読み、上記いずれかのデータ形式に変換したものを返すという事を行う。変換結果は大抵の場合は画像にするのが適切だが、動画にしてしまうことも可能である。

また、QuickLookはサムネイルとプレビューという2種類の形式を扱うようになっている。両者の主な違いはサムネイルは形式が画像のみであること、そして大きさがかなり小さいという事である。したがってサムネイルは主にアイコン表示の様な場合に使われ、実際Finderのアイコン表示での画像ファイルのサムネイルはQuickLookによって行われている。アイコン表示ではドロップシャドウが付くようになっているが、QuickLookのサムネイル取得APIではアイコン用に枠を付けたものを要求するというオプションが指定できるようになっている。

2008年03月26日(水)

ActiveRecorで日付データを扱う際の罠

カテゴリ: Ruby, Ruby on Rails, トラブルシューティング, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - ActiveRecorで日付データを扱う際の罠

ウェブアプリケーションと日付型のデータは相性が良いとは言えない。オブジェクトに変換すると取り扱いが楽になるが、そのオブジェクトの表現力に制限される。time_t型の限界に阻まれたり、何より日付として不正な入力が保持できない。文字列のままであれば表現できないものは無くなるが、比較などの処理が難しくなる。

ActiveRecordはオブジェクトに変換して扱う派である。さすがのActiveRecordもオブジェクトに変換した際のデメリットを回避できなかったようで、明文化されていない以下の問題を抱えている。

  1. 日付として不正な値はnilになってしまうため、未入力と区別がつかない(任意項目の場合に入力チェックをすり抜けてしまう)
  2. DATETIME型のカラムをTimeクラスにマッピングするため(MySQLの場合)、DB側では扱えるのにも関わらずActiveRecord側でtime_t型的な限界に阻まれ扱えない日付がある
  3. オブジェクト化にTime#local()やTime#utc()を使用しているため、それらのメソッドが持つ5月31日を6月1日に補正する謎の機能の影響を受けている

一つ目の問題は、validates_presence_ofを使わずに自力でチェックすれば対応可能ではある。ActiveRecordは内部的には元の値を保持していて、値が取得されるときに型変換するようになっている。型変換前の値は属性名_before_type_castというアクセッサで取得することができる。したがって型変換後の値がnilで変換前の値がnilでなかったら日付として正しくないものが入力されたということになる。が、この方法が使えるのは、ひとつのテキストとして入力したときだけである。年月日の各要素を個々に入力する形式だといきなりオブジェクトになってしまうのでbefore_type_castの値もオブジェクトになってしまうのである。

二つ目の問題はActiveRecord::ConnectionAdapters::MySQLColumnクラスのklassメソッドを書き換えてマッピングをTimeクラスからDateTimeクラスに変更すればよさそうだが、そんなことをしても大丈夫なのかは分からない。

三つ目はTimeクラス側がなんとかならないとどうにもならないが、一つ目の方法を応用して検出することはできる。同じように限界があるが。

といったようにActiveRecordでも日付の扱いは面倒なのであった。

2008年05月12日(月)

SQL ServerのExecute文

カテゴリ: セキュリティ, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - SQL ServerのExecute文

SQL Serverにはxp_cmdshellというよく悪用される拡張ストアドプロシージャがあるのはその筋では有名な話だが、実はもうひとつExecute文というものがある。これはスクリプト言語でおなじみのevalと同等の機能を持っている。つまり任意の文字列をTransact-SQLとして実行できるのである。

何の目的で用意されたものなのかわからないが、通常のクエリーやストアドプロシージャではExecute文の活躍の場は無いように思える。AIのように自己進化するプログラムならば話は別だが、一般的なアプリケーションでは仕様として盛り込まれていない処理を行う必要は無いので、造り付けの処理をを条件分岐などで切り替えるだけで済む。

そんな使い道のなさそうなEexecute文が最近話題になっているSQLインジェクションによるウェブサイト改竄で有効に使われているのが皮肉なところである。

全テーブルの全カラムの値を書き換えるには、スキーマの情報を取得し、それを使用してUPDATE文を組み立て、実行するという3つのステップが必要になる。通常この攻撃を行うためにはスキーマの情報を攻撃対象から抜き出して、手元でUPDATE文を組み立て、さらにUPDATE文をインジェクションしなければならない。これはかなり手間のかかる作業になるので、よほどの理由が無い限り行使されないものと言えるだろう。だがExecute文が使えれば、すべて攻撃対象のサーバ内で完結させることができる。固定のSQLをインジェクションして、あとは待つだけで良いのだ。まったく簡単である。

Execute文は文であるので、それ自体の使用を禁止することはできないようだ。Transactg-SQLはそれ自体がストアドプロシージャを記述するための手続き型言語も兼ねているので、SQL ServerでのSQLインジェクションは、単なるSQLインジェクションとは別次元のものになっているような気がする。

2008年05月20日(火)

プログラミング言語Erlang

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - プログラミング言語Erlang

並列処理と分散処理を記述するのに優れているというプログラミング言語Eralan。マルチプロセッサ化が進む今の世ではもはや必須のもになりつつあるが、最終的にはアプリケーションレイヤで色々と仕組みを作らなければならないため、対応はなかなか難しい。そんな問題を言語レベルでどうサポートしているのだろうか。とりあえずプログラミングErlangを読んでみた。

まず、Eralangで並列処理が簡単に書ける理由は、プロセスという機能によるところが大きい。Eralangのプロセスはスレッドのようなものだが、環境が他のプロセスと完全に分離されている点が違う。データのやり取りはメッセージの送受信で行い、プロセス間で共有するものが無いため、排他制御は必要ない。さらにEralangのデータ型はすべて変更不能なので、これに関する排他制御も必要ない。したがって簡単というわけだ。別マシンにいるプロセスともメッセージの送受信ができるので、そのまま分散処理も実装できるというわけだ。

しかし、いくらEralangの世界の中では排他制御は必要ないといっても、ファイルやデバイスなど、Eralangの外のものに対する操作には、どうしても排他制御が必要になる。それらのものは状態を持つため、操作する順序が重要であり、順序を保障する仕組みが無いと、結果がどうなるか保障できないのである。しかし、Eralangには排他制御の仕組みは存在しない。

排他制御というのは、同時に複数個所で実行できなくするための仕組みである。それならば、そもそも実行箇所を1箇所にしておけば良いというのがEralangの流儀らしい。排他制御が必要なデバイスごとに○○サーバというプロセスを作って、そのプロセスにメッセージを送ることで、デバイスを操作するわけである。メッセージは待ち行列に入れられて逐次処理されていくので、順序も保障されるし、同時に実行されてしまう心配も無いというわけだ。

言語がサポートしてくれるのはここまでで、並列処理のアルゴリズムは自分でがんばらなければならない。とはいえ分散処理前提のアプリケーションを実装するには良いプラットホームと言えるだろう。

分散処理とObjective-C

カテゴリ: Cocoa, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 分散処理とObjective-C

プログラミングErlangを読んで思ったのだが、Objective-CというかFoundation Frameworkに用意されている変更不能なデータ型のセット、RunLoopによるメッセージキュー、同RunLoopを用いた分散オブジェクトの機能を用いれば、同じようなことができるのではないだろうか。スレッド間通信でもRunLoopを使うのだが、当時はなんでこんな面倒なことになっているのか理解できなかった。これはきっと他の言語とは違う思想で作られたものだったのだろう。

それはそれとして、Objective-Cで、変更不能なデータ型のセットは活用できるのだろうか。変数への再代入はがんばって禁止するとしても、ループはすべて末尾再起の関数呼び出しという形にしなければならない。繰り返し1回分の処理結果を次の繰り返しに引き継がなければならないが、変数への再代入とデータの更新を禁止すると、あとはもう関数への引数という道しかなくなるのである。Objective-CのベースはC言語なので、末尾再起は行われない気がする。とすると変更不能なデータ型のセットは、分散処理用に設けられたものではないということなのだろうか。

2008年06月01日(日)

ロジッククラスという存在

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - ロジッククラスという存在

MVC で「テーブル:モデル=1:1」が許されるのは小学生まで - Devel::Baysideより

「テーブル:モデル=1:1」が許されるのは、Rails や Catalyst で本当に小さいアプリケーションを作るときまでで、多少大きいアプリケーションを作ろうと思ったら、MyApp::Model::XXXX を自動生成するなら、MyApp::Logic::XXXX がロジックを担当し、C からはこちらを呼ぶべきです。社内ではこのアーキテクチャパターンを勝手に「MLVCモデル」と読んでいます。C が膨れてきたなと思ったら、早いうちに「MLVCモデル」に移行することをオススメします。

MVCパターンで出てくるロジッククラスというと、コントローラの処理をただ委譲しているだけの、コントローラというかHTTPリクエスト情報との依存関係をばっちり持っている謎の位置づけのクラスという実装をいくつか見たことがあるが、イメージコードからすると、そこで言うロジッククラスもそういうものらしい。「テーブル:モデル=1:1」を解決するにはモデルを増設するしか無いのだが、コントローラとの依存関係ばっちりなロジッククラスはモデルではない。こういう形のロジッククラスの効果はコントローラの見た目のコード量が減ることしかないなのだが、どんな利点があるとして、この構成を取っているのだろうか。

はじめにビジネスロジックという階層をどこかに作らなければならないという設計思想があり、それとO/Rマッパが自動生成したクラスは変更しない、けれど本来モデルに実装すべき機能をコントローラに書きたくないというのが混ざった結果、コントローラの分身であるロジッククラスというのが生まれたのではないかと思う。実際、私が観たことのあるロジッククラスに書かれていたものはデータ取得のバリエーション程度のものであり、本来ならモデルクラスにメソッドを追加すれば良い程度のものだった。しかもそこで使用されていたO/RマッパはPropelというもので、独自のメソッドを定義出来るように、生成・更新されるFooBaseクラスと、それを継承したFooクラスという形でクラスを生成してくれるようになっているにも関わらず、Fooクラスは一切触られていなかったのだ。

MVCパターンでは、ビューでもコントローラでもないものはモデルであるはずなので、ロジッククラスが必要であるならばモデルとして実装すべきだろう。とはいえ大抵の場合はDBにマッピングされたクラスにメソッドを追加する程度で済んでしまうので、新たな階層ができてしまうくらいクラスを追加する必要は無いはずだ。

2008年08月06日(水)

ActionMailerと携帯メールアドレス

カテゴリ: Ruby, Ruby on Rails, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - ActionMailerと携帯メールアドレス

携帯サイトで悩まされるのが、世界に迷惑を振りまくdocomo仕様のメールアドレス。RailsのActionMailerも、その影響を逃れられなかった。ActionMailerは下位ライブラリとして日本人作のTMailを使用しており、TMailはdocomo仕様に対応する修正が行われているが、残念ながらピリオド2連続までしか対応していない。解決するにはTMailのraccの文法ファイルを修正してパーサを再生成するしかない。それについては下記の記事を参考にしてもらうとして、気になる、というか困るのが、postfixで受信したメールをTMailで処理するとFromやToのアドレスが正しく得られないという問題の方だ。

postfixは、メールアドレスのlocalpartがRFC2822のフォーマットでない場合は、ダブルクォートで括ることでRFC的に正しい形に補正する(バージョンによってはそうしないらしい)。「aa..bb@example.com」なら「"aa..bb"@example.com」になるという訳だ。この形式ならばTMailは扱えるはずだが、Fromに入っているはずのこのメールアドレスを取得しようとすると、パースに失敗し、nilが返ってくる。このメールアドレスはRFCに準拠しているのでTMailが対応していないとは思えないし、事実、文法ファイルではそれに対応したルールが定義されている。どういうことなのだろうか。

考えても分からないので actionmailer-1.3.6/lib/action_mailer/vendor/tmail/parser.rbのTMail::Parser::MAILP_DEBUGとTMail::Parser::Racc_debug_parserをtrueに書き換えて、デバッグ出力を見てみよう。ちょっと長いが、次のログが "foo"@example.com をパースしたときのものになる。

>> TMail::Mail.parse('From: "foo"@example.com').from
read    :MADDRESS(MADDRESS) :MADDRESS

shift   MADDRESS
        [ (MADDRESS :MADDRESS) ]

goto    7
        [ 0 7 ]

read    :QUOTED(QUOTED) "foo"

shift   QUOTED
        [ (MADDRESS :MADDRESS) (QUOTED "foo") ]

goto    32
        [ 0 7 32 ]

reduce  QUOTED --> word
        [ (MADDRESS :MADDRESS) (word "foo") ]

goto    35
        [ 0 7 35 ]

reduce  word --> local_head
        [ (MADDRESS :MADDRESS) (local_head ["foo"]) ]

goto    28
        [ 0 7 28 ]

read    :ATOM(ATOM) "@example"

reduce  local_head --> addr_phrase
        [ (MADDRESS :MADDRESS) (addr_phrase "foo") ]

goto    26
        [ 0 7 26 ]

shift   ATOM
        [ (MADDRESS :MADDRESS) (addr_phrase "foo") (ATOM "@example") ]

goto    25
        [ 0 7 26 25 ]

reduce  ATOM --> atom
        [ (MADDRESS :MADDRESS) (addr_phrase "foo") (atom "@example") ]

goto    18
        [ 0 7 26 18 ]

reduce  atom --> word
        [ (MADDRESS :MADDRESS) (addr_phrase "foo") (word "@example") ]

goto    35
        [ 0 7 26 35 ]

reduce  word --> local_head
        [ (MADDRESS :MADDRESS) (addr_phrase "foo") (local_head ["@example"]) ]

goto    67
        [ 0 7 26 67 ]

shift   "."
        [ (MADDRESS :MADDRESS) (addr_phrase "foo") (local_head ["@example"]) ("." ".") ]

goto    101
        [ 0 7 26 67 101 ]

read    :ATOM(ATOM) "com"

reduce  "." --> dots
        [ (MADDRESS :MADDRESS) (addr_phrase "foo") (local_head ["@example"]) (dots 0) ]

goto    76
        [ 0 7 26 67 76 ]

shift   ATOM
        [ (MADDRESS :MADDRESS) (addr_phrase "foo") (local_head ["@example"]) (dots 0) (ATOM "com") ]

goto    25
        [ 0 7 26 67 76 25 ]

reduce  ATOM --> atom
        [ (MADDRESS :MADDRESS) (addr_phrase "foo") (local_head ["@example"]) (dots 0) (atom "com") ]

goto    18
        [ 0 7 26 67 76 18 ]

reduce  atom --> word
        [ (MADDRESS :MADDRESS) (addr_phrase "foo") (local_head ["@example"]) (dots 0) (word "com") ]

goto    110
        [ 0 7 26 67 76 110 ]

reduce  local_head dots word --> local_head
        [ (MADDRESS :MADDRESS) (addr_phrase "foo") (local_head ["@example", "com"]) ]

goto    67
        [ 0 7 26 67 ]

reduce  addr_phrase local_head --> addr_phrase
        [ (MADDRESS :MADDRESS) (addr_phrase "foo @example.com") ]

goto    26
        [ 0 7 26 ]

=> nil

何かおかしい感じがする。パーサ的にはクォートされている部分はwordとしてちゃんと解釈されている。が、全体としては (word "foo") (atom "@example") (dots 0) (atom "com") という並びとして解釈されているため、マッチするルールが無くてパースに失敗している。これはなんか、普通のメールアドレスでもうまく行かない感じがするのだが、どうなのだろうか。foo@example.com をパースすると、以下のログが出力された。

> TMail::Mail.parse('From: foo@example.com').from
read    :MADDRESS(MADDRESS) :MADDRESS

shift   MADDRESS
        [ (MADDRESS :MADDRESS) ]

goto    7
        [ 0 7 ]

read    :ATOM(ATOM) "foo@example"

shift   ATOM
        [ (MADDRESS :MADDRESS) (ATOM "foo@example") ]

goto    25
        [ 0 7 25 ]

reduce  ATOM --> atom
        [ (MADDRESS :MADDRESS) (atom "foo@example") ]

goto    18
        [ 0 7 18 ]

reduce  atom --> word
        [ (MADDRESS :MADDRESS) (word "foo@example") ]

goto    35
        [ 0 7 35 ]

reduce  word --> local_head
        [ (MADDRESS :MADDRESS) (local_head ["foo@example"]) ]

goto    28
        [ 0 7 28 ]

read    "."(".") "."

shift   "."
        [ (MADDRESS :MADDRESS) (local_head ["foo@example"]) ("." ".") ]

goto    75
        [ 0 7 28 75 ]

read    :ATOM(ATOM) "com"

reduce  "." --> dots
        [ (MADDRESS :MADDRESS) (local_head ["foo@example"]) (dots 0) ]

goto    76
        [ 0 7 28 76 ]

shift   ATOM
        [ (MADDRESS :MADDRESS) (local_head ["foo@example"]) (dots 0) (ATOM "com") ]

goto    25
        [ 0 7 28 76 25 ]

reduce  ATOM --> atom
        [ (MADDRESS :MADDRESS) (local_head ["foo@example"]) (dots 0) (atom "com") ]

goto    18
        [ 0 7 28 76 18 ]

reduce  atom --> word
        [ (MADDRESS :MADDRESS) (local_head ["foo@example"]) (dots 0) (word "com") ]

goto    110
        [ 0 7 28 76 110 ]

reduce  local_head dots word --> local_head
        [ (MADDRESS :MADDRESS) (local_head ["foo@example", "com"]) ]

goto    28
        [ 0 7 28 ]

read    false($end) "$"

reduce  local_head --> local
        [ (MADDRESS :MADDRESS) (local ["foo@example", "com"]) ]

goto    33
        [ 0 7 33 ]

reduce  local --> spec
        [ (MADDRESS :MADDRESS) (spec #) ]

goto    34
        [ 0 7 34 ]

reduce  spec --> mbox
        [ (MADDRESS :MADDRESS) (mbox #) ]

goto    48
        [ 0 7 48 ]

reduce  mbox --> addr
        [ (MADDRESS :MADDRESS) (addr #) ]

goto    47
        [ 0 7 47 ]

reduce  addr --> addrs
        [ (MADDRESS :MADDRESS) (addrs [#]) ]

goto    50
        [ 0 7 50 ]

reduce  addrs --> addrs_TOP
        [ (MADDRESS :MADDRESS) (addrs_TOP [#]) ]

goto    49
        [ 0 7 49 ]

reduce  MADDRESS addrs_TOP --> content
        [ (content [#]) ]

goto    5
        [ 0 5 ]

shift   $end
        [ (content [#]) ($end "$") ]

goto    42
        [ 0 5 42 ]

shift   $end
        [ (content [#]) ($end "$") ($end "$") ]

goto    81
        [ 0 5 42 81 ]

accept

=> ["foo@example.com"]

パースは成功しているが、なんと、ローカルアドレスとして解釈されている! なんだろうこれ。良く考えてみると、そもそもatomに「@」が含まれているのがおかしい。atomの元のルールはATOMで、すべて大文字のルールは終端記号なので、スキャナの方からやって来る。したがってその定義はスキャナの方にあるはずだ。スキャナはピュアRuby版とバイナリ版があるようだが、ピュアRuby版のソースを見てみると、ATOMに含まれる文字の定義が見つかった。

actionmailer-1.3.6/lib/action_mailer/vendor/tmail/scanner_r.rb

    atomsyms   = %q[  _#!$%&`'*+-{|}~^@/=?  ].strip
    tokensyms  = %q[  _#!$%&`'*+-{|}~^@.    ].strip

確かに「@」が含まれている。これではうまく動くはずがない。しかし、こんな恐ろしいバグがTMailに存在するとは思えないのだが......

実はここまで来るのにかなり時間がかかっているのだが、それはparse.yの中味を確認するために、TMailの配布元から入手したソースコードの見ていたからだった。実は、こちらの方のscanner_r.rbのATOMの定義には「@」は含まれていない。だからどうしてこんな結果になってしまうのか全く理解できなかったのだが、ActionMailerに添付されている方のソースコードを見た時に全ての謎が解けた。おそらくtokensymsに「@」を追加した際に、勢いでatomsymsの方にも追加してしまったのだろう。こうなっている原因がActionMailerに添付されているTMailが0.10.7というものすごく古いバージョンだからなのか、それともActionMailer独自の修正が行われているからなのかは分からないが、とにかく、動いているように見えるのは偶然に過ぎないということなのだ。

ちなみにこのスキャナのバグは、ActionMailer 2.0.0以降に添付されているTMailでは修正されている。

2008年08月10日(日)

「オブジェクト指向でなぜ作るのか」は読んではいけない

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 「オブジェクト指向でなぜ作るのか」は読んではいけない

会社の本棚に何気なく「オブジェクト指向でなぜ作るのか」が入っていたのでちょっと読んでみたのだが、この本はあまり良くないように思う。少なくとも、JavaScript、ActionScript、Ruby、Perlなどの言語を主とするWeb系プログラマーは、読んでも意味がないというか、むしろ混乱するだけなのではないかと思う。

何となくググってみたら、タイムリーな感じで話題になっている模様。

具体的に何が悪いか結論が出ていない模様だが、結局、内容が間違っているのではなく、本の題名が間違っているという事ではないかと思う。題名からするとオブジェクト仕様一般の話を期待してしまうが、CからC++に移行するのに必要な知識が書かれているだけの本だ。C++初心者にはそれなりに役立つとは思うが、抽象データ型のスーパセット系以外のオブジェクト指向言語の利用者にはむしろ毒だろう。

2008年08月17日(日)

強い型付けとは

カテゴリ: C#, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 強い型付けとは

WikipediaのC#の頁には言語の特徴として「強い静的型付け」というものが挙げられていた。静的な型付けは良いとして、強い型付けというのはどういうことを示しているのだろう。Wikipediaにある強い型付けの説明を読んでもいまいちよく分からないというか、タイプセーフ性と何が違うのか分からない。ググってみると「定義は色々あり、はっきりしない」とあったりしてあれだが、英語版のWikipediaにStrongly-typed programming languageというページがあった。まとめると概ね以下のいずれかの意味で使われているようだ。

  1. 静的な型付けと静的な型チェックがあること
  2. タイプセーフであること
  3. 型変換の機能を持たないこと
  4. 暗黙の型変換が行われないこと
  5. 型システムを回避する方法が存在しないこと
  6. 複雑できめ細かい型システムがあること(Common lispの特徴?)
  7. データ型が不変オブジェクトであること(参照透明性のこと?)
  8. プログラム実行前に実行時の挙動が強く保証されていること

C#のページの著者はどの意味で使ったのだろう。1つ目は静的な型付けと言えば十分なので、まあ違うだろう。C#にはキャストがあり、かつ暗黙のキャストも定義できるので、型変換系は該当しないはずだ。6〜8は何か特殊すぎるので違う気がする。ということはタイプセーフ性の事を意味しているということなのだろうか。

ちなみに「弱い型付け」の方は、おおむね「情報を損なうような暗黙の型変換が行われること」という意味で使われるようだ。つまり、PerlやJavaScript、VBのように、数値と文字列を暗黙のうちに変換してしまうような言語を弱い型付けの言語と言う。その逆の意味とすれば「強い型付け」は「暗黙の型変換が行われないこと」ということになるような気がする。ググった時に見つけた以下のページでは、いちばん妥当な定義が「型変換の機能を持たないこと」だとしているが、どうしてそういう結論になったのか興味深いところ。

強い型付け vs 強いテスト

カテゴリ: プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - 強い型付け vs 強いテスト

強い型付けについて調べていたら、BEST SOFTWARE WRITINGの「強い型付け vs 強いテスト」の感想が検索結果にあった。ちなみにここでの「強い型付け」というのは静的な型付けの意。

強いテスト万歳という結論になっているが、本当にそこで終わって良いのだろうか。強いテストの理論では、型チェックをテストコードとして実装しなければならないわけだが、チェックの深度も書き方も、テストコードの実装者によって異なることが考えられる。適切なチェックであることを誰がどうやって検証するのかが問題となるが、それができればソフトウェアの品質で誰も悩まないだろう。また、テストコードは、実装の変更に追随して修正していく必要があり、たとえば何かの型が変わったら、テストコードのどこかに埋まっている型チェックのコードを見つけ出し、変更しなければならない。追加開発というのは担当者が変わりがちなものだが、テストコードにはおそらく仕様書も資料も無いだろう。新しい担当者にそんなコードを修正する能力が無かったら、自動化テストは形骸化する。自動化テストさえあればいいなんて言えるのは、そういうことを普通にできる人が普通にいる組織(あるいは個人)だからこそなのだ。

テストすれば良いというのは間違いないが、強いテスト論はテストの品質と保守のことが考えられていない感じを受ける。静的な型付けを持つ言語では、文法に従って型を記述しさえすれば、コンパイラが一定の水準のチェックを行ってくれるのである。この安定性は思っているよりも大きい。テストの自動化という点で考えると、単調な型チェックこそ自動化するために、静的型付け言語を使うべきではないかと私は思う。

2008年08月31日(日)

パフォーマンスカウンタのアクセス権限

カテゴリ: C#, MacFace, Windows, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - パフォーマンスカウンタのアクセス権限

WMI経由で他のマシンの稼働状態も見れたりするMacFace for Windows Enterprise Edition(仮)の構想を温めるために、他のマシンのパフォーマンスカウンタを参照する際の認証はどうなっているのかと.NET FrameworkのSystem.Diagnostics.PerformanceCounterクラスのリファレンスマニュアルを見ていたら、こんな注意書きを見つけた。

PerformanceCounter クラス (System.Diagnostics)

NET Framework Version 1.0 および 1.1 では、このクラスは直接の呼び出し元に完全な信頼を要求します。バージョン 1.1 より後の .NET Framework では、このクラスは、特定のアクションについて PerformanceCounterPermission を要求します。

PerformanceCounter コンストラクタ (String, String, String, String) (System.Diagnostics)

メモ : Windows Vista、Windows XP Professional x64 Edition、または Windows Server 2003 でパフォーマンス カウンタを読み取るには、Performance Monitor Users グループに属しているか、または、管理者特権を持っている必要があります。

どうやら.NET Frameworkからパフォーマンスカウンタを参照する場合は、何らかの権限が必要になるようだ。もしかすると、MacFaceでパフォーマンスカウンタにアクセスできなくて起動できない場合があるというのはこれが原因なのだろうか。気になったので調べてみた。

PerformanceCounterPermissionというのは、.NET Frameworkのセキュリティモデル固有のもののようだが、通常与えられているものなのか、どうやって取得するのかといったことがMSDNライブラリで見つけられなくて困った。ググって見つけた「@IT:インサイド .NET Framework [改訂版]」の第6回~最終回にちょうどその辺りのことが詳しく書かれていた。

PerformanceCounterPermissionはアクセス許可のひとつで、アクセス許可は基本的にはIEでおなじみのゾーンごとにアクセス許可セットとしてまとめて与えられる。ローカルマシンにインストールされたアプリケーションはMyComputerゾーンに属し、与えられるアクセス許可セットはFullTrustなので、問題なくパフォーマンスカウンタにアクセスすることができる。共有フォルダから起動するとLocalIntranetゾーンに属し、そこでは結構きつめのアクセス許可セットであるLocalIntranetが与えられる。このアクセス許可セットにはPerformanceCounterPermissionは含まれていないので、パフォーマンスカウンタにはアクセスできない。原因はこれだろうか?

もうひとつのほうは簡単で、Windows Vistaなどでは、管理者権限か特殊なグループに属していないとパフォーマンスカウンタにアクセスできないというものである。パフォーマンスカウンタには結構機微な情報が詰まっているので、セキュリティ的な事情でそうせざるを得ないというのは理解できなくもない。だがHome系のEditionでは、ユーザーのグループを変更するなどといった比較的高度な管理機能が省かれているため、Vistaでは事実上管理者権限を持っていないとパフォーマンスカウンタにアクセスできないことになる。

CPUやメモリの状態はパフォーマンスカウンタを使わなくても取得できるのだが、ディスクアクセスの統計情報だけはパフォーマンスカウンタ以外では取得できないようで困っている。コンピュータの利用可能度合いをエモーショナルな感じで表すことを目的としているMacFaceとしては、今のPCで最もパフォーマンスに影響を及ぼしていると思われるディスクアクセス状況はぜひとも取り込みたいところ。とはいえ、単なるCPUメーターなのに「使うためには管理者権限が必要です」というのはさすがにいかがなものか。

ちなみにVistaのリソースモニタは全面的にパフォーマンスカウンタを使っているようで、管理者権限の無いアカウントで動かすと、こんな表示になってしまうのだった。

これらのツールのすべての機能を使用するには、AdministratorグループまたはPerformance Log Usersグループのメンバである必要があります。

2008年09月25日(木)

MySQLのカラム切り捨て脆弱性

カテゴリ: セキュリティ, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - MySQLのカラム切り捨て脆弱性

WordPress 2.6.2で修正された「mt_srand and not so random numbers」と「MySQL and SQL Column Truncation Vulnerabilities」のうち、MySQLの方は他のアプリケーションでも影響しているものがありそうな感じがする。セキュリティ上の問題まで行かなくても、意図しない挙動になっていたりしないだろうか。

この問題はMySQLの以下の2つの挙動によって起る。

  • INSERT、UPDATE文でカラムの長さ以上の文字列を挿入しても、エラーにせず文字列末尾を切り捨てたものを挿入する
  • WHERE句での文字列の比較は、文字列末尾のスペースを切り捨てたもので行われる

論理削除を行えるようにした場合、カラムに一意制約を付けられないため、アプリケーション側で一意性チェックを行うことで、一意性が保証されるようにすることがある。が、この2つの挙動を利用すると、データ登録時の一意性チェックをすり抜けることが出来てしまう。 まず、重複させようとする値の末尾にカラムの最大長までスペースを付け、その後ろにスペースでない文字を付けたものを用意する。一意性チェックでは、テーブルを検索して同じ値がないことを確認する訳だが、スペースではないものは切り捨てられないので、この文字列は既存の値とはマッチしない。一意でないことが確認できたので、この文字列はテーブルに挿入される。すると末尾の文字が切り捨てられ、末尾はスペースのみになる。MySQLは末尾のスペースを無視して検索するので、データを取り出す時には、スペース有り無しの両方がマッチしてしまう。1件しかマッチしないという前提で作られた処理がどうなるかは実装次第だ。

入力時に文字列の長さチェックを行っている場合、末尾の切り捨てが起るような文字列が挿入されることがないため、この問題はかろうじて回避されている。根本的な解決方法は、MySQLのSQLモードにSTRICT_ALL_TABLESかSTRICT_TRANS_TABLESを指定して、カラムに不正値を挿入した時にエラーになるようにすることである。その他のMySQL独特の挙動を抑制するためにTRADITIONALを指定する方が良い感じではあるが、アプリケーションがその挙動に依存している可能性もあるので、変更するのは難しいかもしれない。

ちなみにカラム切り捨ての方は、文字コードを指定せずにテーブルを作った場合に問題になる場合がある。たとえばMobavle Typeのエントリータイトルに255文字以上入力して保存すると、末尾の文字が化けてしまう場合がある。これはMovable Type側はUTF-8の文字列を送っているが、MySQL側はISO-8859-1として扱うので、UTF-8の途中のバイトで切り捨てられてしまうためである。

2008年09月28日(日)

Event Tracing for Windows

カテゴリ: MacFace, Windows, プログラミング このエントリーを含むはてなブックマーク はてなブックマーク - Event Tracing for Windows

コメントで教えてもらったEvent Tracing for Windowsについて調べてみた。

Event Tracing for Windows(ETW)は、デバイスドライバやアプリケーションでトレース出力を行うための仕組みである。特にデバイスドライバでは行えることに制限があるため、処理の各ポイントでトレース情報を出力して動作状態を観察するということを行うのは難しいが、ETWを使えばそれを容易に行うことができる。また、トレース情報をXMLとして取り出す事ができるので、膨大なトレース情報を処理することも難しくない。「信頼性とパフォーマンスモニタ」からトレース情報をetlファイルとして保存する事ができる。Windows Performance Toolkit(xperf)は、NTカーネルのetlファイルを解析してパフォーマンス情報をグラフ化してくれるツールである。また、オフライン処理だけではなく、リアルタイムにトレース情報を受けとることもできる。

mmc-trace-provider.png

ETWではトレースイベントを登録する方をプロバイダ、取りだす方をコンシューマと読んでいるが、コンシューマ側の利用方法はおおむね以下の通りである。

  1. OpenTrace()でトレースセッションを開く
  2. ProcessTrace()を呼び出すことで、ファイルへの出力やコールバックの呼び出しが行われる
  3. CloseTrace()でトレースセッションを閉じる

OpenTrace()に渡すPEVENT_TRACE_LOGFILE構造体が、その名前に反してリアルタイムモードでのコールバック関数の指定にも使われるので少しややこしい。リアルタイムモードにするにはProcessTraceModeフィールドにPROCESS_TRACE_MODE_REAL_TIMEを指定し、EventCallbackフィールドにコールバック関数へのポインタを指定すれば良い。トレース情報はコールバック関数の引数として渡されるEVENT_TRACE構造体のMofDataフィールドに入っている。カーネルが出しているトレース情報はMOF(Managed Object Format)になっている。中味の詳しい情報はWMI経由で入手できるらしい。

パフォーマンスカウンタはカウンタの種類に関わらずアクセスには管理者権限を必要とするが、ETWはACLによってもっと細かく制御できるようになっているらしい。「信頼性とパフォーマンスモニタ」からACLを確認することができる。それによると、NT Kernelのトレース情報を参照するには管理者権限が必要なようだ。残念ながらCPUメータに使うのは現実的ではなさそうだ。

mmc-nt-kernel-provider-acl.png