chupa-text 1.1.9 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16a8f0167ee54c9339de2a91a3ca5dcd5550a19daac95228c57869a67e09f54c
4
- data.tar.gz: 14d0a81b1f75aef784e20fc895037397c15f0dd88dbdcda0dbed58d7f7d16f58
3
+ metadata.gz: ae05a44da20490f2d7833d513cfc423819dd65ef5e1d6285e5a82db8482673fa
4
+ data.tar.gz: b202c67426ca48058422f169339c069cb6bc42e892a40fd9c72bda3463610b94
5
5
  SHA512:
6
- metadata.gz: b6abd29982245d2817af96ef24bc2000ea99fcf89758c3eed0de8de70b3a1d25691d41898bc20c2661d5e521cb8c8d1b23fe6adeeea83b6f8515b3e9a298379f
7
- data.tar.gz: 3afc26b51edff767f8b86a3cbfa55405e07217f2b42a69a00c698f97a3e0fc2b567ef0f25b39cf645d434876f52ad246ea00c1af71faa0fc926d3fe617b2143f
6
+ metadata.gz: 28126643d65dc7ef423dccc0f727be7b275db0005f66f4c40f0c8e5dafe8b159cdea2b6c8aeca98bf7e9979849cf531ddd5afad81f30c85d031dae925029e763
7
+ data.tar.gz: 498fb93a26bfd139d9fba1d15d00bbc17547b9e326c403301794b89f2548c78e8d000ed45c3404c3b0a00073e677c1783c64b1423357893c89326d60efeb07d3
data/doc/text/news.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # News
2
2
 
3
+ ## 1.2.0: 2019-03-03
4
+
5
+ ### Improvements
6
+
7
+ * Added support timeout for external command execution by
8
+ `CHUPA_TEXT_EXTERNAL_COMMAND_TIMEOUT` environment variable.
9
+
3
10
  ## 1.1.9: 2019-03-03
4
11
 
5
12
  ### Improvements
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2014 Kouhei Sutou <kou@clear-code.com>
1
+ # Copyright (C) 2014-2019 Kouhei Sutou <kou@clear-code.com>
2
2
  # Copyright (C) 2010 Yuto HAYAMIZU <y.hayamizu@gmail.com>
3
3
  #
4
4
  # This library is free software; you can redistribute it and/or
@@ -20,6 +20,8 @@ require "pathname"
20
20
 
21
21
  module ChupaText
22
22
  class ExternalCommand
23
+ include Loggable
24
+
23
25
  attr_reader :path
24
26
  def initialize(path)
25
27
  @path = Pathname.new(path)
@@ -31,12 +33,11 @@ module ChupaText
31
33
  else
32
34
  options = {}
33
35
  end
34
- spawn_options = options[:spawn_options] || {}
35
36
  pid = spawn(options[:env] || {},
36
37
  @path.to_s,
37
38
  *arguments,
38
- default_spawn_options.merge(spawn_options))
39
- pid, status = Process.waitpid2(pid)
39
+ spawn_options(options[:spawn_options]))
40
+ status = wait_process(pid, options[:timeout])
40
41
  status.success?
41
42
  end
42
43
 
@@ -51,101 +52,147 @@ module ChupaText
51
52
  end
52
53
 
53
54
  private
54
- def default_spawn_options
55
- SpawnLimitOptions.new.options
55
+ def spawn_options(user_options)
56
+ options = (user_options || {}).dup
57
+ apply_default_spawn_limit(options, :cpu, :int)
58
+ apply_default_spawn_limit(options, :as, :size)
59
+ options
56
60
  end
57
61
 
58
- class SpawnLimitOptions
59
- include Loggable
60
-
61
- attr_reader :options
62
- def initialize
63
- @options = {}
64
- set_default_options
62
+ def apply_default_spawn_limit(options, key, type)
63
+ # TODO: Workaround for Ruby 2.3.3p222
64
+ case key
65
+ when :cpu
66
+ option_key = :rlimit_cpu
67
+ when :as
68
+ option_key = :rlimit_as
69
+ else
70
+ option_key = :"rlimit_#{key}"
65
71
  end
72
+ return if options[option_key]
66
73
 
67
- private
68
- def set_default_options
69
- set_option(:cpu, :int)
70
- set_option(:as, :size)
74
+ tag = "[limit][#{key}]"
75
+ value =
76
+ ENV["CHUPA_TEXT_EXTERNAL_COMMAND_LIMIT_#{key.to_s.upcase}"] ||
77
+ # For backward compatibility
78
+ ENV["CHUPA_EXTERNAL_COMMAND_LIMIT_#{key.to_s.upcase}"]
79
+ value = send("parse_#{type}", tag, value)
80
+ return if value.nil?
81
+ rlimit_number = Process.const_get("RLIMIT_#{key.to_s.upcase}")
82
+ soft_limit, hard_limit = Process.getrlimit(rlimit_number)
83
+ if hard_limit < value
84
+ log_hard_limit_over_value(tag, value, hard_limit)
85
+ return nil
71
86
  end
87
+ limit_info = "soft-limit:#{soft_limit}, hard-limit:#{hard_limit}"
88
+ info("#{log_tag}#{tag}[set] <#{value}>(#{limit_info})")
72
89
 
