class ExecApp
Run an application on the client.
Borrows from Open3
Attributes
clean_exit[R]
pid[R]
Public Class Methods
[](id)
click to toggle source
Return an application instance based on its ID
@param [String] id of the application to return
# File lib/omf_common/exec_app.rb, line 47 def self.[](id) app = @@all_apps[id] warn "Unknown application '#{id}/#{id.class}'" if app.nil? return app end
has?(id)
click to toggle source
# File lib/omf_common/exec_app.rb, line 53 def self.has?(id) @@all_apps.has_key?(id) end
new(id, cmd, map_std_err_to_out = false, working_directory = nil, &observer)
click to toggle source
Run an application ‘cmd’ in a separate thread and monitor its stdout. Also send status reports to the ‘observer’ by calling its “call(eventType, appId, message”)“
@param id ID of application (used for reporting) @param observer Observer of application’s progress @param cmd Command path and args @param map_std_err_to_out If true report stderr as stdin [false]
# File lib/omf_common/exec_app.rb, line 92 def initialize(id, cmd, map_std_err_to_out = false, working_directory = nil, &observer) @id = id || self.object_id @observer = observer @@all_apps[@id] = self @exit_status = nil @threads = [] pw = IO::pipe # pipe[0] for read, pipe[1] for write pr = IO::pipe pe = IO::pipe logger.debug "Starting application '#{@id}' - cmd: '#{cmd}'" #@observer.call(:STARTED, id, cmd) call_observer(:STARTED, cmd) @pid = fork { # child will remap pipes to std and exec cmd pw[1].close STDIN.reopen(pw[0]) pw[0].close pr[0].close STDOUT.reopen(pr[1]) pr[1].close pe[0].close STDERR.reopen(pe[1]) pe[1].close begin pgid = Process.setsid # Create a new process group # which includes all potential child processes STDOUT.puts "INTERNAL WARNING: Assuming process_group_id == pid" unless pgid == $$ Dir.chdir working_directory if working_directory exec(cmd) rescue => ex cmd = cmd.join(' ') if cmd.kind_of?(Array) STDERR.puts "exec failed for '#{cmd}' (#{$!}): #{ex}" end # Should never get here exit! } pw[0].close pr[1].close pe[1].close monitor_pipe(:stdout, pr[0]) monitor_pipe(map_std_err_to_out ? :stdout : :stderr, pe[0]) # Create thread which waits for application to exit @threads << Thread.new(id, @pid) do |id, pid| Process.waitpid(pid) # Exit status is sometimes nil (OSX 10.8, ping) @exit_status = $?.exitstatus || 0 if @exit_status > 127 @exit_status = 128 - @exit_status end @@all_apps.delete(@id) # app finished if (@exit_status == 0) || @clean_exit logger.debug "Application '#{@id}' finished" else logger.debug "Application '#{@id}' failed (code=#{@exit_status})" end end @stdin = pw[1] # wait for done in yet another thread Thread.new do @threads.each {|t| t.join } call_observer("EXIT", @exit_status) end logger.debug "Application is running with PID #{@pid}" end
signal_all(signal = 'KILL')
click to toggle source
# File lib/omf_common/exec_app.rb, line 57 def self.signal_all(signal = 'KILL') @@all_apps.each_value { |app| app.signal(signal) } end
Public Instance Methods
signal(signal = 'KILL')
click to toggle source
# File lib/omf_common/exec_app.rb, line 76 def signal(signal = 'KILL') logger.debug "Sending signal '#{signal}' to app '#{@id}' with pid #{@pid}" @clean_exit = true Process.kill(signal, -1 * @pid) # we are sending to the entire process group end
stdin(line)
click to toggle source
# File lib/omf_common/exec_app.rb, line 70 def stdin(line) logger.debug "Writing '#{line}' to app '#{@id}' with pid #{@pid}" @stdin.write("#{line}\n") @stdin.flush end
Private Instance Methods
call_observer(event_type, msg)
click to toggle source
# File lib/omf_common/exec_app.rb, line 193 def call_observer(event_type, msg) return unless @observer begin @observer.call(event_type, @id, msg) rescue Exception => ex logger.warn "Exception while calling observer '#{@observer}': #{ex}" logger.debug "#{ex}\n\t#{ex.backtrace.join("\n\t")}" end end
monitor_pipe(name, pipe)
click to toggle source
Create a thread to monitor the process and its output and report that back to the server
@param name Name of app stream to monitor (should be :stdout, :stderr) @param pipe Pipe to read from
# File lib/omf_common/exec_app.rb, line 175 def monitor_pipe(name, pipe) @threads << Thread.new() do begin while true do s = pipe.readline.chomp call_observer(name.to_s.upcase, s) end rescue EOFError # do nothing rescue => err logger.error "monitorApp(#{@id}): #{err}" logger.debug "#{err}\n\t#{err.backtrace.join("\n\t")}" ensure pipe.close end end end