class ChupaText::ExternalCommand
Attributes
path[R]
Public Class Methods
default_limit_as()
click to toggle source
# File lib/chupa-text/external-command.rb, line 45 def default_limit_as @default_limit_as || limit_env("AS") end
default_limit_as=(as)
click to toggle source
# File lib/chupa-text/external-command.rb, line 49 def default_limit_as=(as) @default_limit_as = as end
default_limit_cpu()
click to toggle source
# File lib/chupa-text/external-command.rb, line 37 def default_limit_cpu @default_limit_cpu || limit_env("CPU") end
default_limit_cpu=(cpu)
click to toggle source
# File lib/chupa-text/external-command.rb, line 41 def default_limit_cpu=(cpu) @default_limit_cpu = cpu end
default_timeout()
click to toggle source
# File lib/chupa-text/external-command.rb, line 29 def default_timeout @default_timeout || ENV["CHUPA_TEXT_EXTERNAL_COMMAND_TIMEOUT"] end
default_timeout=(timeout)
click to toggle source
# File lib/chupa-text/external-command.rb, line 33 def default_timeout=(timeout) @default_timeout = timeout end
new(path)
click to toggle source
# File lib/chupa-text/external-command.rb, line 62 def initialize(path) @path = Pathname.new(path) end
Private Class Methods
limit_env(name)
click to toggle source
# File lib/chupa-text/external-command.rb, line 54 def limit_env(name) ENV["CHUPA_TEXT_EXTERNAL_COMMAND_LIMIT_#{name}"] || # For backward compatibility ENV["CHUPA_EXTERNAL_COMMAND_LIMIT_#{name}"] end
Public Instance Methods
exist?()
click to toggle source
# File lib/chupa-text/external-command.rb, line 97 def exist? if @path.absolute? @path.file? and @path.executable? else (ENV['PATH'] || "").split(File::PATH_SEPARATOR).any? do |path| (Pathname.new(path) + @path).expand_path.exist? end end end
run(*arguments)
click to toggle source
# File lib/chupa-text/external-command.rb, line 66 def run(*arguments) if arguments.last.is_a?(Hash) options = arguments.pop else options = {} end data = options[:data] pid = spawn(options[:env] || {}, @path.to_s, *arguments, spawn_options(options[:spawn_options], data)) if data soft_timeout = data.timeout else soft_timeout = nil end status = nil begin status = wait_process(pid, options[:timeout], soft_timeout) ensure unless status begin Process.kill(:KILL, pid) Process.waitpid(pid) rescue SystemCallError end end end status.success? end
Private Instance Methods
apply_default_spawn_limit(options, soft_value, key, type)
click to toggle source
# File lib/chupa-text/external-command.rb, line 122 def apply_default_spawn_limit(options, soft_value, key, type) # TODO: Workaround for Ruby 2.3.3p222 case key when :cpu option_key = :rlimit_cpu unit = "s" when :as option_key = :rlimit_as unit = "" else option_key = :"rlimit_#{key}" unit = "" end return if options[option_key] tag = "[limit][#{key}]" value = self.class.__send__("default_limit_#{key}") value = __send__("parse_#{type}", tag, value) soft_value = __send__("parse_#{type}", tag, soft_value) if value value = soft_value if soft_value and soft_value < value else value = soft_value end return if value.nil? rlimit_number = Process.const_get("RLIMIT_#{key.to_s.upcase}") soft_limit, hard_limit = Process.getrlimit(rlimit_number) if hard_limit < value log_hard_limit_over_value(tag, value, hard_limit) return nil end limit_info = "soft-limit:#{soft_limit}, hard-limit:#{hard_limit}" info("#{log_tag}#{tag}[set] <#{value}#{unit}>(#{limit_info})") options[option_key] = value end
log_hard_limit_over_value(tag, value, hard_limit)
click to toggle source
# File lib/chupa-text/external-command.rb, line 159 def log_hard_limit_over_value(tag, value, hard_limit) warn("#{log_tag}#{tag}[large] " + "<#{value}>(hard-limit:#{hard_limit})") end
log_invalid_value(tag, value, type)
click to toggle source
Calls superclass method
ChupaText::Loggable#log_invalid_value
# File lib/chupa-text/external-command.rb, line 257 def log_invalid_value(tag, value, type) super("#{log_tag}#{tag}", value, type) end
log_tag()
click to toggle source
# File lib/chupa-text/external-command.rb, line 296 def log_tag "[external-command]" end
parse_int(tag, value)
click to toggle source
# File lib/chupa-text/external-command.rb, line 164 def parse_int(tag, value) case value when nil nil when Integer value when Float value.round else return nil if value.empty? begin Integer(value) rescue ArgumentError log_invalid_value(tag, value, type, "int") nil end end end
parse_size(tag, value)
click to toggle source
# File lib/chupa-text/external-command.rb, line 183 def parse_size(tag, value) case value when nil nil when Numeric value else return nil if value.empty? scale = 1 case value when /GB?\z/i scale = 1000 ** 3 number = $PREMATCH when /GiB?\z/i scale = 1024 ** 3 number = $PREMATCH when /MB?\z/i scale = 1000 ** 2 number = $PREMATCH when /MiB?\z/i scale = 1024 ** 2 number = $PREMATCH when /[kK]B?\z/i scale = 1000 ** 1 number = $PREMATCH when /KiB?\z/i scale = 1024 ** 1 number = $PREMATCH when /B?\z/i number = $PREMATCH else number = value end begin number = Float(number) rescue ArgumentError log_invalid_value(tag, value, "size") return nil end (number * scale).to_i end end
parse_time(tag, value)
click to toggle source
# File lib/chupa-text/external-command.rb, line 226 def parse_time(tag, value) case value when nil nil when Numeric value else return nil if value.empty? scale = 1 case value when /h\z/i scale = 60 * 60 number = $PREMATCH when /m\z/i scale = 60 number = $PREMATCH when /s\z/i number = $PREMATCH else number = value end begin number = Float(number) rescue ArgumentError log_invalid_value(tag, value, "time") return nil end (number * scale).to_f end end
spawn_options(user_options, data)
click to toggle source
# File lib/chupa-text/external-command.rb, line 108 def spawn_options(user_options, data) options = (user_options || {}).dup if data soft_limit_cpu = data.limit_cpu soft_limit_as = data.limit_as else soft_limit_cpu = nil soft_limit_as = nil end apply_default_spawn_limit(options, soft_limit_cpu, :cpu, :time) apply_default_spawn_limit(options, soft_limit_as, :as, :size) options end
wait_process(pid, timeout, soft_timeout)
click to toggle source
# File lib/chupa-text/external-command.rb, line 261 def wait_process(pid, timeout, soft_timeout) tag = "[timeout]" timeout = TimeoutValue.new(tag, timeout || self.class.default_timeout).raw soft_timeout = TimeoutValue.new(tag, soft_timeout).raw if timeout timeout = soft_timeout if soft_timeout and soft_timeout < timeout else timeout = soft_timeout end if timeout info("#{log_tag}#{tag}[use] " + "<#{TimeoutValue.new(tag, timeout)}>: <#{pid}>") status = wait_process_timeout(pid, timeout) return status if status info("#{log_tag}#{tag}[terminate] <#{pid}>") Process.kill(:TERM, pid) status = wait_process_timeout(pid, 5) return status if status info("#{log_tag}#{tag}[kill] <#{pid}>") Process.kill(:KILL, pid) end _, status = Process.waitpid2(pid) status end
wait_process_timeout(pid, timeout)
click to toggle source
# File lib/chupa-text/external-command.rb, line 286 def wait_process_timeout(pid, timeout) limit = Time.now + timeout while Time.now < limit _, status = Process.waitpid2(pid, Process::WNOHANG) return status if status sleep(1) end nil end