CoffeeScript を使える環境を作ったメモ
% sudo portinstall www/node % npm install -g coffee-script
# homebrew を入れて % /usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)" # node.js をインストールして % brew install node # npm を入れてから % curl http://npmjs.org/install.sh | sh % npm install -g coffee-script
FreeBSD なサーバーに VirtualBox を入れて、その上で Ubuntu server を動かす
というのを CUI だけで行う手順。(途中で VNC は使っちゃうけど。)
VBoxGuestAdditions について追記。
VirtualBox インストール
まずは、VirtualBox をインストールする。ただし、make option で、"Build with VNC support" を追加しておく。
% sudo portinstall emulators/virtualbox-ose
インストールが終わったら /boot/loader.conf に以下を追記する。
vboxdrv_load="YES"
さらに、/etc/rc.conf に以下を追記する。
vboxnet_enable="YES"
インストールはここまで。ドライバーを読ませるために再起動しておく。
ゲスト OS インストール
ゲスト OS をインストールするには、まず以下でサポートされているOSのタイプを調べる。
% VBoxManage list ostypes (snip) ID: Ubuntu Description: Ubuntu (snip)
今回は ubuntu を入れるので、これをメモ。
いよいよ VM 作成。
# VM を作成 % VBoxManage createvm -name "ubuntu server" -basefolder /path/to/vm/dir --ostype Ubuntu --register # HDD を作成 % VBoxManage createhd --filename "ubuntu server/ubuntu server.vdi" --size 102400 # IDE コントローラーを VM に追加。(初めは SirialATA でやってみたけど、途中でハングした。) % VBoxManage storagectl "ubuntu server" --name "IDE Controller" --add ide # HDD をアタッチ % VBoxManage storageattach "ubuntu server" --storagectl "IDE Controller" --port 0 --device 0 --type hdd --medium "ubuntu server/ubuntu server.vdi" # ゲスト OS のインストールディスクをアタッチ % VBoxManage storageattach "ubuntu server" --storagectl "IDE Controller" --port 1 --device 1 --type dvddrive --medium ubuntu-11.04-server-i386.iso # VM を VNC (port 2929) 付きで起動 % VBoxHeadless --startvm "ubuntu server" --vnc --vncport 2929
この状態で、VNC で繋いでみると ubuntu のインストーラーが上がっているはずなので、普通にインストールする。
インストールが完了したら、インストールディスクをデタッチする。
% VBoxManage storageattach "ubuntu server" --storagectl "IDE Controller" --port 1 --device 1 --type dvddrive --medium none
NIC をブリッジ接続でアタッチする。
% VBoxManage modifyvm "ubuntu server" --nic1 bridged --bridgeadapter1 bge0 --nicspeed1 1000000
使わないので VRDE を切る
% VBoxManage modifyvm "ubuntu server" --vrde off
これで、完了。あとは、ゲスト OS 側で SSH を設定してやれば VNC はもう要らないので、次回以降 VM を起動するときは以下のコマンドを使う。
% VBoxHeadless --vrde off --startvm "ubuntu server"
VBoxGuestAdditions インストール
まず、Guest OS 側で dkms をインストールしておく。
% sudo aptitude install dkms
ホスト側で、VBoxGuestAdditions.iso をアタッチする。(Guest 停止中じゃないとダメかも。)
% VBoxManage storageattach "ubuntu server" --storagectl "IDE Controller" --port 1 --device 0 --type dvddrive --medium /usr/local/lib/virtualbox/additions/VBoxGuestAdditions.iso
次に、Guest 側で CDROM を mount して、cd して以下を実行する。(x 入ってないので --nox11 付けてる。)
% sudo sh VBoxLinuxAdditions.run --nox11
Guest を停止して、ホスト側で以下を実行して VBoxGuestAdditions.iso をデタッチする。
% VBoxManage storageattach "ubuntu server" --storagectl "IDE Controller" --port 1 --device 0 --type dvddrive --medium none
Guest を起動しなおせば完成。
SSL_read, SSL_write での SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE と、SSL_pending の話
TLS/SSL による通信をやりたい場合、OpenSSL を使えば簡単に実装することができる。
具体的には、recv(2), send(2) を直に発行するノリで、SSL_read(3), SSL_write(3) を使えばいい……と思っていたが、そうではないらしい。
ということで、調べたことをつらつら書いてみる。なお、以下は socket が non-blocking であることを想定して書いている。
それから、OpenSSL をそこまで読み込んだわけではないので、間違っているかもしれない。ツッコミ大歓迎!
SSL_read での SSL_ERROR_WANT_READ. SSL_write での SSL_ERROR_WANT_WRITE
データの送信を行おうとしたときに TCP/IP のバッファがいっぱいで積めない場合、send は EAGAIN でエラーする。
同様に SSL_write は SSL_ERROR_WANT_WRITE を返す。この時、socket が writable になったら、全く同じ引数で SSL_write を呼ばなければならない。
WARNING
When an SSL_write() operation has to be repeated because of
SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be repeated with
the same arguments.When calling SSL_write() with num=0 bytes to be sent the behaviour is
undefined.
recv の場合は、TCP/IP のバッファが空であった場合に EAGAIN でエラーする。
同様に SSL_read は SSL_ERROR_WANT_READ でエラーする。この時 SSL_write と同じで、socket が readable になったら、全く同じ引数で SSL_read を呼ばなければならない。
ここで注意しなければならないのが、select(2) で readable だったとしても、SSL_read が SSL_ERROR_WANT_READ でエラーすることがあるという点。
TLS/SSL では、データはある程度ブロックに分けられた上で、暗号化されて送受信されているため、1ブロック分全部そろっていないとデコードできない。よって、中途半端にデータを受信した場合にも SSL_ERROR_WANT_READ が返るということになる。
SSL_read での SSL_ERROR_WANT_WRITE. SSL_write での SSL_ERROR_WANT_READ
ややこしい事に、先ほどとは逆の理由でエラーすることがある。
OpenSSL では、SSL_read, SSL_write の延長上でネゴシエーション処理が実行されることがあるために、read しようとしたのに WANT_WRITE, write しようとしたのに WANT_READ が返ってきてしまう。
よって、先程の仕様とあわせると、
しないといけない。
すなわち……
SSL_read | SSL_write | |
SSL_ERROR_WANT_READ でエラー | select(readable), SSL_read | select(readable), SSL_write |
SSL_ERROR_WANT_WRITE でエラー | select(writable), SSL_read | select(writable), SSL_write |
これはメンドクサイですね…。
SSL_pending
更に、SSL_pending という物がある。
この関数は、ssl オブジェクトのバッファから block せずに読み取り可能な byte 数を返すものである。
ここで注意しなければならないのが、socket が readable ではない場合にも ssl オブジェクトのバッファにデータが乗っていることがあるということである。*1
これを考慮すると、select で readable か判定する前に SSL_pending で SSL_read *2可能かどうかチェックする必要性が見えてくる。
ただし、次の記載が man に有って、少々気にはなる。
BUGS
(snip)Up to OpenSSL 0.9.6, SSL_pending() does not check if the record type of
pending data is application data.
Submit ボタンを押した瞬間に disabled にして、2度押しを抑止する
$("form").submit(function(){ $(this).find(":submit").attr("disabled", "disabled"); });
IPC::Open3 を使って、子プロセスの標準出力と標準エラー出力をポーリングする。(Windows でも動くよ!)
先日の
IPC::Open3 を使って、子プロセスの標準出力と標準エラー出力をポーリングする。(Windows では動かなかった…) - ◆F99a.q8oVEの日記
は Windows では動きませんでした。
Windows のルートでは
# $dad_wtr は open3 の第2引数 # open3(undef, '>&'. fileno($child_out), '>&' . fileno($child_err), @cmd) # を実行したとすると '>&'. fileno($child_out) $dad_wtr =~ s/^[<>]&// $kid_rdr = \*{$dad_wtr};
されたものが fdopen に渡されています。
ここで、上の例のように fd を渡してもうまくいきませんでした。
そこで、
open3(undef, '>&'. $child_out, '>&' . $child_err, @cmd);
こうして見てもうまくいかないので…
色々試して↓ならうまくいきました。"*" できるもの、つまりグロブを指定しないといけないということかな?
open3(undef, '>&CHILD_OUT', '>&CHILD_ERR', @cmd);
ということで、完成版。
qx2 は Windows 以外、qx2_win は Windows 用。Windows では WIFEXITED が定義されていないと思うので、適宜あれしてください。
#!/usr/bin/env perl use strict; use warnings; sub qx2_win { my @cmd = @_; use IPC::Open3; use File::Temp qw/tmpnam/; my $out_file = tmpnam(); my $err_file = tmpnam(); local (*CHILD_OUT, *CHILD_ERR); open CHILD_OUT, '+>', $out_file or die $!; open CHILD_ERR, '+>', $err_file or die $!; my $pid = open3(undef, '>&CHILD_OUT', '>&CHILD_ERR', @cmd); waitpid $pid, 0; # my $code = WIFEXITED($?) ? WEXITSTATUS($?) : WTERMSIG($?); my $code = ($? | 0xFF) ? ($? >> 8) & 0xFF : $?; seek CHILD_OUT, 0, 0; seek CHILD_ERR, 0, 0; my $out = do { local $/; <CHILD_OUT>; }; my $err = do { local $/; <CHILD_ERR>; }; close CHILD_OUT; close CHILD_ERR; unlink $out_file, $err_file; return ($out, $err, $code); } sub qx2 { my @cmd = @_; use IPC::Open3; use Symbol qw/gensym/; use IO::Select; use POSIX ":sys_wait_h"; my ($child_out, $child_err) = (gensym, gensym); my $pid = open3(undef, $child_out, $child_err, @cmd); my $s = new IO::Select($child_out, $child_err); my $out = my $err = ''; while (1) { while (my @ready = $s->can_read) { for my $fh (@ready) { if (sysread($fh, my $buf, 4096) > 0) { if ($fh == $child_out) { $out .= $buf; } elsif ($fh == $child_err) { $err .= $buf; } } else { $s->remove($fh); close $fh; } } } if (waitpid($pid, WNOHANG) > 0) { last; } } my $code = WIFEXITED($?) ? WEXITSTATUS($?) : WTERMSIG($?); return ($out, $err, $code); } use Test::More; my @tests = ( { command => [ 'perl', '-e', q{print 'a'} ], out => 'a', err => '', code => 0 }, { command => [ 'perl', '-e', q{exit 0} ], out => '', err => '', code => 0 }, { command => [ 'perl', '-e', q{exit 1} ], out => '', err => '', code => 1 }, { command => [ 'perl', '-e', q{exit 255} ], out => '', err => '', code => 255 }, { command => [ 'perl', '-e', q{print 'a' x (1024 ** 2)} ], out => 'a' x (1024 ** 2), err => '', code => 0 }, { command => [ 'perl', '-e', q{print STDERR 'a' x (1024 ** 2)} ], out => '', err => 'a' x (1024 ** 2), code => 0 }, { command => [ 'perl', '-e', q{print STDERR 'b' x (1024 ** 2); sleep 3; print 'a' x (1024 ** 2)} ], out => 'a' x (1024 ** 2), err => 'b' x (1024 ** 2), code => 0 }, ); for my $func (\&qx2, \&qx2_win) { for my $test (@tests) { my ($out, $err, $code) = &$func(@{$test->{command}}); is $out, $test->{out}; is $err, $test->{err}; is $code, $test->{code}; } } done_testing;