Module: RightScale::RightPopen
- Defined in:
- lib/right_popen.rb,
lib/right_popen/popen3_sync.rb,
lib/right_popen/process_base.rb,
lib/right_popen/target_proxy.rb,
lib/right_popen/linux/process.rb,
lib/right_popen/process_status.rb,
lib/right_popen/linux/popen3_async.rb,
lib/right_popen/safe_output_buffer.rb
Defined Under Namespace
Modules: InputHandler, PipeHandler, StatusHandler Classes: Process, ProcessBase, ProcessError, ProcessStatus, SafeOutputBuffer, TargetProxy
Constant Summary collapse
- DEFAULT_POPEN3_OPTIONS =
see popen3_async for details.
{ :directory => nil, :environment => nil, :exit_handler => nil, :group => nil, :inherit_io => false, :input => nil, :locale => true, :pid_handler => nil, :size_limit_bytes => nil, :stderr_handler => nil, :stdout_handler => nil, :target => nil, :timeout_seconds => nil, :umask => nil, :user => nil, :watch_handler => nil, :watch_directory => nil, }
Class Method Summary collapse
-
.popen3_async(cmd, options) ⇒ TrueClass
Spawns a process to run given command asynchronously, hooking all three standard streams of the child process.
-
.popen3_async_impl(cmd, target, options) ⇒ Object
See RightScale.popen3_async for details.
-
.popen3_sync(cmd, options) ⇒ TrueClass
Spawns a process to run given command synchronously.
-
.popen3_sync_impl(cmd, target, options) ⇒ Object
See RightScale.popen3_sync for details.
-
.require_popen3_impl(synchronicity) ⇒ TrueClass
Loads the specified implementation.
-
.watch_process(process, wait_time, target, handlers) ⇒ Object
watches process for exit or interrupt criteria.
Class Method Details
.popen3_async(cmd, options) ⇒ TrueClass
Spawns a process to run given command asynchronously, hooking all three standard streams of the child process. Implementation requires the eventmachine gem.
Streams the command’s stdout and stderr to the given handlers. Time- ordering of bytes sent to stdout and stderr is not preserved.
Calls given exit handler upon command process termination, passing in the resulting Process::Status.
All handlers must be methods exposed by the given target.
Parameters
Returns
155 156 157 158 159 160 161 162 163 |
# File 'lib/right_popen.rb', line 155 def self.popen3_async(cmd, ) = DEFAULT_POPEN3_OPTIONS.dup.merge() require_popen3_impl(:popen3_async) unless ::EM.reactor_running? raise ::ArgumentError, "EventMachine reactor must be running." end ::RightScale::RightPopen.popen3_async_impl( cmd, ::RightScale::RightPopen::TargetProxy.new(), ) end |
.popen3_async_impl(cmd, target, options) ⇒ Object
See RightScale.popen3_async for details
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/right_popen/linux/popen3_async.rb', line 131 def self.popen3_async_impl(cmd, target, ) # always create eventables on the main EM thread by using next_tick. this # prevents synchronization problems between EM threads. ::EM.next_tick do process = nil begin # create process. process = ::RightScale::RightPopen::Process.new() process.spawn(cmd, target) # connect EM eventables to open streams. handlers = [] handlers << ::EM.attach(process.status_fd, ::RightScale::RightPopen::StatusHandler, process.status_fd, target) handlers << ::EM.attach(process.stderr, ::RightScale::RightPopen::PipeHandler, process.stderr, target, :stderr_handler) handlers << ::EM.attach(process.stdout, ::RightScale::RightPopen::PipeHandler, process.stdout, target, :stdout_handler) handlers << ::EM.attach(process.stdin, ::RightScale::RightPopen::InputHandler, process.stdin, [:input]) target.pid_handler(process.pid) # initial watch callback. # # note that we cannot abandon async watch; callback needs to interrupt # in this case target.watch_handler(process) # periodic watcher. watch_process(process, 0.1, target, handlers) rescue Exception => e # we can't raise from the main EM thread or it will stop EM. # the spawn method will signal the exit handler but not the # pid handler in this case since there is no pid. any action # (logging, etc.) associated with the failure will have to be # driven by the exit handler. if target target.async_exception_handler(e) rescue nil target.exit_handler(process.status) rescue nil if process end end end true end |
.popen3_sync(cmd, options) ⇒ TrueClass
Spawns a process to run given command synchronously. This is similar to the Ruby backtick but also supports streaming I/O, process watching, etc. Does not require any evented library to use.
Streams the command’s stdout and stderr to the given handlers. Time- ordering of bytes sent to stdout and stderr is not preserved.
Calls given exit handler upon command process termination, passing in the resulting Process::Status.
All handlers must be methods exposed by the given target.
Parameters
Returns
113 114 115 116 117 118 |
# File 'lib/right_popen.rb', line 113 def self.popen3_sync(cmd, ) = DEFAULT_POPEN3_OPTIONS.dup.merge() require_popen3_impl(:popen3_sync) ::RightScale::RightPopen.popen3_sync_impl( cmd, ::RightScale::RightPopen::TargetProxy.new(), ) end |
.popen3_sync_impl(cmd, target, options) ⇒ Object
See RightScale.popen3_sync for details
30 31 32 33 34 |
# File 'lib/right_popen/popen3_sync.rb', line 30 def self.popen3_sync_impl(cmd, target, ) process = ::RightScale::RightPopen::Process.new() process.sync_all(cmd, target) true end |
.require_popen3_impl(synchronicity) ⇒ TrueClass
Loads the specified implementation.
Parameters
Return
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/right_popen.rb', line 63 def self.require_popen3_impl(synchronicity) # implementation of Process is specific to platform. case RUBY_PLATFORM when /mswin/ platform_subdir = 'windows' impl_subdir = ::File.join(platform_subdir, 'mswin') when /mingw/ platform_subdir = 'windows' impl_subdir = ::File.join(platform_subdir, 'mingw') when /win32|dos|cygwin/ raise NotImplementedError else platform_subdir = 'linux' impl_subdir = platform_subdir end impl_module = ::File.join(impl_subdir, 'process') # only require EM when async is requested. case synchronicity when :popen3_sync sync_module = 'popen3_sync' when :popen3_async sync_module = ::File.join(platform_subdir, 'popen3_async') else fail 'unexpected synchronicity' end # platform-specific requires. base_dir = ::File.join(::File.dirname(__FILE__), 'right_popen').gsub("\\", '/') require ::File.(impl_module, base_dir) require ::File.(sync_module, base_dir) end |
.watch_process(process, wait_time, target, handlers) ⇒ Object
watches process for exit or interrupt criteria. doubles the wait time up to a maximum of 1 second for next wait.
Parameters
Return
- true
-
Always return true
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/right_popen/linux/popen3_async.rb', line 184 def self.watch_process(process, wait_time, target, handlers) ::EM::Timer.new(wait_time) do begin if process.alive? if process.timer_expired? || process.size_limit_exceeded? process.interrupt else # cannot abandon async watch; callback needs to interrupt in this case target.watch_handler(process) end watch_process(process, [wait_time * 2, 1].min, target, handlers) else handlers.each { |h| h.drain_and_close rescue nil } process.wait_for_exit_status target.timeout_handler rescue nil if process.timer_expired? target.size_limit_handler rescue nil if process.size_limit_exceeded? target.exit_handler(process.status) rescue nil end rescue Exception => e # we can't raise from the main EM thread or it will stop EM. # the spawn method will signal the exit handler but not the # pid handler in this case since there is no pid. any action # (logging, etc.) associated with the failure will have to be # driven by the exit handler. if target target.async_exception_handler(e) rescue nil status = process && process.status status ||= ::RightScale::RightPopen::ProcessStatus.new(nil, 1) target.exit_handler(status) end end end true end |