73
- def set_option(key, type)
74
- value =
75
- ENV["CHUPA_TEXT_EXTERNAL_COMMAND_LIMIT_#{key.to_s.upcase}"] ||
76
- # For backward compatibility
77
- ENV["CHUPA_EXTERNAL_COMMAND_LIMIT_#{key.to_s.upcase}"]
78
- return if value.nil?
79
- return if value.empty?
80
- value = send("parse_#{type}", key, value)
81
- return if value.nil?
82
- rlimit_number = Process.const_get("RLIMIT_#{key.to_s.upcase}")
83
- soft_limit, hard_limit = Process.getrlimit(rlimit_number)
84
- if hard_limit < value
85
- log_hard_limit_over_value(key, value, hard_limit)
86
- return nil
87
- end
88
- limit_info = "soft-limit:#{soft_limit}, hard-limit:#{hard_limit}"
89
- info("#{log_tag}[#{key}][set] <#{value}>(#{limit_info})")
90
-
91
- # TODO: Workaround for Ruby 2.3.3p222
92
- case key
93
- when :cpu
94
- @options[:rlimit_cpu] = value
95
- when :as
96
- @options[:rlimit_as] = value
97
- else
98
- @options[:"rlimit_#{key}"] = value
99
- end
90
+ options[option_key] = value
91
+ end
92
+
93
+ def log_hard_limit_over_value(tag, value, hard_limit)
94
+ warn("#{log_tag}#{tag}[large] " +
95
+ "<#{value}>(hard-limit:#{hard_limit})")
96
+ end
97
+
98
+ def parse_int(tag, value)
99
+ return nil if value.nil?
100
+ return nil if value.empty?
101
+ begin
102
+ Integer(value)
103
+ rescue ArgumentError
104
+ log_invalid_value(tag, value, type, "int")
105
+ nil
100
106
  end
107
+ end
101
108
 
102
- def parse_int(key, value)
103
- begin
104
- Integer(value)
105
- rescue ArgumentError
106
- log_invalid_value(key, value, type, "int")
107
- nil
108
- end
109
+ def parse_size(tag, value)
110
+ return nil if value.nil?
111
+ return nil if value.empty?
112
+ scale = 1
113
+ case value
114
+ when /GB?\z/i
115
+ scale = 1024 ** 3
116
+ number = $PREMATCH
117
+ when /MB?\z/i
118
+ scale = 1024 ** 2
119
+ number = $PREMATCH
120
+ when /KB?\z/i
121
+ scale = 1024 ** 1
122
+ number = $PREMATCH
123
+ when /B?\z/i
124
+ number = $PREMATCH
125
+ else
126
+ number = value
109
127
  end
128
+ begin
129
+ number = Float(number)
130
+ rescue ArgumentError
131
+ log_invalid_value(tag, value, "size")
132
+ return nil
133
+ end
134
+ (number * scale).to_i
135
+ end
110
136
 
111
- def parse_size(key, value)
112
- return nil if value.nil?
113
- scale = 1
114
- case value
115
- when /GB?\z/i
116
- scale = 1024 ** 3
117
- number = $PREMATCH
118
- when /MB?\z/i
119
- scale = 1024 ** 2
120
- number = $PREMATCH
121
- when /KB?\z/i
122
- scale = 1024 ** 1
123
- number = $PREMATCH
124
- when /B?\z/i
125
- number = $PREMATCH
126
- else
127
- number = value
128
- end
129
- begin
130
- number = Float(number)
131
- rescue ArgumentError
132
- log_invalid_value(key, value, "size")
133
- return nil
134
- end
135
- (number * scale).to_i
137
+ def parse_time(tag, value)
138
+ return nil if value.nil?
139
+ return nil if value.empty?
140
+ scale = 1
141
+ case value
142
+ when /h\z/i
143
+ scale = 60 * 60
144
+ number = $PREMATCH
145
+ when /m\z/i
146
+ scale = 60
147
+ number = $PREMATCH
148
+ when /s\z/i
149
+ number = $PREMATCH
150
+ else
151
+ number = value
152
+ end
153
+ begin
154
+ number = Float(number)
155
+ rescue ArgumentError
156
+ log_invalid_value(tag, value, "time")
157
+ return nil
136
158
  end
159
+ (number * scale).to_f
160
+ end
161
+
162
+ def log_invalid_value(tag, value, type)
163
+ warn("#{log_tag}#{tag}[invalid] <#{value}>(#{type})")
164
+ end
137
165
 
138
- def log_hard_limit_over_value(key, value, hard_limit)
139
- warn("#{log_tag}[#{key}][large] <#{value}>(hard-limit:#{hard_limit})")
166
+ def wait_process(pid, timeout)
167
+ if timeout.nil?
168
+ timeout_env = ENV["CHUPA_TEXT_EXTERNAL_COMMAND_TIMEOUT"]
169
+ timeout = parse_time("[timeout]", timeout_env) if timeout_env
140
170
  end
141
171
 
142
- def log_invalid_value(key, value, type)
143
- warn("#{log_tag}[#{key}][invalid] <#{value}>(#{type})")
172
+ if timeout
173
+ status = wait_process_timeout(pid, timeout)
174
+ return status if status
175
+ Process.kill(:TERM, pid)
176
+ status = wait_process_timeout(pid, 5)
177
+ return status if status
178
+ Process.kill(:KILL, pid)
144
179
  end
180
+ _, status = Process.waitpid2(pid)
181
+ status
182
+ end
145
183
 
146
- def log_tag
147
- "[external-command][limit]"
184
+ def wait_process_timeout(pid, timeout)
185
+ limit = Time.now + timeout
186
+ while Time.now < limit
187
+ _, status = Process.waitpid2(pid, Process::WNOHANG)
188
+ return status if status
189
+ sleep(1)
148
190
  end
191
+ nil
192
+ end
193
+
194
+ def log_tag
195
+ "[external-command]"
149
196
  end
150
197
  end
151
198
  end
@@ -15,5 +15,5 @@
15
15
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
  module ChupaText
18
- VERSION = "1.1.9"
18
+ VERSION = "1.2.0"
19
19
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chupa-text
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.9
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kouhei Sutou