Ruby: 外部コマンドとの IPC

ある Samba のバグ を回避する方法を探っている。 Samba をアップグレードできないことが想定されるため、 クライアント側 (今回は Ruby スクリプト) で対処しなければならない。 そこで、 Ruby スクリプトから Samba の rpcclient コマンドを利用して SID の名前解決を実行すべく、 外部コマンドと IPC するクラスを書いてみた。

#!/usr/bin/ruby -w

IPC class with external command

class CmdIPC attrreader :reader, :writer def initialize(command, *args, &ipcproc) if command.class == Array @command, @arg0 = command else @command = @arg0 = command end @args = args @ipcproc = ipcproc @pid = nil startcommand end def startcommand @reader, cwriter = IO.pipe c_reader, @writer = IO.pipe

Parent process returns here

return if @pid = fork

Child process

begin @reader.close @writer.close STDIN.reopen(creader) STDOUT.reopen(cwriter) creader.close cwriter.close exec([@command, @arg0], @args) rescue => e raise "cannot execute external command: #{@command}: #{e}" end end def stopcommand if @pid @reader.close @writer.close Process.kill('TERM', @pid) Process.waitpid(@pid) end @pid = nil end def restartcommand stopcommand startcommand end def ipc(input) @ipcproc.call(self, input) end end if FILE == $0 lookupsid = CmdIPC.new('/usr/bin/rpcclient', '-U%', '-d0', '127.0.0.1') { |sid, reader, writer| writer.puts("lookupsids #{sid}") next unless line = reader.gets next unless match = line.match(/^S(?:-\d+)+\s(.)\s+(\d+)$/) next match[1] } puts lookupsid.ipc('S-1-1-0') puts lookupsid.ipc('S-1-3-0') puts lookupsid.ipc('S-1-3-1') end

注意: この実装では、出力をバッファリングするコマンドに対しては利用できない。

(まだ素人なので)この実装がオブジェクト指向的 (Ruby 的) に美しいかどうかはよくわからないが、 参考書を片手に数分でそれっぽいのが完成してしまった。 ちょっと感動した。 Ruby だとより直感的に書けるということかな。 ソフトウェア関係で感動したことは、過去、両手で足りる回数しか経験がないので、 貴重な体験だった。

一点気になるのは、CmdIPC.new に渡しているコードブロックには return 文が書けないこと。 Ruby のリファレンスを読めば理由は納得できるのだが、 next は違和感ありすぎ。 せめて break ならいいのだけど、 それだと LocalJumpError 例外が発生して break from proc-closure と文句を言われる。 何か間違っているのかなぁ?

add to hatena hatena.comment (1) add to del.icio.us (0) add to livedoor.clip (0) add to Yahoo!Bookmark (0) Total: 1

コメントを書く

Your email is never shared. * 印の項目は必須項目です。

*
*