10 def systemu(*a, &b) SystemUniversal.new(*a, &b).systemu end
17 SystemUniversal::VERSION = '2.5.2' unless SystemUniversal.send(:const_defined?, :VERSION)
18 def SystemUniversal.version() SystemUniversal::VERSION end
19 def version() SystemUniversal::VERSION end
24 @host = Socket.gethostname
27 @turd = ENV['SYSTEMU_TURD']
29 c = begin; ::RbConfig::CONFIG; rescue NameError; ::Config::CONFIG; end
30 ruby = File.join(c['bindir'], c['ruby_install_name']) << c['EXEEXT']
31 @ruby = if system('%s -e 42' % ruby)
34 system('%s -e 42' % 'ruby') ? 'ruby' : warn('no ruby in PATH/CONFIG')
37 class << SystemUniversal
38 %w( host ppid pid ruby turd ).each{|a| attr_accessor a}
41 words.map{|word| word.inspect}.join(' ')
49 def initialize argv, opts = {}, &block
55 @stdin = getopt[ ['stdin', 'in', '0', 0] ]
56 @stdout = getopt[ ['stdout', 'out', '1', 1] ]
57 @stderr = getopt[ ['stderr', 'err', '2', 2] ]
58 @env = getopt[ 'env' ]
59 @cwd = getopt[ 'cwd' ]
61 @host = getopt[ 'host', self.class.host ]
62 @ppid = getopt[ 'ppid', self.class.ppid ]
63 @pid = getopt[ 'pid', self.class.pid ]
64 @ruby = getopt[ 'ruby', self.class.ruby ]
76 IO.popen "#{ quote(@ruby) } #{ quote(c['program']) }", 'r+' do |pipe|
80 cid = Integer line[%r/\d+/]
84 buf = "#{ line }#{ buf }"
86 raise unless Exception === e
89 raise "systemu: Error - process interrupted!\n#{ buf }\n"
92 thread = new_thread cid, @block if @block
103 status.instance_eval{ @thread = thread }
110 if @stdout or @stderr
111 open(c['stdout']){|f| relay f => @stdout} if @stdout
112 open(c['stderr']){|f| relay f => @stderr} if @stderr
115 [status, IO.read(c['stdout']), IO.read(c['stderr'])]
120 def quote *args, &block
121 SystemUniversal.quote(*args, &block)
124 def new_thread cid, block
126 Thread.new(cid) do |cid|
127 current = Thread.current
128 current.abort_on_exception = true
136 stdin = File.expand_path(File.join(tmp, 'stdin'))
137 stdout = File.expand_path(File.join(tmp, 'stdout'))
138 stderr = File.expand_path(File.join(tmp, 'stderr'))
139 program = File.expand_path(File.join(tmp, 'program'))
140 config = File.expand_path(File.join(tmp, 'config'))
143 open(stdin, 'w'){|f| relay @stdin => f}
145 FileUtils.touch stdin
147 FileUtils.touch stdout
148 FileUtils.touch stderr
157 c['program'] = program
158 open(config, 'w'){|f| Marshal.dump(c, f)}
160 open(program, 'w'){|f| f.write child_program(config)}
173 def child_program config
179 config = Marshal.load(IO.read('#{ config }'))
181 argv = config['argv']
184 stdin = config['stdin']
185 stdout = config['stdout']
186 stderr = config['stderr']
189 env.each{|k,v| ENV[k.to_s] = v.to_s} if env
195 PIPE.puts "pid: \#{ Process.pid }"
196 PIPE.flush ### the process is ready yo!
200 rescue Exception => e
201 PIPE.write Marshal.dump(e) rescue nil
208 src, dst, ignored = srcdst.to_a.first
209 if src.respond_to? 'read'
210 while((buf = src.read(8192))); dst << buf; end
212 if src.respond_to?(:each_line)
213 src.each_line{|buf| dst << buf}
215 src.each{|buf| dst << buf}
220 def tmpdir d = Dir.tmpdir, max = 42, &b
224 tmp = File.join d, "systemu_#{ @host }_#{ @ppid }_#{ @pid }_#{ rand }_#{ i += 1 }"
238 FileUtils.rm_rf tmp unless SystemU.turd
247 def getopts opts = {}
249 keys, default, ignored = args
251 [keys].flatten.each do |key|
252 [key, key.to_s, key.to_s.intern].each do |key|
253 throw :opt, opts[key] if opts.has_key?(key)
262 # some monkeypatching for JRuby
263 if defined? JRUBY_VERSION
265 java_import org.jruby.RubyProcess
267 class SystemUniversal
269 split_argv = JRuby::PathHelper.smart_split_command @argv
270 process = java.lang.Runtime.runtime.exec split_argv.to_java(:string)
272 stdout, stderr = [process.input_stream, process.error_stream].map do |stream|
273 StreamReader.new(stream)
276 exit_code = process.wait_for
277 field = process.get_class.get_declared_field("pid")
278 field.set_accessible(true)
279 pid = field.get(process)
281 RubyProcess::RubyStatus.new_process_status(JRuby.runtime, exit_code, pid),
288 def initialize(stream)
290 @thread = Thread.new do
291 reader = java.io.BufferedReader.new java.io.InputStreamReader.new(stream)
293 while line = reader.read_line
294 @data << line << "\n"
309 SystemU = SystemUniversal unless defined? SystemU
310 Systemu = SystemUniversal unless defined? Systemu
328 date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " )
330 status, stdout, stderr = systemu date
331 p [status, stdout, stderr]
333 status = systemu date, 1=>(stdout = '')
336 status = systemu date, 2=>(stderr = '')
341 sleep = %q( ruby -e" p(sleep(1)) " )
342 status, stdout, stderr = systemu sleep
343 p [status, stdout, stderr]
345 sleep = %q( ruby -e" p(sleep(42)) " )
346 status, stdout, stderr = systemu(sleep){|cid| Process.kill 9, cid}
347 p [status, stdout, stderr]
351 env = %q( ruby -e" p ENV['A'] " )
352 status, stdout, stderr = systemu env, :env => {'A' => 42}
353 p [status, stdout, stderr]
357 env = %q( ruby -e" p Dir.pwd " )
358 status, stdout, stderr = systemu env, :cwd => Dir.tmpdir
359 p [status, stdout, stderr]