ある 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




