posix-spawn 0.3.10 → 0.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +7 -1
- data/lib/posix/spawn/child.rb +19 -2
- data/lib/posix/spawn/version.rb +1 -1
- data/test/test_child.rb +97 -20
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f593fc74a9f8637ea2c1dbfa897a805d6f87c0fe
|
4
|
+
data.tar.gz: 89a1a14ec837e06d68e85af34e56e956a976df30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33b05adfe9baf71faabd10e25e434c03e6aac674b4d9d05a6b79e1f8972a34aeacba3bb7b40598355fc9aa4c455049eeb0b3f6c00f7790d906f1ef01bb1799fe
|
7
|
+
data.tar.gz: 75ab91856b7ee41fad74bf2be66096d2667f8fbe888d5c7d67ab364c5033438ceb8e1f1a6b0e9f99cd120da4b65ca9486f2177b1bf4aa51ad7463229102e6dc1
|
data/Rakefile
CHANGED
@@ -14,7 +14,13 @@ end
|
|
14
14
|
# Ruby Extension
|
15
15
|
# ==========================================================
|
16
16
|
|
17
|
-
|
17
|
+
begin
|
18
|
+
require 'rake/extensiontask'
|
19
|
+
rescue LoadError => boom
|
20
|
+
warn "ERROR: The rake-compiler gem dependency is missing."
|
21
|
+
warn "Please run `bundle install' and try again."
|
22
|
+
raise
|
23
|
+
end
|
18
24
|
Rake::ExtensionTask.new('posix_spawn_ext', GEMSPEC) do |ext|
|
19
25
|
ext.ext_dir = 'ext'
|
20
26
|
end
|
data/lib/posix/spawn/child.rb
CHANGED
@@ -77,6 +77,9 @@ module POSIX
|
|
77
77
|
# :max => total Maximum number of bytes of output to allow the
|
78
78
|
# process to generate before aborting with a
|
79
79
|
# MaximumOutputExceeded exception.
|
80
|
+
# :pgroup_kill => bool Boolean specifying whether to kill the process
|
81
|
+
# group (true) or individual process (false, default).
|
82
|
+
# Setting this option true implies :pgroup => true.
|
80
83
|
#
|
81
84
|
# Returns a new Child instance whose underlying process has already
|
82
85
|
# executed to completion. The out, err, and status attributes are
|
@@ -87,6 +90,10 @@ module POSIX
|
|
87
90
|
@input = @options.delete(:input)
|
88
91
|
@timeout = @options.delete(:timeout)
|
89
92
|
@max = @options.delete(:max)
|
93
|
+
if @options.delete(:pgroup_kill)
|
94
|
+
@pgroup_kill = true
|
95
|
+
@options[:pgroup] = true
|
96
|
+
end
|
90
97
|
@options.delete(:chdir) if @options[:chdir].nil?
|
91
98
|
exec! if !@options.delete(:noexec)
|
92
99
|
end
|
@@ -128,6 +135,11 @@ module POSIX
|
|
128
135
|
# Total command execution time (wall-clock time)
|
129
136
|
attr_reader :runtime
|
130
137
|
|
138
|
+
# The pid of the spawned child process. This is unlikely to be a valid
|
139
|
+
# current pid since Child#exec! doesn't return until the process finishes
|
140
|
+
# and is reaped.
|
141
|
+
attr_reader :pid
|
142
|
+
|
131
143
|
# Determine if the process did exit with a zero exit status.
|
132
144
|
def success?
|
133
145
|
@status && @status.success?
|
@@ -139,6 +151,7 @@ module POSIX
|
|
139
151
|
def exec!
|
140
152
|
# spawn the process and hook up the pipes
|
141
153
|
pid, stdin, stdout, stderr = popen4(@env, *(@argv + [@options]))
|
154
|
+
@pid = pid
|
142
155
|
|
143
156
|
# async read from all streams into buffers
|
144
157
|
read_and_write(@input, stdin, stdout, stderr, @timeout, @max)
|
@@ -148,8 +161,12 @@ module POSIX
|
|
148
161
|
rescue Object => boom
|
149
162
|
[stdin, stdout, stderr].each { |fd| fd.close rescue nil }
|
150
163
|
if @status.nil?
|
151
|
-
|
152
|
-
|
164
|
+
if !@pgroup_kill
|
165
|
+
::Process.kill('TERM', pid) rescue nil
|
166
|
+
else
|
167
|
+
::Process.kill('-TERM', pid) rescue nil
|
168
|
+
end
|
169
|
+
@status = waitpid(pid) rescue nil
|
153
170
|
end
|
154
171
|
raise
|
155
172
|
ensure
|
data/lib/posix/spawn/version.rb
CHANGED
data/test/test_child.rb
CHANGED
@@ -1,10 +1,42 @@
|
|
1
1
|
# coding: UTF-8
|
2
|
-
|
3
2
|
require 'test_helper'
|
4
3
|
|
5
4
|
class ChildTest < Minitest::Test
|
6
5
|
include POSIX::Spawn
|
7
6
|
|
7
|
+
# Become a new process group.
|
8
|
+
def setup
|
9
|
+
Process.setpgrp
|
10
|
+
end
|
11
|
+
|
12
|
+
# Kill any orphaned processes in our process group before continuing but
|
13
|
+
# ignore the TERM signal we receive.
|
14
|
+
def teardown
|
15
|
+
trap("TERM") { trap("TERM", "DEFAULT") }
|
16
|
+
begin
|
17
|
+
Process.kill("-TERM", Process.pid)
|
18
|
+
Process.wait
|
19
|
+
rescue Errno::ECHILD
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# verify the process is no longer running and has been reaped.
|
24
|
+
def assert_process_reaped(pid)
|
25
|
+
Process.kill(0, pid)
|
26
|
+
assert false, "Process #{pid} still running"
|
27
|
+
rescue Errno::ESRCH
|
28
|
+
end
|
29
|
+
|
30
|
+
# verifies that all processes in the given process group are no longer running
|
31
|
+
# and have been reaped. The current ruby test process is excluded.
|
32
|
+
# XXX It's weird to use the SUT here but the :pgroup option is useful. Could
|
33
|
+
# be a IO.popen under Ruby >= 1.9 since it also supports :pgroup.
|
34
|
+
def assert_process_group_reaped(pgid)
|
35
|
+
command = "ps axo pgid,pid,args | grep '^#{pgid} ' | grep -v '^#{pgid} #$$'"
|
36
|
+
procs = POSIX::Spawn::Child.new(command, :pgroup => true).out
|
37
|
+
assert procs.empty?, "Processes in group #{pgid} still running:\n#{procs}"
|
38
|
+
end
|
39
|
+
|
8
40
|
def test_sanity
|
9
41
|
assert_same POSIX::Spawn::Child, Child
|
10
42
|
end
|
@@ -56,21 +88,45 @@ class ChildTest < Minitest::Test
|
|
56
88
|
end
|
57
89
|
|
58
90
|
def test_max
|
59
|
-
|
60
|
-
|
61
|
-
|
91
|
+
child = Child.build('yes', :max => 100_000)
|
92
|
+
assert_raises(MaximumOutputExceeded) { child.exec! }
|
93
|
+
assert_process_reaped child.pid
|
94
|
+
assert_process_group_reaped Process.pid
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_max_pgroup_kill
|
98
|
+
child = Child.build('yes', :max => 100_000, :pgroup_kill => true)
|
99
|
+
assert_raises(MaximumOutputExceeded) { child.exec! }
|
100
|
+
assert_process_reaped child.pid
|
101
|
+
assert_process_group_reaped child.pid
|
62
102
|
end
|
63
103
|
|
64
104
|
def test_max_with_child_hierarchy
|
65
|
-
|
66
|
-
|
67
|
-
|
105
|
+
child = Child.build('/bin/sh', '-c', 'true && yes', :max => 100_000)
|
106
|
+
assert_raises(MaximumOutputExceeded) { child.exec! }
|
107
|
+
assert_process_reaped child.pid
|
108
|
+
assert_process_group_reaped Process.pid
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_max_with_child_hierarchy_pgroup_kill
|
112
|
+
child = Child.build('/bin/sh', '-c', 'true && yes', :max => 100_000, :pgroup_kill => true)
|
113
|
+
assert_raises(MaximumOutputExceeded) { child.exec! }
|
114
|
+
assert_process_reaped child.pid
|
115
|
+
assert_process_group_reaped child.pid
|
68
116
|
end
|
69
117
|
|
70
118
|
def test_max_with_stubborn_child
|
71
|
-
|
72
|
-
|
73
|
-
|
119
|
+
child = Child.build("trap '' TERM; yes", :max => 100_000)
|
120
|
+
assert_raises(MaximumOutputExceeded) { child.exec! }
|
121
|
+
assert_process_reaped child.pid
|
122
|
+
assert_process_group_reaped Process.pid
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_max_with_stubborn_child_pgroup_kill
|
126
|
+
child = Child.build("trap '' TERM; yes", :max => 100_000, :pgroup_kill => true)
|
127
|
+
assert_raises(MaximumOutputExceeded) { child.exec! }
|
128
|
+
assert_process_reaped child.pid
|
129
|
+
assert_process_group_reaped child.pid
|
74
130
|
end
|
75
131
|
|
76
132
|
def test_max_with_partial_output
|
@@ -80,6 +136,8 @@ class ChildTest < Minitest::Test
|
|
80
136
|
p.exec!
|
81
137
|
end
|
82
138
|
assert_output_exceeds_repeated_string("y\n", 100_000, p.out)
|
139
|
+
assert_process_reaped p.pid
|
140
|
+
assert_process_group_reaped Process.pid
|
83
141
|
end
|
84
142
|
|
85
143
|
def test_max_with_partial_output_long_lines
|
@@ -88,28 +146,47 @@ class ChildTest < Minitest::Test
|
|
88
146
|
p.exec!
|
89
147
|
end
|
90
148
|
assert_output_exceeds_repeated_string("nice to meet you\n", 10_000, p.out)
|
149
|
+
assert_process_reaped p.pid
|
150
|
+
assert_process_group_reaped Process.pid
|
91
151
|
end
|
92
152
|
|
93
153
|
def test_timeout
|
94
154
|
start = Time.now
|
95
|
-
|
96
|
-
|
97
|
-
|
155
|
+
child = Child.build('sleep', '1', :timeout => 0.05)
|
156
|
+
assert_raises(TimeoutExceeded) { child.exec! }
|
157
|
+
assert_process_reaped child.pid
|
158
|
+
assert_process_group_reaped Process.pid
|
159
|
+
assert (Time.now-start) <= 0.2
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_timeout_pgroup_kill
|
163
|
+
start = Time.now
|
164
|
+
child = Child.build('sleep', '1', :timeout => 0.05, :pgroup_kill => true)
|
165
|
+
assert_raises(TimeoutExceeded) { child.exec! }
|
166
|
+
assert_process_reaped child.pid
|
167
|
+
assert_process_group_reaped child.pid
|
98
168
|
assert (Time.now-start) <= 0.2
|
99
169
|
end
|
100
170
|
|
101
171
|
def test_timeout_with_child_hierarchy
|
102
|
-
|
103
|
-
|
104
|
-
|
172
|
+
child = Child.build('/bin/sh', '-c', 'true && sleep 1', :timeout => 0.05)
|
173
|
+
assert_raises(TimeoutExceeded) { child.exec! }
|
174
|
+
assert_process_reaped child.pid
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_timeout_with_child_hierarchy_pgroup_kill
|
178
|
+
child = Child.build('/bin/sh', '-c', 'true && sleep 1', :timeout => 0.05, :pgroup_kill => true)
|
179
|
+
assert_raises(TimeoutExceeded) { child.exec! }
|
180
|
+
assert_process_reaped child.pid
|
181
|
+
assert_process_group_reaped child.pid
|
105
182
|
end
|
106
183
|
|
107
184
|
def test_timeout_with_partial_output
|
108
185
|
start = Time.now
|
109
|
-
p = Child.build('echo Hello; sleep 1', :timeout => 0.05)
|
110
|
-
assert_raises
|
111
|
-
|
112
|
-
|
186
|
+
p = Child.build('echo Hello; sleep 1', :timeout => 0.05, :pgroup_kill => true)
|
187
|
+
assert_raises(TimeoutExceeded) { p.exec! }
|
188
|
+
assert_process_reaped p.pid
|
189
|
+
assert_process_group_reaped Process.pid
|
113
190
|
assert (Time.now-start) <= 0.2
|
114
191
|
assert_equal "Hello\n", p.out
|
115
192
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: posix-spawn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Tomayko
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-04-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake-compiler
|