1 # vim: ts=2:sw=2:sts=2:et:fdm=marker
10 def systemu(*a, &b) SystemUniversal.new(*a, &b).systemu end
17 SystemUniversal::VERSION = '1.2.0' unless defined? SystemUniversal::VERSION
18 def version() SystemUniversal::VERSION end
23 @host = Socket.gethostname
26 @turd = ENV['SYSTEMU_TURD']
29 ruby = File.join(c['bindir'], c['ruby_install_name']) << c['EXEEXT']
30 @ruby = if system('%s -e 42' % ruby)
33 system('%s -e 42' % 'ruby') ? 'ruby' : warn('no ruby in PATH/CONFIG')
37 %w( host ppid pid ruby turd ).each{|a| attr_accessor a}
44 def initialize argv, opts = {}, &block
50 @stdin = getopt[ ['stdin', 'in', '0', 0] ]
51 @stdout = getopt[ ['stdout', 'out', '1', 1] ]
52 @stderr = getopt[ ['stderr', 'err', '2', 2] ]
53 @env = getopt[ 'env' ]
54 @cwd = getopt[ 'cwd' ]
56 @host = getopt[ 'host', self.class.host ]
57 @ppid = getopt[ 'ppid', self.class.ppid ]
58 @pid = getopt[ 'pid', self.class.pid ]
59 @ruby = getopt[ 'ruby', self.class.ruby ]
71 IO.popen "#{ @ruby } #{ c['program'] }", 'r+' do |pipe|
75 cid = Integer line[%r/\d+/]
79 buf = "#{ line }#{ buf }"
81 raise unless Exception === e
84 raise "wtf?\n#{ buf }\n"
87 thread = new_thread cid, @block if @block
98 status.instance_eval{ @thread = thread }
105 if @stdout or @stderr
106 open(c['stdout']){|f| relay f => @stdout} if @stdout
107 open(c['stderr']){|f| relay f => @stderr} if @stderr
110 [status, IO.read(c['stdout']), IO.read(c['stderr'])]
115 def new_thread cid, block
117 Thread.new(cid) do |cid|
118 current = Thread.current
119 current.abort_on_exception = true
127 stdin = File.expand_path(File.join(tmp, 'stdin'))
128 stdout = File.expand_path(File.join(tmp, 'stdout'))
129 stderr = File.expand_path(File.join(tmp, 'stderr'))
130 program = File.expand_path(File.join(tmp, 'program'))
131 config = File.expand_path(File.join(tmp, 'config'))
134 open(stdin, 'w'){|f| relay @stdin => f}
136 FileUtils.touch stdin
138 FileUtils.touch stdout
139 FileUtils.touch stderr
148 c['program'] = program
149 open(config, 'w'){|f| YAML.dump c, f}
151 open(program, 'w'){|f| f.write child_program(config)}
164 def child_program config
170 config = YAML.load(IO.read('#{ config }'))
172 argv = config['argv']
175 stdin = config['stdin']
176 stdout = config['stdout']
177 stderr = config['stderr']
180 env.each{|k,v| ENV[k.to_s] = v.to_s} if env
186 PIPE.puts "pid: \#{ Process.pid }"
187 PIPE.flush ### the process is ready yo!
191 rescue Exception => e
192 PIPE.write Marshal.dump(e) rescue nil
199 src, dst, ignored = srcdst.to_a.first
200 if src.respond_to? 'read'
201 while((buf = src.read(8192))); dst << buf; end
203 src.each{|buf| dst << buf}
207 def tmpdir d = Dir.tmpdir, max = 42, &b
211 tmp = File.join d, "systemu_#{ @host }_#{ @ppid }_#{ @pid }_#{ rand }_#{ i += 1 }"
225 FileUtils.rm_rf tmp unless SystemU.turd
234 def getopts opts = {}
236 keys, default, ignored = args
238 [keys].flatten.each do |key|
239 [key, key.to_s, key.to_s.intern].each do |key|
240 throw 'opt', opts[key] if opts.has_key?(key)
249 SystemU = SystemUniversal unless defined? SystemU
267 date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " )
269 status, stdout, stderr = systemu date
270 p [status, stdout, stderr]
272 status = systemu date, 1=>(stdout = '')
275 status = systemu date, 2=>(stderr = '')
280 sleep = %q( ruby -e" p(sleep(1)) " )
281 status, stdout, stderr = systemu sleep
282 p [status, stdout, stderr]
284 sleep = %q( ruby -e" p(sleep(42)) " )
285 status, stdout, stderr = systemu(sleep){|cid| Process.kill 9, cid}
286 p [status, stdout, stderr]
290 env = %q( ruby -e" p ENV['A'] " )
291 status, stdout, stderr = systemu env, :env => {'A' => 42}
292 p [status, stdout, stderr]
296 env = %q( ruby -e" p Dir.pwd " )
297 status, stdout, stderr = systemu env, :cwd => Dir.tmpdir
298 p [status, stdout, stderr]