-# A simple IPC server for executing arbitrary Ruby program.
-# The protocol is based on Assuan protocol of GnuPG.
-# http://www.gnupg.org/(en)/related_software/libassuan/index.html
+# A simple IPC server executing Ruby programs.
require 'thread'
-require 'stringio'
class RubyServ
def initialize
- @buf = StringIO.new
+ @buf = ''
@que = Queue.new
+ @thr = Hash.new
end
def dispatch(line)
case line.chomp
when /\AD /
- return @buf << unescape($')
+ @buf << $'
when /\A(\S+)\s*/
c = $1
r = $'
end
def dispatch_eval(c, r)
- r = deq_data if r.empty?
- p r
- open('|-') do |f|
- if f
- d = f.read
- Process.wait
- send_data(d)
- if $?.success?
- puts("OK\r\n")
- else
- puts("ERR #{$?.exitstatus}\r\n")
- end
+ name, code = r.split(/\s+/, 2)
+ if @thr.include?(name) && @thr[name].alive?
+ puts("ERR 105 Parameter error: \"#{name}\" is already in use\r\n")
+ return
+ end
+ code = deq_data unless code
+ puts("OK\r\n")
+ @thr[name] = Thread.current
+ Thread.current[:rubyserv_name] = name
+ begin
+ Thread.current[:rubyserv_error] = false
+ Thread.current[:rubyserv_response] = eval(code)
+ rescue Exception => e
+ Thread.current[:rubyserv_error] = true
+ Thread.current[:rubyserv_response] = e.to_s.sub(/\A.*?\n/m, '')
+ end
+ puts("# exit #{name}\r\n")
+ end
+
+ def dispatch_poll(c, r)
+ thr = @thr[r]
+ if !thr
+ puts("ERR 105 Parameter error: no such name \"#{r}\"\r\n")
+ elsif thr.alive?
+ puts("S program running\r\n")
+ puts("OK\r\n")
+ else
+ @thr.delete(r)
+ if thr[:rubyserv_error]
+ puts("S program exited\r\n")
else
- eval(r)
- exit
+ puts("S program finished\r\n")
end
+ if d = thr[:rubyserv_response]
+ send_data(d.to_s)
+ end
+ puts("OK\r\n")
end
end
s.gsub(/%([0-9A-Z][0-9A-Z])/, ['\1'].pack('H*'))
end
+ def output(s)
+ puts("# output #{Thread.current[:rubyserv_name]} #{s}\r\n")
+ end
+
def send_data(d)
+ d = escape(d)
begin
- r = [d.length, 998].min # 998 = 1000 - CRLF
- (0 ... r).each do |i|
- r -= 2 if d[i] =~ /[%\r\n]/
- end
- puts("D #{escape(d[0 ... r])}\r\n")
- d = d[r .. -1]
+ len = [d.length, 998].min # 998 = 1000 - "D "
+ puts("D #{d[0 ... len]}\r\n")
+ d = d[len .. -1]
end until d.empty?
end
def enq_data
- @que.enq(@buf.string)
+ d = unescape(@buf)
+ @buf = ''
+ @que.enq(d)
end
def deq_data